引子:关于 lsusb 命令
lsusb 列出系统中所有的USB设备:
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 004 :表示第四个 usb 主控制器(机器上总共有四个 usb 主控制器,可以通过命令 lspci | grep USB 查看)。
Device 001表示系统给 usb 鼠标设备分配的设备号(devnum),同时也可以看到该设备是插入到了第4个 usb 主控制器。
ID 1d6b:0003 表示 usb 设备的 ID(这个 ID 由芯片制造商设置,可以唯一表示该设备)。可在 /sys/devices/pci0000:00/0000:[]:][][.[]/usbx/x-x/ 目录下查看 devnum、idVendor、idProduct 等信息。
最后面的 root hub 应该是代表 该设备是挂载在跟集线器
lsusb -v 列出系统中所有的USB设备的各个描述符信息:
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 3.00
bDeviceClass 9 Hub
bDeviceSubClass 0 Unused
bDeviceProtocol 3
bMaxPacketSize0 9
idVendor 0x1d6b Linux Foundation
idProduct 0x0003 3.0 root hub
bcdDevice 4.04
iManufacturer 3
iProduct 2
iSerial 1
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 31
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xe0
Self Powered
Remote Wakeup
MaxPower 0mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 9 Hub
bInterfaceSubClass 0 Unused
bInterfaceProtocol 0 Full speed (or root) hub
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0004 1x 4 bytes
bInterval 12
bMaxBurst 0
...
这里代表的是 这USB设备的 四个描述符:设备、配置、接口、端点 的描述信息
一 设备、配置、接口、端点、及其描述符、管道、URB
端点:struct usb_host_endpoint
端点是USB通信的最基本形式,每一个USB设备在主机看来就是一个端点的集合,因为主机只能通过端点与设备进行通信,每个端点都有唯一的地址,由设备号和端点号给出。每个端点都有一定的属性,包括传输方向,总线访问频率,带宽,端点号,数据包最大容量等信息。一个端点只能在一个方向上承载数据,从主机到设备(输出端点),或者从设备到主机(输入端点),因此端点可以看做是一个单项的管道。端点0通常作为控制端点,用于设备初始化参数等。只要设备连接到USB上并上电,端点0就可以被访问。端点1、2 通常作为数据端点,用于主机与设备之间数据往来。
接口: struct usb_interface
接口由多个端点组成,其中端点我们知道可以看作一个单项的数据管道,即单项的数据通道。那么众多端点汇集成的接口 就形成了一个基本功能,所以接口就代表一个基本的功能,是USB设备驱动程序控制的对象,一个功能复杂的USB设备可以具有多个接口,即多个功能。另外每个接口可以有备用接口,一提供不同质量的服务参数。
配置:struct usb_host_config
由众多接口汇集而成,那么配置可以理解为是 功能集,包含选择的若干功能。一个配置中的所有借口可以同时有效,即所有功能可以同时工作。
设备:usb_device 代表一个usb设备
USB标准定义了一系列的 描述符数据结构来保存设备的信息。Linux-USB核心定义的描述符有四种类型。
设备描述符:存放设备的普通信息 如产品ID、设备ID等 — struct usb_device_descriptor
配置描述符:描述设置的配置模式信息,如设备是总线供电还是自己供电 ---- struct usb_config_descriptor
接口描述符:使得USB设备能支持多种功能 ---- struct usb_interface_descriptor
端点描述符:描述端点信息 ---- struct usb_endpoint_descriptor
打印设备上的全部端点地址:
//USB device
struct usb_device *udevice;
//USB设备描述符
struct usb_device_descriptor u_d_desc = udevice->descriptor
//USB 配置
struct usb_host_config *uconfig;
///USB 配置描述符
struct usb_config_descriptor U_c_desc;
//接口
struct usb_interface *uinterface;
//接口设置
struct usb_host_interface *ualtsetting
//接口描述符
struct usb_interface_descriptor u_i_desc;
//USB 端点
struct usb_host_endpoint *uendpoint;
//USB 端点描述符
struct usb_endpoint_descriptor u_e_desc;
//获取配置
uconfig = udevice->actconfig;
//获取配置描述符
U_c_desc = uconfig->desc;
//遍历配置(功能集)中的所有接口(功能)
for(int i=0; i<U_c_desc.bNumInterfaces; i++){
//获取接口
uinterface = udevice->actconfig->interface[i];
//遍历所有备用设置
for(int j=0; j<uinterface->num_altsetting; j++){
//获取备用甚设置
ualtsetting = &uinterface->altsetting[j];
//获取接口描述符
u_i_desc = ualtsetting->desc;
//遍历接口下的所有端点
for(int k=0; k<u_i_desc.bNumEndpoints; k++){
//获取端点
uendpoint = &ualtsetting->endpoint[k];
//获取端点描述符
u_e_desc = uendpoint->desc;
//打印端点地址
printk("Endpoint Address = %d\n",u_e_desc.bEndpointAddress);
}
}
}
关系如下:
//usb 设备
struct usb_device
//设备描述符
struct usb_device_descriptor descriptor;
//配置
struct usb_host_config *actconfig;
//USB 配置描述符
struct usb_config_descriptor desc;
//接口集(功能集)
struct usb_interface *interface[USB_MAXINTERFACES];
//当前激活的备用设置
struct usb_host_interface *cur_altsetting;
//备用设置数组
struct usb_host_interface *altsetting;
//接口描述符
struct usb_interface_descriptor desc;
//端点数量
__u8 bNumEndpoints;
//端点数组
struct usb_host_endpoint *endpoint;
//端点描述符
struct usb_endpoint_descriptor desc;
//端点地址
__u8 bEndpointAddress;
//备用设置数量
unsigned num_altsetting;
URB: USB数据传输机制使用的核心数据结构。URB供USB协议栈使用:struct urb
管道: 管道包括以下几个部分:
端点地址
数据传输方向(IN或OUT)
数据传输模式(控制模式,中断模式,批量模式,等时模式)
管道是URB的重要成员,为USB数据传输提供地址信息。
二 Linux USB 子系统
如下图是 Linux USB 子系统的架构。该子系统由以下几个部分组成:
USB核心:USB核心由一些基础代码组成,这些基础代码包括结构体和函数定义,供HCD和客户驱动程序使用,同时也间接的使得客户驱动与具体的主控制器无关。
驱动不同主机控制器的 HCD
用于根集线器(包括物理集线器)的Hub驱动和一个内核辅助线程 khubd。khubd监视与该集线器连接的所有端口。系统检测端口状态变化以及配置热拔插设备是很消耗时间的事情,而内核提供的基础设施辅助线程就能很好的完成这些任务。通常情况下,该线程处于休眠状态。当集线器驱动程序检测到USB端口状态变化后,该内核线程立马被唤醒。
用于USB客户设备的设备驱动程序
USB文件系统usbfs,能够让你从用户空间驱动USB设备。
三 关于集线器枚举
枚举过程是热拔插USB设备的起始步骤,该过程中,主机控制器最终会获得设备的相关信息并配置好设备。在USB子系统中,集线器驱动程序负责该枚举过程。如将USB笔驱动器插入主机后,设备的枚举分一下几步执行。
1 根集线器报告插入设备导致的端口电流变化,集线器驱动程序检测到这一变化(在Linux-USB里称为 USB_PORT_STAT_C_CONNECTION),唤醒 khubd线程。
2 khubd识别出电流变化在哪个端口,即我们的设备插入的端口
3 接着khubd线程再 1~127中挑选一个数分配给笔驱动器的批量端点,这是通过控制端点0发送控制URB来实现的。
4 khubd线程利用端口0使用的控制URB从笔驱动器设备获取设备描述符,配置描述符。
5 khubd线程请求USB核心把对应的客户驱动程序和该USB设备挂钩。当枚举过程完成后,驱动程序和设备也已经绑定好了,khubd线程就会调用相关客户驱动程序的probe()函数。这里, khubd线程调用的是 storage_probe()。