[原创]嵌入式系统设备驱动在系统内部的组织
0赞最近给一个公司写驱动,因此有了很多驱动方面的想法:
面对如此众多的设备,系统是如何组织、管理的?在此针对ADI的把blankchfin系统谈一点自己的看法。
如果在设备文件目录下执行ls –l命令,就能看到设备文件项的详细信息。通常最后修改日期前的的两个数就是主、次设备号(如是普通文件,这里则是文件长度信息)。如:
crw-rw-rw- 1 root dialout 4 , 64 Jun 30 11:19 ttys0
上面的6和64就分别是串口0(ttys0)的主、次设备号。最前面的c表示设备类型,即字符设备。我们暂以字符设备为例进行说明,块设备的情况大致相当。
在系统中,设备类型和主设备号唯一标识驱动程序,而次设备号是由主设备号确定的驱动程序来使用的,它对系统没有任何意义。在驱动程序中,次设备号用来区分共享同一驱动的不同设备或者不同功能。如:
crw-rw-rw- 1 root dialout 4 , 65 Aug 16 00:00 ttys1
就表示与设备ttys0使用同一驱动的另一设备ttys1。它的次设备号是65。
在传统方式下,向系统添加一个驱动程序必须首先为其分配一个主设备号。当应用程序调用该设备时,系统就根据主设备号来定位相应的驱动程序。这个分配主设备号的工作在驱动程序初始化时由注册函数来完成。字符设备的注册函数如下:
int register_chrdev(unsigned int major , const char *name , struct file_operations *fops);
这个注册函数的参数major就是你要显式的为该驱动分配的主设备号,当然如果你不知道整个外设的分配情况,也可以动态的分配,这个在后面再详细讨论;参数name是该设备的名称;参数fops就是前面提到的文件操作函数指针。
这个注册函数首先将操作函数集同主设备号对应了起来。下面的问题是应用程序如何知道这个看起来很丑陋的主设备号(就像IP一样难以记忆),聪明的内核开发者早就为我们考虑到了——给设备起个名字(就像域名一样)。这个名字和上面注册函数中的参数name是两回事,不要混淆起来。注册函数中的参数name是驱动程序的名字,而应用程序要用到的是设备的名字。给设备起名字,就是在文件系统上显式的创建一个文件节点,用以代表设备,这就是我们前面提到的设备文件。用mknod命令创建设备文件节点,如:
mknod /dev/mydev1 c 254 0
在上面的命令中,mydev1就是设备的名字,它是/dev目录下的一个文件节点;c表示字符设备;254是主设备号;0是次设备号。这样一来,应用程序看到的设备和文件是一样的,他们都是文件系统上的一个节点,有着相同的接口。硬件设备信息就很好的对用户隐蔽了起来。这就是设备文件带来的好处,在应用程序中,用户可以像操作普通文件那样操作设备文件,比如可以这样打开一个设备:
…
int fdev=open(“/dev/mydev1”,O_RDWR);
…
应用程序通过这样的系统调用向系统发出打开mydev1的请求,系统通过文件节点/dev/mydev1找到设备的主设备号,再由主设备号定位相应的设备驱动程序,最后由驱动程序中的open函数来响应应用程序的系统调用。可以看到,从应用程序发出系统调用到驱动程序做出响应,整个环节正是由主设备号联系起来的。
在同一类设备下(如字符设备),主设备号定位一个驱动程序,主设备号和次设备号共同定位一个设备。所以,同一个主设备号可以对应多个次设备号,因而同一驱动程序就可以由很多设备来使用。就像上面我们举例时提到的ttys0和ttys1。它们是不同的两个串口,但使用相同的串口驱动。
驱动在嵌入式系统当中发挥着极其重要的作用,会带来异常辉煌的发展,在此抛砖引玉写一点自己的认识,希望大家能讨论!!!