本文基于君正平台(SoC)和其集成的 DWC2(DesignWare® USB 2.0 Controller),对 USB 子系统的核心流程进行了详细分析。文章主要聚焦于以下几个方面:
USB 设备描述符和 urb 通讯
USB 设备驱动管理
USB 主机控制器(HCD)的注册流程;
USB 设备的识别流程;
希望本文能为从事相关开发或研究的读者提供有价值的参考
一、USB 描述符
USB 设备用 USB 描述符 来描述自己属性及用途 . 所以设备端必须实现对应的描述符 . 主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯方式等.
1 2 3 4 5 6 7 8 #define USB_DT_DEVICE 0x01 #define USB_DT_CONFIG 0x02 #define USB_DT_STRING 0x03 #define USB_DT_INTERFACE 0x04 #define USB_DT_ENDPOINT 0x05 #define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 0x0b ......
1. 数据结构
1.1 设备描述符
每一个 USB 设备只有一个设备描述符 , 主要向主机说明设备类型、端点0最大包长、设备版本、配置数量等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct usb_device_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __le16 idVendor; __le16 idProduct; __le16 bcdDevice; __u8 iManufacturer; __u8 iProduct; __u8 iSerialNumber; __u8 bNumConfigurations; } __attribute__ ((packed));
其中 bDeviceClass
、bDeviceSubClass
和 bDeviceProtocol
组合起来描述具体的设备的类型.
bDeviceClass
bDeviceSubClass 值
子类别描述
bDeviceProtocol 值
协议描述
设备未定义 (0x00)
0x00
类别由接口指定
0x00
无协议
音频类 (0x01)
0x00
不指定子类别
0x00
无协议
0x01
音频控制设备
0x00
无协议
0x02
音频流传输设备
0x00
无协议
0x03
MIDI 流传输设备
0x00
无协议
通信设备类 (0x02)
0x00
不指定子类别
0x00
无协议
0x01
直接线路控制模型
0x00
无协议
0x02
通用协议模型
0x00
无协议
0x06
以太网网络设备
0x00
无协议
人机接口设备 (HID, 0x03)
0x00
无子类
0x00
无协议
0x01
键盘
0x01
键盘协议
0x02
鼠标
0x02
鼠标协议
物理设备类 (0x05)
0x00
物理设备
0x00
无协议
(无子类别和协议定义)
图像类 (0x06)
0x00
图像控制设备
0x00
无协议
0x01
图像传输设备
0x00
无协议
(无其他子类和协议定义)
打印机类 (0x07)
0x00
打印机设备
0x01
双向协议
(0x02 - IEEE 1284.4 协议)
(0x03 - IEEE 1284.4 兼容协议)
大容量存储类 (0x08)
0x00
未定义
0x00
无协议
0x01
只读存储设备 (RBC)
0x00
无协议
0x02
ATAPI 命令块设备
0x00
无协议
0x04
UFI(通用软盘接口设备)
0x00
无协议
0x05
SFF-8070i
0x00
无协议
0x06
SCSI 传输协议
0x50
Bulk-Only Transport
集线器类 (0x09)
0x00
集线器设备
0x00
全速集线器(FS)
(0x01 - 高速集线器(HS))
复合设备类 (0xEF)
0x02
常规复合设备
0x01
FCP (功能复合协议)
应用特定类 (0xFE)
0x01
DFU(设备固件升级)
0x01
设备固件升级协议
0x02
IrDA 桥接设备
0x00
无协议
0x03
USB 测试和测量类设备
0x00
无协议
供应商自定义类 (0xFF)
0x00
供应商自定义设备
0x00
无协议
1.2 配置描述符
一个设备可以有多个配置描述符, 大部分 usb 只有一个配置描述符.
1 2 3 4 5 6 7 8 9 10 11 12 13 struct usb_config_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wTotalLength; __u8 bNumInterfaces; __u8 bConfigurationValue; __u8 iConfiguration; __u8 bmAttributes; __u8 bMaxPower; } __attribute__ ((packed));
1.3 接口描述符
1 2 3 4 5 6 7 8 9 10 11 12 struct usb_interface_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bInterfaceNumber; __u8 bAlternateSetting; __u8 bNumEndpoints; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; } __attribute__ ((packed));
主要用于描述复合设备中的接口关联.
1 2 3 4 5 6 7 8 9 10 11 struct usb_interface_assoc_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bFirstInterface; __u8 bInterfaceCount; __u8 bFunctionClass; __u8 bFunctionSubClass; __u8 bFunctionProtocol; __u8 iFunction; } __attribute__ ((packed));
1.4 端点描述符
1 2 3 4 5 6 7 8 9 10 11 12 struct usb_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bEndpointAddress; __u8 bmAttributes; __le16 wMaxPacketSize; __u8 bInterval; __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed));
其中 bmAttributes
的详细说明如下
位位置
描述
值
含义
位 0-1
传输类型
00
控制传输(Control)
01
同步传输(Isochronous)
10
批量传输(Bulk)
11
中断传输(Interrupt)
位 2-3
同步类型(同步传输时有效)
00
无同步
01
异步
10
自适应
11
同步
位 4-5
使用类型(同步传输时有效)
00
数据端点
01
反馈端点
10
隐式反馈数据端点
11
保留
位 6-7
保留位
Linux 内核中的 USB Core 是 USB 驱动子系统的核心部分,负责管理整个 USB 子系统的初始化、配置、驱动程序的加载, 它包括 usb 设备驱动管理 , USB 主机控制器驱动(Host Controller Driver, HCD) 、usb hub 、USB Gadget 、 urb数据传输 、USB 设备驱动程序 等服务.
1.5 usb_hub_descriptor
hub 描述符用来描述 usb hub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct usb_hub_descriptor { __u8 bDescLength; __u8 bDescriptorType; __u8 bNbrPorts; __le16 wHubCharacteristics; __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; union { struct { __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7 ) / 8 ]; __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7 ) / 8 ]; } __attribute__ ((packed)) hs; struct { __u8 bHubHdrDecLat; __le16 wHubDelay; __le16 DeviceRemovable; } __attribute__ ((packed)) ss; } u; } __attribute__ ((packed));
1.5 usb_host_config
设备用来管理配置描述符, 一般情况一个设备只有一个配置描述符.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct usb_host_config { struct usb_config_descriptor desc; char *string; struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; struct usb_interface *interface[USB_MAXINTERFACES]; struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; unsigned char *extra; int extralen; };
1.6 usb_interface_cache
用来管理当前配置描述符下所有的接口信息, 使用了柔性数组的技巧, 在 altsetting 数组中保存管理接口描述符的结构体 usb_host_interface
1 2 3 4 5 6 struct usb_interface_cache { unsigned num_altsetting; struct kref ref; struct usb_host_interface altsetting[0 ];
1.7 usb_host_interface
用来管理单独的接口信息.
1 2 3 4 5 6 7 8 9 10 struct usb_host_interface { struct usb_interface_descriptor desc; int extralen; unsigned char *extra; struct usb_host_endpoint *endpoint; char *string; };
1.8 usb_host_endpoint
用来管理单独的接口描述符.
1 2 3 4 5 6 7 8 9 10 11 12 struct usb_host_endpoint { struct usb_endpoint_descriptor desc; struct usb_ss_ep_comp_descriptor ss_ep_comp; struct list_head urb_list; void *hcpriv; struct ep_device *ep_dev; unsigned char *extra; int extralen; int enabled; int streams; };
2. 描述符的构建
usb core 中通过函数 usb_get_configuration
来构建描述符信息.
检查配置描述符 dev->descriptor.bNumConfigurations 的数量是否合理, 配置描述符只能 1-7 最多 8 个最少 1 个 .
首先通过函数 usb_get_descriptor 获取配置描述符的信息, 以及该配置中所有相关描述符的总长度 desc->wTotalLength .
第二次通过第一次获取到的 desc->wTotalLength 长度, 再次通过 usb_get_descriptor 获取到所有的配置描述符, 保存到 dev->rawdescriptors[cfgno] 中, 这里面包含所有的描述符, 被称为原始描述符 .
调用 usb_parse_configuration 解析原始描述符, 解析完之后就会构建出完整的设备描述符信息, 注意这里只构建描述符信息, 并不会创建对应的接口设备. 代码太长就不贴了, 解析完之后的数据如下图所示
补充说明: 内核中设备描述符的构建是自动的 , 第一种是 root hub, root hub 本身也是一个 usb 设备, 他是在 root hub 创建的时候自动调用时调用 usb_get_configuration 构建的描述符 . 第二种是连接在 hub 上的设备是在设备插入的时候由 hub 调用 usb_get_configuration 构建描述符. 后文叙述这个过程.
二、usb 设备驱动管理
usb core
中的设备和驱动都有两种. 分别是usb 设备 usb_device 、usb 设备驱动 usb_device_driver , usb 接口设备 usb_interface 、usb 驱动 usb_driver .
Linux 内核中 USB 驱动设计采用了 usb_device 和 usb_interface 的分离设计,以便更好地支持复合设备和多功能设备的管理
usb_device 表示物理上的整个 USB 设备 ,负责设备的基本信息和底层通信
usb_interface 则代表设备中的具体功能单元 ,便于系统为每个接口加载独立的驱动
usb_device 对应的驱动为 usb_device_driver , 对应的 probe 接口为 usb_probe_device , 对应的设备类型为 usb_device_type
usb_interface 对应的驱动为 usb_driver , 对应的 probe 接口为 usb_probe_interface , 对应的设备类型为 usb_if_device_type
他们在内核中通过设备驱动模型由 usb 总线管理, 如下图所示.
1. 设备和驱动类型
1.1 usb_device
usb 设备它由 struct usb_device
描述,代表的是整个物理 USB 设备 .它包含该 USB 设备的所有信息 ,包括设备描述符、配置描述符、地址等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct usb_device { int devnum; char devpath[16 ]; u32 route; enum usb_device_state state ; enum usb_device_speed speed ; ...... struct usb_device *parent ; struct usb_bus *bus ; struct usb_host_endpoint ep0 ; struct device dev ; struct usb_device_descriptor descriptor ; struct usb_host_bos *bos ; struct usb_host_config *config ; struct usb_host_config *actconfig ; struct usb_host_endpoint *ep_in [16]; struct usb_host_endpoint *ep_out [16]; char **rawdescriptors; ...... }; #define to_usb_device(d) container_of(d, struct usb_device, dev)
1.2 usb 设备分配
接口 usb_alloc_dev 用于分配 usb_device. 主要的内容如下
分配 usb_device 结构体的内存空间,并为相关字段做初始化
设置总线类型 (dev->dev.bus = &usb_bus_type) 和设备类型 (dev->dev.type = &usb_device_type) ,表示这是一个 USB 设备。
为设备分配默认的属性节点、DMA 掩码和设备节点信 初始化端点 0 的描述符,并将其添加到端点输出 dev->ep_out[0] 数组和输入 dev->ep_in[0] 数组中 .
根据是否有父设备(parent
)来处理不同的设备路径和路由, 若设备无父设备,则设置为 root hub
,若设备有父设备,则基于父设备的路径生成子设备的路径
如果启用了电源管理配置(CONFIG_PM
),则设置设备的自动挂起延迟时间, 如果设备为root hub
,则直接设置为授权;否则,授权状态由控制器的默认授权配置决定
1.3 usb_device_driver
内核为 usb_device 提供专用的驱动 usb 设备驱动 usb_device_driver , 其中 usb_device_driver->drvwrap->for_devices
标志位用来区分当前的 driver 是 usb_device_driver
还是 usb_driver
.为 1 表示 usb_device_driver ,为 0 表示 usb_driver .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct usb_device_driver { const char *name; int (*probe) (struct usb_device *udev); void (*disconnect) (struct usb_device *udev); int (*suspend) (struct usb_device *udev, pm_message_t message); int (*resume) (struct usb_device *udev, pm_message_t message); struct usbdrv_wrap drvwrap; unsigned int supports_autosuspend:1 ; }; struct usbdrv_wrap { struct device_driver driver; int for_devices; };
驱动的注册接口为 usb_register_device_driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int usb_register_device_driver (struct usb_device_driver *new_udriver, struct module *owner) { int retval = 0 ; if (usb_disabled ()) return -ENODEV; new_udriver->drvwrap.for_devices = 1 ; new_udriver->drvwrap.driver.name = new_udriver->name; new_udriver->drvwrap.driver.bus = &usb_bus_type; new_udriver->drvwrap.driver.probe = usb_probe_device; new_udriver->drvwrap.driver.remove = usb_unbind_device; new_udriver->drvwrap.driver.owner = owner; retval = driver_register (&new_udriver->drvwrap.driver); if (!retval) pr_info ("%s: registered new device driver %s\n" , usbcore_name, new_udriver->name); else printk (KERN_ERR "%s: error %d registering device " " driver %s\n" , usbcore_name, retval, new_udriver->name); return retval; } EXPORT_SYMBOL_GPL (usb_register_device_driver);
1.4 USB 接口设备
USB 接口设备 usb_interface 是 usb_device 中的一个功能单元,代表设备中的某个具体功能接口 . 一个 USB 设备可以包含多个接口,每个接口可以实现不同的功能.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct usb_interface { struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; unsigned num_altsetting; struct usb_interface_assoc_descriptor *intf_assoc; int minor; enum usb_interface_condition condition; unsigned sysfs_files_created:1 ; unsigned ep_devs_created:1 ; unsigned unregistering:1 ; unsigned needs_remote_wakeup:1 ; unsigned needs_altsetting0:1 ; unsigned needs_binding:1 ; unsigned resetting_device:1 ; unsigned authorized:1 ; struct device dev; struct device *usb_dev; atomic_t pm_usage_cnt; struct work_struct reset_ws; }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev)
同样 usb core也提供了对应的驱动 usb_driver
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 struct usb_driver { const char *name; int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); int (*reset_resume)(struct usb_interface *intf); int (*pre_reset)(struct usb_interface *intf); int (*post_reset)(struct usb_interface *intf); const struct usb_device_id *id_table; struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1 ; unsigned int supports_autosuspend:1 ; unsigned int disable_hub_initiated_lpm:1 ; unsigned int soft_unbind:1 ; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) struct usbdrv_wrap { struct device_driver driver; int for_devices; };
对应的注册接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int usb_register_driver (struct usb_driver *new_driver, struct module *owner, const char *mod_name) { int retval = 0 ; if (usb_disabled ()) return -ENODEV; new_driver->drvwrap.for_devices = 0 ; new_driver->drvwrap.driver.name = new_driver->name; new_driver->drvwrap.driver.bus = &usb_bus_type; new_driver->drvwrap.driver.probe = usb_probe_interface; new_driver->drvwrap.driver.remove = usb_unbind_interface; new_driver->drvwrap.driver.owner = owner; new_driver->drvwrap.driver.mod_name = mod_name; spin_lock_init (&new_driver->dynids.lock); INIT_LIST_HEAD (&new_driver->dynids.list); retval = driver_register (&new_driver->drvwrap.driver); if (retval) goto out; retval = usb_create_newid_files (new_driver); if (retval) goto out_newid; pr_info ("%s: registered new interface driver %s\n" , usbcore_name, new_driver->name); out: return retval; out_newid: driver_unregister (&new_driver->drvwrap.driver); printk (KERN_ERR "%s: error %d registering interface " " driver %s\n" , usbcore_name, retval, new_driver->name); goto out; } EXPORT_SYMBOL_GPL (usb_register_driver);
2. 总线
2.1 usb_bus_type
它在 usb core 中用于管理维护usb 设备 usb_device 、usb 设备驱动 usb_device_driver 、usb 接口设备 usb_interface 、usb 驱动 usb_driver 以及他们的匹配.
1 2 3 4 5 struct bus_type usb_bus_type = { .name = "usb" , .match = usb_device_match, .uevent = usb_uevent, };
它由系统在开机的时候默认默认创建.
1 2 3 4 5 6 7 static int __init usb_init (void ) { retval = bus_register (&usb_bus_type); } subsys_initcall (usb_init);
设备驱动的匹配规则由 usb_bus_type
提供的 usb_device_match
函数决定.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 static int usb_device_match (struct device *dev, struct device_driver *drv) { if (is_usb_device (dev)) { if (!is_usb_device_driver (drv)) return 0 ; return 1 ; } else if (is_usb_interface (dev)) { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; if (is_usb_device_driver (drv)) return 0 ; intf = to_usb_interface (dev); usb_drv = to_usb_driver (drv); id = usb_match_id (intf, usb_drv->id_table); if (id) return 1 ; id = usb_match_dynamic_id (intf, usb_drv); if (id) return 1 ; } return 0 ; }
2.2 匹配规则
设备 usb_device 且驱动是 usb_device_driver 则直接匹配成功
接口设备 usb_interface 的匹配则有两次匹配机会, 首先是和 usb_drv->id_table 进行匹配 , 然后是和 usb_drv->dynids 中链接的 dynid->id 进行匹配 , 匹配规则如下.
匹配 usb_interface 所属的 usb_device 和 usb_drv->id_table 中设置的 match_flags 匹配条件 进行匹配, 匹配成功直接返回
第一种匹配失败之后, 匹配 usb_interface 当前使用的接口描述符 cur_altsetting 和 usb_drv->id_table 中设置的 match_flags 匹配条件 进行匹配
匹配规则如下所
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct usb_device_id { __u16 match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 __u16 idVendor; __u16 idProduct; __u16 bcdDevice_lo; __u16 bcdDevice_hi; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 bInterfaceNumber; kernel_ulong_t driver_info __attribute__((aligned (sizeof (kernel_ulong_t )))); };
3. 创建接口设备
usb 接口设备(usb_interface) 描述的是具体的接口, 依赖于 usb 设备(usb_device) . 从前面的匹配规则可以知道, 设备 usb_device 且驱动是 usb_device_driver 则直接匹配成功 . 利用这个规则内核提供了一个 usb_generic_driver
专门用来创建 usb 设备(usb_device) 下的 usb 接口设备(usb_interface) .
1 2 3 4 5 6 7 8 9 10 struct usb_device_driver usb_generic_driver = { .name = "usb" , .probe = generic_probe, .disconnect = generic_disconnect, #ifdef CONFIG_PM .suspend = generic_suspend, .resume = generic_resume, #endif .supports_autosuspend = 1 , };
内核中只要注册 usb 设备(usb_device) 都会调用 generic_probe
如下图所示.
然后在 generic_probe
中调用 usb_set_configuration
为该设备创建其所属的 usb 接口设备(usb_interface) .然后注册 usb_interface 到 usb_bus_type 同时设置设备类型为 usb_if_device_type .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static int generic_probe (struct usb_device *udev) { int err, c; if (udev->authorized == 0 ) dev_err (&udev->dev, "Device is not authorized for usage\n" ); else { c = usb_choose_configuration (udev); if (c >= 0 ) { err = usb_set_configuration (udev, c); if (err && err != -ENODEV) { dev_err (&udev->dev, "can't set config #%d, error %d\n" , c, err); } } } usb_notify_add_device (udev); return 0 ; }
接口设备创建流程如下所示.
此时我们已经可以大致总结出 usb 设备的枚举流程,
识别到设备之后, 调用 usb_new_deivce 解析设备的描述符信息, 创建设备对应的 usb_device
然后会调用 usb core 提供的 generic_probe 函数, 在这里为设备的每一个接口创建接口设备 usb_interface
但是还是有以下疑问, 设备在哪里被检测? 如何触发检测等? 等这些问题, 这里可以先给出结论, 在hub中被检查, 详细参考后文 hub 章节相关内容.
三、usb 通讯
usb core 中为 usb 通讯提供了统一的接口 urb . URB 用于描述一个 USB 请求,它封装了与 USB 设备通信的所有必要信息,包括数据传输、控制命令等。通过 URB, 驱动程序可以发送和接收数据以及执行各种操作。每一次传输请求都需要通过 urb 进行, 每个 urb 都是独立的. 并且可以多次重复使用. urb 的数据结构如下所示.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 struct urb { struct kref kref; void *hcpriv; atomic_t use_count; atomic_t reject; int unlinked; struct list_head urb_list; struct list_head anchor_list; struct usb_anchor *anchor; struct usb_device *dev; struct usb_host_endpoint *ep; unsigned int pipe; unsigned int stream_id; int status; unsigned int transfer_flags; void *transfer_buffer; dma_addr_t transfer_dma; struct scatterlist *sg; int num_mapped_sgs; int num_sgs; u32 transfer_buffer_length; u32 actual_length; unsigned char *setup_packet; dma_addr_t setup_dma; int start_frame; int number_of_packets; int interval; int error_count; void *context; usb_complete_t complete; struct usb_iso_packet_descriptor iso_frame_desc[0 ]; };
urb 的使用分为三步, 创建 urb, 填充 urb, 提交 urb.
1. 创建 urb
urb 的创建比较简单调用 usb_alloc_urb 即可创建一个 urb.
1 struct urb *usb_alloc_urb (int iso_packets, gfp_t mem_flags);
2. 填充 urb
根据传输类型的不同内核提供了不同的填充接口
2.1 控制传输
主要用于获取设备描述符(如 USB_REQ_GET_DESCRIPTOR 请求) , 设置设备配置(如 USB_REQ_SET_CONFIGURATION 请求) , 与 USB 设备的控制端点通信 等场景.
1 2 3 4 5 6 7 8 9 10 static inline void usb_fill_control_urb ( struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context ) ;
2.2 批量传输
用于大容量数据传输 ,如从 USB 存储设备读取数据或向其写入数据 . 打印机通信(如文件传输到 USB 打印机). 适合对数据速率要求较高、可靠性优先的设备
1 2 3 4 5 6 7 8 9 static inline void usb_fill_bulk_urb ( struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context ) ;
2.3 中断传输
用于键盘、鼠标等低延迟输入设备的数据传输 . 游戏控制器的按键输入与状态反馈. 需要周期性短数据通信的场景 .
1 2 3 4 5 6 7 8 9 10 static inline void usb_fill_int_urb ( struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval ) ;
2.4 同步传输
同步传输并没有专门的接口需要手动填充, 用于实时性高的音频设备, 视屏流设备的数据传输.
1 2 3 4 5 6 7 8 9 10 11 12 13 struct urb *urb = usb_alloc_urb (num_packets, GFP_KERNEL);urb->dev = dev; urb->pipe = usb_sndisocpipe (dev, endpoint); urb->transfer_buffer = transfer_buffer; urb->transfer_buffer_length = buffer_length; urb->number_of_packets = num_packets; urb->interval = interval; urb->complete = complete_fn; urb->context = context; for (int i = 0 ; i < num_packets; i++) { urb->iso_frame_desc[i].offset = i * packet_size; urb->iso_frame_desc[i].length = packet_size; }
3. 提交 urb
接口 usb_submit_urb 用于提交 urb . 函数原型如下.
1 int usb_submit_urb (struct urb *urb, gfp_t mem_flags) ;
提交流程如下图所示.
4. pipe
urb 传输过程中的参数, 它包含传输需要的, 传输类型 , 端点号 , 设备号 , 方向标志 . 也就是 pipe 就决定了传输的类型方向以及端点. 如下所示
位范围
位表示内容
描述
示例值
31–30
PIPE_*
(传输类型)
指定传输类型:控制、同步、批量、中断。
00
(控制)、01
(同步)、10
(批量)、11
(中断)。
29–16
endpoint
(端点号)
USB 端点号,用于标识设备上的端点。
0x01
(端点 1)
15–8
dev->devnum
(设备号)
USB 设备号,用于标识具体的连接设备。
0x02
(设备号 2) ,不能超过 128 个
7
USB_DIR_IN
(方向标志)
指定传输方向:OUT 或 IN。
1
(IN,设备到主机)、0
(OUT,主机到设备)
6–0
保留
通常未使用,可能用于协议扩展。
内核也提供了创建的接口如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint) { return (dev->devnum << 8 ) | (endpoint << 15 ); } #define usb_sndctrlpipe(dev, endpoint) \ ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint)) #define usb_rcvctrlpipe(dev, endpoint) \ ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndisocpipe(dev, endpoint) \ ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint)) #define usb_rcvisocpipe(dev, endpoint) \ ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndbulkpipe(dev, endpoint) \ ((PIPE_BULK << 30) | __create_pipe(dev, endpoint)) #define usb_rcvbulkpipe(dev, endpoint) \ ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndintpipe(dev, endpoint) \ ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint)) #define usb_rcvintpipe(dev, endpoint) \ ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
四、 hcd
HCD (Host Controller Driver) 是在 USB 主机控制器驱动, 它主要包括 usb_hcd 、root_hub 两个部分.它们分别负责不同的任务.
usb_hcd 主要用于管理和维护 USB 控制器的硬件资源,包括 PHY (物理层接口)、IRQ (中断) 等硬件相关的部分 。这一部分主要与底层硬件打交道,确保 USB 控制器的正常工作。它通过控制器的寄存器操作和物理层接口来实现 USB 通信,并管理中断处理流程
root_hub 是 USB 控制器的一部分, 它由 usb 控制器模拟出来,作为位于拓扑结构顶部的 hub , 与普通的物理 hub 在功能上类似. 在内核中 root hub 和 hub 均用 usb_device 来描述 ,它们的主要职责如下:
hub 的功能 : 负责 usb 设备的连接和枚举,管理端口的电源状态和数据传输,并支持多层级 usb 拓扑结构的扩展。
root_hub 的特殊性 : 作为控制器的逻辑扩展, root hub 不是真正的物理设备, 但功能上完全模拟了普通 hub 的行为。它位于 usb 层次结构的顶端, 与主机控制器直接通信, 所有 usb 设备连接的起点.
一个 usb host 最多可以同时支持 128 个地址 , 地址 0 作为默认地址,只在设备枚举期间临时使用,而不能被分配给任何一个设备 ,因此一个 usb host 最多可以同时支持 127 个地址.
1. 数据结构
1.1 usb_hcd
用来描述 usb 控制器的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct usb_hcd { struct usb_bus self ; struct kref kref ; const char *product_desc; int speed; char irq_descr[24 ]; struct timer_list rh_timer ; struct urb *status_urb ; #ifdef CONFIG_PM struct work_struct wakeup_work ; #endif const struct hc_driver *driver ; struct usb_phy *usb_phy ; struct phy *phy ; ...... unsigned long hcd_priv[0 ] __attribute__ ((aligned(sizeof (s64)))); };
1.2 usb_bus
usb_bus 表示 usb 控制其的一部分, 用来描述 usb 控制器的物理总线信息.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct usb_bus { struct device *controller ; struct device *sysdev ; int busnum; const char *bus_name; u8 uses_dma; u8 uses_pio_for_control; u8 otg_port; unsigned is_b_host:1 ; unsigned b_hnp_enable:1 ; unsigned no_stop_on_short:1 ; unsigned no_sg_constraint:1 ; unsigned sg_tablesize; int devnum_next; struct mutex devnum_next_mutex ; struct usb_devmap devmap ; struct usb_device *root_hub ; ...... };
1.3 usb_hub
usb_hub 用来描述 hub 和 hub 的接口状态.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct usb_hub { struct device *intfdev; struct usb_device *hdev; struct kref kref; struct urb *urb; u8 (*buffer)[8 ]; union { struct usb_hub_status hub; struct usb_port_status port; } *status; struct mutex status_mutex; int error; int nerrors; ...... struct work_struct events; struct usb_port **ports; };
2. 注册 hcd
注册 hcd 主要分为两步, 首先调用 usb_create_hcd 创建 usb_hcd 结构, 然后调用 usb_add_hcd 注册.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int dwc2_hcd_init (struct dwc2_hsotg *hsotg) { struct platform_device *pdev = to_platform_device (hsotg->dev); struct resource *res; struct usb_hcd *hcd; struct dwc2_host_chan *channel; u32 hcfg; int i, num_channels; int retval; ...... hcd = usb_create_hcd (&dwc2_hc_driver, hsotg->dev, dev_name (hsotg->dev)); ...... retval = usb_add_hcd (hcd, hsotg->irq, IRQF_SHARED); ...... }
2.1 usb_create_hcd
usb_create_hcd 函数的主要功能是创建并初始化一个 usb_hcd, 该函数的功能分为几个主要步骤:
分配和初始化 usb_hcd 结构体, 分配内存空间,如果失败则返回 NULL
为 address0_mutex 和 bandwidth_mutex 锁分配和初始化
初始化 usb 逻辑总线 usb_bus, 设置总线名称、控制器和 DMA 支持等
设置定时器, 初始化定时器并配置回调函数 rh_timer_func
. 这个定时器是hub用来监听是否有新的 usb 插入用的 , 老式的 ehci , xhci 等usb 控制器使用这个方式 .而文章中的 dwc2 以及后面的 dwc3 都采用中断的方式检查是否有新的设备插入 .
电源管理支持, 如果启用了电源管理,初始化恢复工作队列
设置驱动和描述信息, 配置驱动、速度标志和产品描述信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, const char *bus_name) { return usb_create_shared_hcd(driver, dev, bus_name, NULL ); } struct usb_hcd *usb_create_shared_hcd (const struct hc_driver *driver, struct device *dev, const char *bus_name, struct usb_hcd *primary_hcd) { struct usb_hcd *hcd ; hcd = kzalloc(sizeof (*hcd) + driver->hcd_priv_size, GFP_KERNEL); if (!hcd) { dev_dbg (dev, "hcd alloc failed\n" ); return NULL ; } if (primary_hcd == NULL ) { hcd->address0_mutex = kmalloc(sizeof (*hcd->address0_mutex), GFP_KERNEL); if (!hcd->address0_mutex) { kfree(hcd); dev_dbg(dev, "hcd address0 mutex alloc failed\n" ); return NULL ; } mutex_init(hcd->address0_mutex); hcd->bandwidth_mutex = kmalloc(sizeof (*hcd->bandwidth_mutex), GFP_KERNEL); if (!hcd->bandwidth_mutex) { kfree(hcd->address0_mutex); kfree(hcd); dev_dbg(dev, "hcd bandwidth mutex alloc failed\n" ); return NULL ; } mutex_init(hcd->bandwidth_mutex); dev_set_drvdata(dev, hcd); } else { mutex_lock(&usb_port_peer_mutex); hcd->address0_mutex = primary_hcd->address0_mutex; hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; hcd->shared_hcd = primary_hcd; primary_hcd->shared_hcd = hcd; mutex_unlock(&usb_port_peer_mutex); } kref_init(&hcd->kref); usb_bus_init(&hcd->self); hcd->self.controller = dev; hcd->self.bus_name = bus_name; hcd->self.uses_dma = (dev->dma_mask != NULL ); init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long ) hcd; #ifdef CONFIG_PM INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif hcd->driver = driver; hcd->speed = driver->flags & HCD_MASK; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller" ; return hcd; } EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
2.2 usb_add_hcd
注册 hcd 主要包括两个内容, 一个是 usb controler 相关的 phy 等硬件的初始化, 另一个则是 root hub 的注册.如下图所示
root hub 是 usb 控制器虚拟出来的 usb_device:
在 usb_new_device 中调用 usb_get_configuration 从 usb 控制器获取并解析 root hub 设备的描述符信息, 构建描述符拓扑结构. 并将该 udev 注册进内核.
root hub 对应的 udev 注册进内核之后, 会和通用的 usb_generic_driver 匹配, 并调用 generic_probe 函数, 为 root hub 创建对应接口设备 usb_interface 并将其注册进内核.
然后该接口设备会匹配应的驱动的 prob 函数, 君正用的匹配规则如下所示, 上图也标注出来了.
1 2 3 4 5 static const struct usb_device_id hub_id_table[] = { { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, .bDeviceClass = USB_CLASS_HUB}, { } }
匹配成功之后会调用 hub_probe 函数.然后做进一步的处理, 如下图所示
如图所示整个过程分为3个阶段
2.2.1 hub_probe
设置 hub 自动休眠时间, 使能自动休眠
检测 hdev->level 设备的拓扑层级最大不能超过 6 层
检测 hub 的端点数, hub 有且只能有一个中断输入端点
初始化 hub_event 工作队列, 分配并初始化 usb_hub 结构, 同时通过 usb_set_intfdata* (intf, hub) 设置接口的私有数据为 usb_hub
分配更新 hub 描述符, 更新端口数量, root hub 只有一个端口
更新 hub 的电源切换模式, hub 过流保护相关标志位, 根据 hub 标志位设置 hub 的时延.
获取 hub 的状态, 更新 hub 的电流设置, 如果电流太小打印异常, 如果是自供电设备也打印日志提示. 如果设置了过流保护也打印日志提示.
创建一个中断传输的 pipe, 使用这个 pipe 和 hub_irq 函数创建 hub->urb. 使用中断传输调用这个 urb 时最后会调用到 hub_irq.
根据端口的数量为 hub 接口设备创建并注册端口 usb_port, 存放在 hub->ports[i]中.
回调 hcd->driver->update_hub_device()
2.2.3 hub_activate
非唤醒的状态, 才能触发 init1 阶段, 更新电源状态为上电状态.
init2 阶段检查并更新端口状态
init3 阶段调用 usb_submit_urb 提交 hub->urb 最终通过定时器回调 hub_irq 接口, 调用 kick_hub_wq 启动 hub_event
五、usb 设备的识别
usb 设备的识别根据同的 usb 控制器有不同的实现方式, 但总体上分为两种.
通过定时器的方式, 轮训监听是否有设备的插入. , 早期的设备采用这种方式 UHCI 等
通过中断的方式监听, 如果有设备插入触发中断, 启动检测流程. 新的中断控制器 OHCI , EHCI, xHCI, dwc2 等都采用中断的方式.
usb 控制器触发中断, 调用 usb_hcd_resume_root_hub 唤醒 usb 系统 , 之后调用 usb_resume_both
usb_resume_both 先唤醒 usb 设备, 给 usb 设备上电等相关初始化. 然后唤醒 usb 接口设备触发 usb 的检测, 在 hub_event 中检测到 usb change
在 usb_new_device 中尝试枚举 usb 设备, 如果枚举成功则注册设备到 usb_bus_type 总线中 , 匹配通用驱动 usb_generic_driver 为 usb 设备创建 usb 接口设备
在 generic_probe 解析设备描述中的接口描述符, 为设备创建接口设备 , 并也将其注册进入 usb_bus_type 总线中, 在总线中匹配对应接口的驱动 , 这个驱动就是我们所说的 usb 驱动了
注意: 参考前面 usb设备 和 usb接口设备的概念
usb 设备: 由 struct usb_device
描述,代表的是整个物理 USB 设备 .
usb 接口设备: 由 usb_interface
是 usb_device 中的一个功能单元
不同厂家的控制器的识别可能略有差别, 但也都是大同小异, rk 使用 extcon 监听usb id 脚的变化, 如果由usb插入则动态调用 usb_add_hcd 注册 usb 控制器, 然后控制器中调用 usb_hcd_resume_root_hub 触发唤醒流程.