LinuxUSB驱动开发(五)——USB驱动程序开发过程简单总
私有数据组成,是应⽤程序和硬件设备之间的桥梁。在应⽤      设备驱动程序是操作系统内核和机器硬件之间的接⼝,由⼀组函数
⼀组函数和⼀些私有数据
程序看来,硬件设备只是⼀个设备⽂件,应⽤程序可以像操作普通⽂件⼀样对硬件设备进⾏操作。
设备驱动程序是内核的⼀部分,主要完成以下功能:对设备的初始化和释放
把数据从内核传送到硬件设备和从硬件设备读取数
对设备的初始化和释放;把数据从内核传送到硬件设备和从硬件设备读取数
检测和处理硬件设备出现的错误。
据;读取应⽤程序数据传送给设备⽂件和回送应⽤程序请求的数据
夏至吃什么传统美食读取应⽤程序数据传送给设备⽂件和回送应⽤程序请求的数据;检测和处理硬件设备出现的错误
⼀、 Linux USB⼦系统分析
在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)
怎么查QQ谁给你设了特别关心不同种类的USB
USB核⼼驱动(USBD)和不同种类的USB
USB主机控制器驱动(HCD)、USB核⼼驱动(USBD)
设备类驱动,如下所⽰。其中HCD和USBD被称为协议软件或者
HCD和USBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作。
设备类驱动
通过协议软件的抽象      USB设备类驱动可以包含多个,不同的功能接⼝对应不同的驱动程序,它们不直接与USB设备硬件打交道,⽽是通过协议软件的抽象处理来完成与设备的不同功能接⼝之间的通信。
处理来完成与设备的不同功能接⼝之间的通信
是USB协议栈的最底层部分,是USB主机控制器硬件和数据
是USB主机控制器硬件和数据HCD是直接和硬件进⾏交互的软件模块,是USB协议栈的最底层部分
在Linux USB⼦系统中,HCD是直接和硬件进⾏交互的软件模块
传输的⼀种抽象。
传输的⼀种抽象
HCD向上仅对USB总线驱动程序服务,HCD提供了⼀个软件接⼝,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并      HCD向上仅对USB总线驱动程序服务
受USB总线驱动程序的调⽤和管理。HCD向下则直接管理和检测主控制器硬件的各种⾏为。HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接⼝函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。
USBD部分是整个USB主机驱动的核⼼,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4
USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4      USBD部分
USB HUB作为种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接⼝、提供应⽤程序访问USB系统的⽂件接
种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接⼝、提供应⽤程序访问USB系统的⽂件接⼝等。其中USB HUB
⼀类特殊的USB设备,其驱动程序被包含在USBD层。
在嵌⼊式Linux系统中,已经包含HCD模块和USB核⼼驱动USBD,不需要⽤户重新编写,⽤户仅仅需要完成USB设备类驱动即可。⼆、Linux系统中USB⼦系统的主要数据结构
USBD通过定义⼀组宏、数据结构和函数来抽象出所有硬件或是设备具有依赖关系的部分。
Linux系统中,USBD
USBD中主要有四个数据结构,分别是:
1.usb_device保存⼀个USB设备的信息,包括设备地址,设备描述符,配置描述符等。
1.usb_device
⼀个主机控2.usb_bus
2.usb_bus保存⼀个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使⽤情况等。⼀个USB总线系统⾄少有⼀个主机控
⼀个根集线器,Linux系统⽀持多USB总线系统。
制器和⼀个根集线器
制器
3.usb_driver保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使⽤的函数指针等。
3.usb_driver
4.URB(Universal Request Block)是进⾏USB通信的数据结构,USBD通过URB在USB设备类驱动和USBD、USBD和HCD间进4.URB(Universal Request Block)
⾏数据传输。
三、Linux系统中USB设备的加载与卸载
当把⼀个USB设备插⼊到⼀个USB HUB的某个端⼝时,集中器就会检测到设备的接⼊,从⽽在下⼀次受到主机通过中断交互查询时就会向其报告。集中器的端⼝在没有设备接⼊时都处于关闭状态,插⼊设备之后也不会⾃动打开,必须由主机通过控制交互发出命令予以打开。所以,在得到集中器的报告之后,主机的USB驱动程序就会为新插⼊的设备调度若⼲个控制交互,并向集中器发出打开这个端⼝
主机的USB驱动程序就会为新插⼊的设备调度若⼲个控制交互,并向集中器发出打开这个端⼝的命令,这样新插⼊的设备就会出现在USB总线上了,并为该设备分配唯⼀的地址。
的命令,这样新插⼊的设备就会出现在USB总线上了,并为该设备分配唯⼀的地址
usb_new_device(struct usb_device *dev)解析设备的      HUB驱动程序调⽤函数usb_connect(struct usb_device *dev)
usb_connect(struct usb_device *dev)和usb_new_device(struct usb_device *dev)
各种描述符信息,分配资源,并与相应的设备驱动程序建⽴联系。
函数usb_new_device主要完成以下⼯作:
1.调⽤usb_set_address把新分配的设备地址传送给设备。
2.调⽤usb_get_descriptor获得设备的设备描述符,得到设备端点的包的最⼤长度,接下来的控制传输按这个数据包最⼤长度进⾏。
3.调⽤usb_get_configuration得到设备的所有配置描述符、接⼝描述符和端点描述符信息。
4.调⽤usb_set_configuration激活当前的配置作为默认⼯作配置。
5.在⽬录“proc/bus/usb”中为设备创建节点。
为设备的每⼀个接⼝寻相应的驱动程序,驱动
usb_find_interface_driver,为设备的每⼀个接⼝寻相应的驱动程序,驱动usb_find_drivers和usb_find_interface_driver
6.在USB⼦系统中,通过函数usb_find_drivers
程序对接⼝进⾏配置并为它们分配所需的资源。当每个接⼝被成功驱动后,此设备就能正常⼯作了。
程序对接⼝进⾏配置并为它们分配所需的资源
设备拔下时,与之相联的集线器⾸先检测到设备的拔下信号,通过中断传输将信息传送给集线器的驱动,集线器的驱动先验证设备是否被拔下,如果是则调⽤usb_disconnect(struct usb_device **pdev)进⾏处理。设备断开后,USB系统到设备当前活动配置的每个接⼝的驱动程序,调⽤它们提供的disconnect接⼝函数,中断它们与各个接⼝的数据传输操作,释放它们为每个接⼝分配的资源。如果此设备是集线器,则递归调⽤usb_disconnect来处理它的⼦设备,释放设备地址,通过usbdevfs_remove_device函数释放给设备创建的⽂件节点,通过usb_free_dev释放USBD给设备分配的资源。
四、编写USB驱动程序步骤
1、所有usb驱动都必须创建主要结构体struct usb_driver
1、所有usb驱动都必须创建主要结构体
struct usb_driver
->struct module *owner
(有他可正确对该驱动程序引⽤计数,应为THIS_MODULE)
->const char *name
(驱动名字,运⾏时可在查看 /sys/bus/usb/drivers/)
->const struct usb_device_id *id_table
(包含该驱动可⽀持的所有不同类型的驱动设备,没添探测回调函数不会被调⽤)
->->int (*probe)(struct usb_interface *intf,const struct usb_device_id *id)
(usb驱动探测函数,确认后struct usb_interface 应恰当初始化,然后返0,如果出错则返负值)
->void(*disconnect)(struct usb_interface *intf)
(当struct usb_interface 被从系统中移除或驱动正从usb核⼼中卸载时,usb核⼼将调⽤此函数)
代码实例:
static struct usb_driver skel_driver={
.
怎样酿制葡萄酒owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};
2、usb_register()注册将struct usb_driver 注册到usb核⼼,传统是在usb驱动程序模块初始化代码中完成该⼯作的
static int __ init usb_skel_init(void)
{
...
usb_register(&skel_driver);
...
}
3、struct usb_device_id usb核⼼⽤该表判断哪个设备该使⽤哪个驱动程序,热插拔脚本使⽤它来确定当⼀个特定的设备插⼊到系统时该⾃动装载哪个驱动程序。
系统时该⾃动装载哪个驱动程序
->__u16 match_flags(确定设备和结构体中下列字段中哪⼀个相匹配)
->__u16 idVendor(设备的usb制造商id)
->__u16 idProduct(设备的usb产品id)
4、USB⾻架程序的关键⼏点如下:
a -- USB驱动的注册和注销
Usb驱动程序在注册时会发送⼀个命令给usb_register,通常在驱动程序的初始化函数⾥。
当要从系统卸载驱动程序时,需要注销usb⼦系统。即需要usb_unregister 函数处理。
b -- 当usb设备插⼊时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔⽀持)系统⾃动装载驱动程序,你需要创建⼀个MODULE_DEVICE_TABLE
代码如下(这个模块仅⽀持某⼀特定设备):
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都会被传递到函数中。
c -- static voi
d * skel_probe(struct usb_devic
e *dev,unsigned int ifnum, const struct usb_device_id *id)
驱动程序需要确认插⼊的设备是否可以被接受
要确认插⼊的设备是否可以被接受,如果不接受,或者在初始化的过程中发⽣任何错误,probe函数返回⼀个NULL值。否则返回⼀个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。
d -- 在⾻架驱动程序⾥,最后⼀点是我们要注册devfs。
d -- 在⾻架驱动程序⾥,最后⼀点是我们要注册devfs
我们创建⼀个缓冲⽤来保存那些被发送给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs⼦系统中注册设备,允许devfs⽤户访问我们的设备。注册过程如下:
/* initialize the devfs node for this device and register it */
sprintf(name, "skel%d", skel->;minor);
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);
5、其他
a -- struct usb_host_endpoint(描述usb端点)
struct usb_endpoint_descriptor(含真正端点信息,数据格式,是真正驱动关⼼的字段)
→(包含)struct usb_endpoint_descriptor
端点描述符:
bEndpointAddress = 81(in)(第8位为1是输⼊设备)(usb的端点地址,包含端点⽅向)
bmAttibutes = 03(interrupt)(端点类型,为中断传输)
wMaxPacketSize = 0008(每次传8个字节)(端点每次可处理最⼤字节长度)
bInterval = 08(8ms)(如端点为中断,该值为轮询间隔)
b -- usb端点捆绑为接⼝,usb接⼝只处理⼀种usb逻辑连接,如⿏标键盘等
⼀个usb设备可有多接⼝,usb扬声器:⼀个usb键盘⽤于按键,⼀个usb⾳频流,则需两个不同的驱动程序。
usb驱动 通常将struct usb_interface 转成 struct usb_device ⽤函数 interface_to_usbdev转
c -- struct usb_interface 描述usb接⼝
→struct usb_host_interface * altsetting(接⼝结构体数组,包含所有可能⽤于该接⼝的可选设置)
→struct usb_host_endpoint
蔡依林身高多少
→unsigned num_altsetting(可选设置的数量)
→struct usb_host_interface * cur_altsetting(接⼝当前活动设置)
→int minor(usb核⼼分配给接⼝的次设备号,成功调⽤usb_register_dev有效)
d -- usb设备⾮常复杂,由许多不同逻辑单元组成,简单关系如下:
d --
设备通常有⼀个以上的配置
配置经常有⼀个以上接⼝
接⼝通常有⼀个以上设置
接⼝通常有⼀个以上端点
设备描述-》配置描述-》接⼝描述-》端点描述
e -- usb sysfs设备命名⽅案
根集线器-集线器端⼝号:配置。接⼝
对于usb hub树中层次更⾼的字树命名⽅案
根集线器-集线器端⼝号-集线器端⼝号:配置。接⼝
f --  linux内核的代码通过⼀个成为urb(usb请求块)和所有usb设备通信.
⽤struct urb描述(include/linux/usb.h中定义)
->urb⽤异步同usb设备特定usb端点发送/接收数据,使⽤类似⽹络代码中的struct skbuff
-> urb 被动态创建,随时可被驱动程序或usb核⼼取消,内部有引⽤计数,可被多次调⽤,使他们可在最后⼀个使⽤者释放他们时⾃动地销毁
-> urb使得流处理或其他复杂的重叠的通信成为可能,获得⾼数据传输速度。
昆凌 跑男->usb_alloc_urb() 创建urb包 usb_free_urb() 释放urb包
->usb_fill_int_urb()正确初始化将发送到usb设备的中断端点urb
usb_fill_bulk_urb() .. .. .. ... 批量传输端点urb
usb_fill_control_urb() .. .. .. ... 控制端点urb
等时urb在提交给核⼼时必须⼿动初始化(很不幸,没函数)
-
>usb_submit_urb()urb被usb驱动正确创建和初始化后,就可提交到usb核⼼,发送到usb设备上了,如果调⽤成功,函数返0,urb控制权转给usb核⼼
->usb_kill_urb() or usb_unlink_urb()取消已经被提交给核⼼的urb
五、USB驱动开发简单⽰例
1、嵌⼊式Linux系统中USB摄像头驱动程序实现
通常USB设备类驱动程序需要提供两个数据结构接⼝,⼀个针对USBD层,⼀个针对⽂件系统。USB摄像头驱动程序需要做的第⼀件事情就是在USB⼦系统⾥注册,并提供⼀些相关信息,包括该驱动程序⽀持哪些设备,当被⽀持的设备从总线插⼊或拔出时,会有哪些动作等,所有这些信息通过usb_driver的形式传送到USBD中,具体实现如下:
static struct usb_driver cam_driver = {
房改房产权
.name: "cam_video",
.probe: cam_probe,
.disconnect: cam_disconnect,
.id_table: cam_ids,
};
其中
cam_video是客户端驱动程序的字符串名称,⽤于避免驱动程序的重复安装和卸载;
cam_probe则指向USB驱动程序的探测函数指针,提供给USB内核的函数,⽤于判断驱动程序是否能对设备的某个接⼝进⾏驱
⽤于判断驱动程序是否能对设备的某个接⼝进⾏驱cam_probe则指向USB驱动程序的探测函数指针,提供给USB内核的函数
动;
cam_disconnect指向USB驱动程序中的断开函数的指针,当从系统中被移除或者驱动程序正在从USB核⼼中卸载时,USB核⼼将调⽤该cam_disconnect
函数;
cam_ids列表包含了⼀系列该驱动程序可以⽀持的所有不同类型的USB设备,如没有设置该列表,则该驱动程序中的探测回调函数不会被调⽤。
如果⽀持,为该设备      当⼀个摄像头连接到USB总线上时,USB内核通过调⽤camDrive.c中的cam_probe函数判断是否⽀持该设备,如果⽀持,为该设备创建设备⽂件节点,以后应⽤程序就可以通过标准POSIX函数,把该设备当成普通⽂件来访问。摄像头驱动程序定义的⽂件系统接⼝创建设备⽂件节点,以后应⽤程序就可以通过标准POSIX函数,把该设备当成普通⽂件来访问
如下: