USB采用树形拓扑结构,主机侧和设备侧的USB设备控制器分别称为主机控制器和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,而设备不能主动向主机发送任何信息。
从主机侧去看,在linux驱动中,处于USB设备驱动最底层的是USB主机控制器硬件,在其上运行的是USB主机控制器驱动。在主机控制器上的为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。linux内核中的USB核心负责USB驱动管理和协议处理的主要工作。
主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。
从linux USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget Function API和Gadget Function驱动程序。UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。当前Gadget Function API是UDC驱动程序回调函数的简单包装。Gadget Function驱动程序具体控制USB设备功能的实现,使设备表现出”网络连接”、”打印机”或”USB Mass Storage”等特性,它使用Gadget FunctionAPI 控制UDC实现上述功能。Gadget Function API把下层的UDC驱动程序和上层的Gadget Function驱动程序隔离,使得linux系统编写USB设备侧驱动程序时能把功能的实现和底层通信分离。
USB相关描述符
1、设备描述符
设备的通用信息,如供应商ID、产品ID和修订ID,支持的设备类、子类和适用的协议以及默认端点的最大包
大小等。
2、配置描述符
此配置中的接口数、支持的挂起和恢复能力以及功率要求。
3、接口描述符
接口类、子类或适用的协议,接口备用配置的数目和端点数目。
4、端点描述符
端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点侧还包括轮询频率。
5、字符串描述符
可选
USB设备驱动的整体结构
这里所说的USB设备驱动指的是从主机角度来看,怎么访问被插入的USB设备,而不是指USB设备内部本身运行的固件程序。linux系统实现了几类通用的USB设备驱动(也称客户驱动),划分为如下几个设备类:音频设备类、通信设备类、HID设备类、显示设备类、电源设备类、存储设备类、打印机设备类、集线器设备类。一般通用linux设备(如U盘、USB鼠标、USB键盘等)不需要再编写驱动,需要编写的是特定厂商、特性芯片的驱动,可参考内核中的驱动模块。
linux内核为各类USB设备分配了相应的设备号,如ACM USB调制解调器的主设备号为166(默认为/dev/ACMn)、USB打印机的主设备号为180,次设备号为0-15(默认为/dev/lpn)、USB串口的主设备号为188(默认为/dev/ttyUSBn)等。
在/sys/kernel/debug/usb/devices下包含了USB的设备信息,可以是使用cat查看相应信息。
在编写新的USB设备驱动时,主要应该完成的工作是probe()和disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出的时候调用,用于初始化和释放软硬件资源。对usb_driver的注册和注销可通过下面两个函数完成
int usb_register(struct usb_driver *new_driver);
void usb_deregister(struct usb_driver *driver);
usb_driver结构体中的id_table成员描述了这个USB驱动所支持的USB设备列表,它指向一个usb_device_id数组,usb_device_id结构体包含有USB设备的制造商ID、产品ID、产品版本、设备类、接口类等信息及其要匹配标志程序match_flags。
实例
static struct usb_device_id id_table[] = {
//在设备插入时 比对ID使用
{USB_DEVICE(VENDOR_ID, PRODUCT_ID)},
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
当USB核心检测到某个设备的属性和某个驱动程序的usb_device_id结构体所携带的信息一致时,这个驱动程序的probe()函数就被执行(如果这个USB驱动是个模块的话,相关的.ko还应被linux自动加载)。拔掉设备或者卸掉驱动模块后,USB核心就执行disconnect()函数来响应。
USB只是一个总线,USB设备驱动真正的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。因此USB设备驱动包含其作为总线上挂载设备的驱动和本身所属设备类型的驱动两部分。
与platform_driver、i2c_driver类似,usb_driver起到了”牵线”的作用,即在probe()里面注册相应的字符、tty等设备,在disconnect()注销相应的字符、tty等设备。
尽管USB本身所属设备驱动的结构与其挂不挂在USB总线上没什么关系,但是访问方式却有很大的变化,例如,对于USB接口的字符设备而言,尽管仍然是write()、read()、ioctl()这些函数,但是在这些函数中,贯穿始终的是称为URB的USB请求块。
用一颗树来类比,把树根比作主机控制器,树比作具体的USB设备叶,树干和树枝就是USB总线。树叶本身与树枝通过usb_driver连接,而树叶本身的驱动(读写、控制)则需要通过其树叶设备本身所属类设备驱动来完成。树根和树叶之间的”通信”依靠在树干和树枝里”流淌”的URB来完成。
由此可见,usb_driver本身只是有找到usb设备、管理USB设备连接和断开的作用,作为USB设备的树叶可以是字符树叶、网络树叶或块树叶,因此必须实现相应设备类的驱动。