应用程序
Open/Read/Write命令操作
设备文件
Mknod通过主设备号关联
返回主设备号 驱动模块 kernel insmod向系统注册 通过模块中的Read/Write操作 主机硬件
这个图我们从下面往上面看,linux驱动最底层是主机硬件,运行在主机硬件其上是驱动模块了, 驱动模块直接对进行读写操作。驱动模块在内核是可以通过静态或者动态加载,来在内核中注册的。例如用 insmod XXX.o 模块,那么用limod就可以看到已经在内核中加载进来,设备文件是用 mknod+新设备名+c/b+主设备号+次设备号来关联起来的。上层的应用程序仅仅对设备文件进行读写等操作。
2.linux USB驱动层次
从运行Linux的主机侧看 设备侧看 USB设备驱动usb-skeleton/ Mass storage/ AL576 USB核心
USB主机控制器驱动
OHCI/EHCI/UHCI
USB控制器 USB控制器
OHCI/EHCI/UHCI OHCI/EHCI/UHCI
上图可以说是前面第一部分的驱动模块的一个特写。
从主机侧来看,在linux驱动中,USB驱动最底层的是USB主控制器硬件。在其上运行的是USB主机控制驱动。主机控制驱动之上为USB核心层,在往上层为USB设备驱动层了(与插入主机上的U盘、鼠标、AL576等对应的设备驱动)。 Linux内核的USB核心负责USB驱动管理和协议处理的重要工作,其功能包括:通过定义一些数据结构、宏、和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口:通过全部变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。
3.USB骨架驱动usb-skeleton分析 module_init (usb_skel_init);/*模块入口*/
module_exit (usb_skel_exit); /*模块出口*/
static struct usb_driver skel_driver = { name: \"skeleton\本驱动名*/ probe: skel_probe,/*USB设备探测函数指针*/ disconnect: skel_disconnect,/*USB设备断开函数指针*/ fops: &skel_fops,/*对应的操作*/ minor: USB_SKEL_MINOR_BASE, id_table: skel_table,/*本驱动支持的设备列里面对应设备VENDOR_ID,PRODUCT_ID*/ };
static struct file_operations skel_fops = {
owner: THIS_MODULE, read: skel_read, write: skel_write, ioctl: skel_ioctl, open: skel_open, release: skel_release, };
USB骨架程序的关键几点如下: USB驱动的注册和注销
Usb驱动程序在注册时会发送一个命令给usb_register,通常在驱动程序的初始化函数里。 当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister 函数处理:
static void __exit usb_skel_exit(void) {
/* deregister this driver with the USB subsystem */ usb_deregister(&skel_driver); }
module_exit(usb_skel_exit);
当usb设备插入时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_TABLE。代码如下(这个模块仅支持某一特定设备):
/* table of devices that work with this driver */ static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ };
MODULE_DEVICE_TABLE (usb, skel_table);
USB_DEVICE宏利用厂商ID和产品ID为我们提供了一个设备的唯一标识。当系统插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。usb_device 结构指针、接口号和接口ID都会被传递到函数中。
static void * skel_probe(struct usb_device *dev,unsigned int ifnum, const struct usb_device_id *id)
驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。
在骨架驱动程序里,最后一点是我们要注册devfs。我们创建一个缓冲用来保存那些被发送
给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs子系统中注册设备,允许devfs用户访问我们的设备。注册过程如下:
/* initialize the devfs node for this deviceand register it */ sprintf(name, \"skel%d\
skel->devfs = devfs_register(usb_devfs_handle, name, DEVFS_FL_DEFAULT, USB_MAJOR, USB_SKEL_MINOR_BASE + skel->minor, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, &skel_fops, NULL);
如果devfs_register函数失败,不用担心,devfs子系统会将此情况报告给用户。
当然最后,如果设备从usb总线拔掉,设备指针会调用disconnect 函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且从devfs上注销调自己。
/* remove our devfs node */ devfs_unregister(skel->devfs);
现在,skeleton驱动就已经和设备绑定上了,任何用户态程序要操作此设备都可以通过file_operations结构所定义的函数进行了。首先,我们要open此设备。在open函数中MODULE_INC_USE_COUNT 宏是一个关键,它的作用是起到一个计数的作用,有一个用户态程序打开一个设备,计数器就加一,例如,我们以模块方式加入一个驱动,若计数器不为零,就说明仍然有用户程序在使用此驱动,这时候,你就不能通过rmmod命令卸载驱动模块了。
/* increment our usage count for the module */ MOD_INC_USE_COUNT; ++skel->open_count;
/* save our object in the file's private structure */ file->private_data = skel;
当open完设备后,read、write函数就可以收、发数据了。
skel的write、和read函数
他们是完成驱动对读写等操作的响应。
在skel_write中,一个FILL_BULK_URB函数,就完成了urb 系统callbak和我们自己的skel_write_bulk_callback之间的联系。注意skel_write_bulk_callback是中断方式,所以要注意时间不能太久,本程序中它就只是报告一些urb的状态等。
read 函数与write 函数稍有不同在于:程序并没有用urb 将数据从设备传送到驱动程序,而是我们用usb_bulk_msg 函数代替,这个函数能够不需要创建urbs 和操作urb函数的情况
下,来发送数据给设备,或者从设备来接收数据??。我们调用usb_bulk_msg函数并传提一个存储空间,用来缓冲和放置驱动收到的数据,若没有收到数据,就失败并返回一个错误信息。
usb_bulk_msg函数
当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提交给usb子系统。
skel_disconnect函数
当我们释放设备文件句柄时,这个函数会被调用。MOD_DEC_USE_COUNT宏会被用到(和MOD_INC_USE_COUNT刚好对应,它减少一个计数器),首先确认当前是否有其它的程序正在访问这个设备,如果是最后一个用户在使用,我们可以关闭任何正在发生的写,操作如下:
/* decrement our usage count for the device */ --skel->open_count;
if (skel->open_count <= 0) {
/* shutdown any bulk writes that might be going on */
usb_unlink_urb (skel->write_urb); skel->open_count = 0; }
/* decrement our usage count for the module */ MOD_DEC_USE_COUNT;
最困难的是,usb 设备可以在任何时间点从系统中取走,即使程序目前正在访问它。usb驱动程序必须要能够很好地处理解决此问题,它需要能够切断任何当前的读写,同时通知用户空间程序:usb设备已经被取走。
如果程序有一个打开的设备句柄,在当前结构里,我们只要把它赋值为空,就像它已经消失了。对于每一次设备读写等其它函数操作,我们都要检查usb_device结构是否存在。如果不存在,就表明设备已经消失,并返回一个-ENODEV错误给用户程序。当最终我们调用release 函数时,在没有文件打开这个设备时,无论usb_device结构是否存在、它都会清空skel_disconnect函数所作工作。
因篇幅问题不能全部显示,请点此查看更多更全内容