Linuxusb设备驱动四篇文章LinuxUSB驱动编程指导:二、LinuxUSB驱动框架剖析LinuxUSB驱动框架剖析说明:原本也想自己总结一下USB驱动,并且在网上见到这篇文章,觉得我想说的它早已说了,而我没想到要说的,它也早已说了,所以就转载了!倘若只是写一个简单的USB驱动,我想看完这篇文章就应当不成问题了!LinuxUSB驱动框架剖析(一)初次接触与OS相关的设备驱动编撰,觉得还挺有意思的,为了不至于忘记看过的东西,笔记跟总结其实不可缺,更毕竟我决定为嵌入式卖命了。好,言归正传,我说一说这段时间的收获,跟你们分享一下Linux的驱动开发。但此次只先针对Linux的USB子系统作剖析linux软件工程师培训,由于周日研讨老总催货。其实,都会顺带提一下其他的驱动程序写法。事实上,Linux的设备驱动都遵守一个惯例——表征驱动程序(用driver更贴切一些,应当称为驱动器比较好吧)的结构体,结构体上面应当包含了驱动程序所须要的所有资源。用术语来说,就是这个驱动器对象所拥有的属性及成员。因为Linux的内核用c来编撰,所以我们也根据这些结构化的思想来剖析代码,但我还是希望从OO的角度来探讨这种细节。这个结构体的名子有驱动开发人员决定,例如说,键盘可能有一个称作mouse_devstruct,鼠标可能由一个keyboard_dev的struct(devdevice,我们做的只是设备驱动)。
而此次我们来剖析一下Linux内核源码中的一个usb-skeleton(就是usb驱动的骨架咯),自然,他定义的设备结构体就称作usb-skel:structusb_skelstructusb_deviceusbdevicestructusb_interfacestructsemaphorelimit_sem;unsignedcharreceivedatasize_tbulk_in_size;receivebuffer__u8bulk_in_endpointAddr;__u8bulk_out_endpointAddr;bulkoutendpointstructkrefkref;这儿我们得补充说明一下一些USB的合同规范细节。USB才能手动检测设备,并调用相应得驱动程序处理设备linux usb 驱动,所以其规范实际上是相当复杂的,幸而,我们何必理会大部份细节问题,由于Linux早已提供相应的解决方案。就我如今的理解来说linux apache 虚拟主机,USB的驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核早已做好了,我们可以不管,但我们起码要了解他的功能。形象得说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB通道抵达该到的地方,这部份工作由usb_core来完成。
当USB设备接到USB控制器插口时,usb_core检查该设备的一些信息,比如生产厂商ID和产品的ID,或则是设备所属的class、subclassprotocol,便于确定应当调用哪一个驱动处理该设备。上面复杂细节我们不用管,我们要做的是另一块工作——usb的设备驱动。也就是说,我们就等着usb_core告诉我们要工作了,我们才工从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每位配置又可以有多个插口(interface),每位插口又有多个设置(setting图中没有给出),而插口本身可能没有端点或则多个端点(endpoint)。USB的数据交换通过端点来进行,主机与各个端点之间构建起双向的管线来传输数据。而这种插口可以分为四类:控制(control)用于配置设备、获取设备信息、发送命令或则获取设备的状态报告中断(interrupt)当USB寄主要求设备传输数据时,中断端点会以一个固定的速度传送少量数据,还用于发送数据到USB设备以控制设备,通常不用于传送大量数据。批量(bulk)用于大量数据的可靠传输,假如总线上的空间不足以发送整个批量包,它会被分割成多个包传输。
等时(isochronous)大量数据的不可靠传输,不保证数据的抵达,但保证恒定的数据流,多用于数据采集。Linux中用structusb_host_endpoint来描述USB端点,每位usb_host_endpoint中包含一个structusb_endpoint_descriptor结构体,当中包含该端点的信息以及设备自定义的各类信息,这种信息包括:bEndpointAddress(b位端点地址,其地址还隐藏了端点方向的信息(之前说过,端点是双向的),可以用网段USB_DIR_OUT和USB_DIR_IN来确定。bmAttributesUSB_ENDPOINT_XFER_INT(中断)。wMaxPacketSize端点一次处理的最大字节数。发送的BULK包可以小于这个数值,但会被分割传送。bInterval假如端点是中断类型,该值是端点的间隔设置,以微秒为单位。在逻辑上,一个USB设备的功能界定是通过插口来完成的。例如说一个USB音响,可能会包括有两个插口:一个用于鼠标控制,另外一个用于音频流传输。而事实上,这些设备须要用到不同的两个驱动程序来操作,一个控制按键,一个控制音频流。
但也有例外,例如蓝牙设备,要求有两个插口linux usb 驱动,第一用于ACL跟EVENT的传输,另外一个用于SCO链路,但二者通过一个驱动控制。在Linux上,插口使用structusb_interface来描述,以下是该结构体中比较重要的字structusb_host_interface*altsetting(注意不是usb_interface)虽然据我理解,他应当是每位插口的设置,尽管名子上有点奇怪。该数组是一个设置的字段(一个插口可以有多个设置),每位usb_host_interface都包含一套由structusb_host_endpoint义的端点配置。但这种配置顺序是不定的。unsignednum_altstting可选设置的数目,即altsetting所指链表的元素个数。structusb_host_interface*cur_altsetting当前活动的设置,指向altsetting链表中的一个。intminor当捆绑到该插口的USB驱动程序使用USB主设备号时,USBcore分配的次设备号。仅在成功调用usb_register_dev以后才有效。不仅它可以用structusb_host_config来描述之外,到现今为止,我对配置的了解不多。
而整个USB设备则可以用structusb_device来描述,但基本上只会用它来初始化函数的插口,真正用到的应当是我们之前所提及的自定义的一个结构体。LinuxUSB驱动框架剖析(二)好,了解过USB一些规范细节以后,我们如今来瞧瞧Linux的驱动框架。事实上,Linux设备驱动,非常是这些hotplug的USB设备驱动,会被编译成模块,之后在须要时挂在到内核。要写一个Linux的模块并不复杂,以一个helloworld#includelinux/init.h#includelinux/module.hMODULE_LICENSE(“GPL”);staticinthello_init(void)printk(KERN_ALERT“HelloWorld!n”);returnstaticinthello_exit(void)printk(KERN_ALERT“GOODBYE!n”);module_init(hello_init);module_exit(hello_exit);这个简单的程序告诉你们应当如何写一个模块,MODULE_LICENSE告诉内核该模块的版权信息,好多情况下,用GPL或则BSD,或则两个,由于一个私有模块通常很难得到社区的帮助。
module_init和module_exit用于向内核注册模块的初始化函数和模块推出函数。如程序所示,初始化函数是hello_init,而退出函数是hello_exit。另外,要编译一个模块一般还须要用到内核源码树中的makefile,所以模块的Makefile以写成:ifneq($(KERNELRELEASE),)obj-m:=hello.o#usb-dongle.oelseKDIR:=/usr/src/linux-headers-$(shellunameBDIR:=$(shellpwd)default:$(MAKE)$(KDIR)M=$(PWD)modules.PHONY:cleanclean:make$(KDIR)M=$(BDIR)cleanendif可以用insmod跟rmmod来验证模块的挂在跟卸载,但必须用root的身分登录命令行,用普通用户加su或则sudo在Ubuntu上的测试是不行的。LinuxUSB驱动框架剖析(三)下边剖析一下usb-skeleton的源码。这个范例程序可以在linux-2.6.17/drivers/usb到,其他版本的内核程序源码可能有所不同,但相差不大。你们可以先找到源码看一看,先有个整体印象。之前早已提及,模块先要向内核注册初始化跟销毁函数:
文章评论