USB Hub Driver
4 Feb 2024
Read time: 6 minute(s)
普通的 Usb Device 通过内部的 Interface 提供各种业务功能。而 Hub 这类特殊的 Usb Device 功能就一种,那就是监控端口的状态变化:
-
在端口上有设备 attach 时,创建新的 usb device,给其适配驱动。如果是 hub device,子 usb 驱动会进一步扫描端口。
-
在端口上有设备 deattach 时,移除掉对应的 usb device。如果是 hub device 进一步移除其所有的子 usb device。
Hub 也是标准的 Usb Device,它也是标准的流程被上一级设备发现后
创建 Usb Device
→ 创建 Usb
Interface
,然后被 Usb Hub Interface
Driver 给适配到。系统中只有一个 Hub
驱动:static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_SMSC,
.idProduct = USB_PRODUCT_USB5534B,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, hub_id_table);
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
hub_driver 驱动启动以后,只做一件事情发送一个查询端口状态的 urb
:
hub_probe() → hub_configure():
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
/* (1) 分配 urb */
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
ret = -ENOMEM;
goto fail;
}
/* (2) 初始化 urb,作用就是通过 ep0 查询 hub 的端口状态
urb 的回调函数是 hub_irq()
*/
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
/* (3) 发送 urb */
hub_activate(hub, HUB_INIT);
}
↓
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
/* (3.1) 提交 urb */
status = usb_submit_urb(hub->urb, GFP_NOIO);
}
Normal Hub Port op
在普通的 hub 中,端口操作是通过标准的 urb 发起 usb ep0 读写。分为两类:
通过轮询读取 Hub Class-specific Requests 配置来查询端口的状态:
设置和使能端口也是通过 Hub Class-specific Requests 中相应的命令实现的:
RootHub Port op
而对于 roothub 来说,对端口的操作的 urb 都需要特殊处理 (以 EHCI 的驱动为例):
-
端口状态的变化可以通过 HCD 触发中断再上报:
ehci_irq() → usb_hcd_poll_rh_status() : void usb_hcd_poll_rh_status(struct usb_hcd *hcd) { /* (1) 获取端口状态的变化 */ length = hcd->driver->hub_status_data(hcd, buffer); if (length > 0) { /* try to complete the status urb */ spin_lock_irqsave(&hcd_root_hub_lock, flags); /* (2) 通过回复 hcd->status_urb 来进行上报 */ urb = hcd->status_urb; if (urb) { clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); hcd->status_urb = NULL; urb->actual_length = length; memcpy(urb->transfer_buffer, buffer, length); usb_hcd_unlink_urb_from_ep(hcd, urb); usb_hcd_giveback_urb(hcd, urb, 0); } else { length = 0; set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); } spin_unlock_irqrestore(&hcd_root_hub_lock, flags); } } ↓ hcd->driver->hub_status_data() → ehci_hub_status_data(): static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { /* (1.1) 通过 HCD 驱动,获取 roothub 端口的状态 */ }
-
设置和使能端口需要嫁接到 HCD 驱动相关函数上实现:
usb_hcd_submit_urb() → rh_urb_enqueue() → rh_call_control() → hcd->driver->hub_control() → ehci_hub_control(): int ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength ) { /* (1) 通过 HCD 驱动,设置 roothub 的端口 */ }
Device Attach
hub_event() → port_event() → hub_port_connect_change() → hub_port_connect():
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
for (i = 0; i < PORT_INIT_TRIES; i++) {
/* (1) 给端口上新 Device 分配 `struct usb_device` 数据结构 */
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
dev_err(&port_dev->dev,
"couldn't allocate usb_device\n");
goto done;
}
/* (2) 给新的 Device 分配一个新的 Address */
choose_devnum(udev);
if (udev->devnum <= 0) {
status = -ENOTCONN; /* Don't retry */
goto loop;
}
/* reset (non-USB 3.0 devices) and get descriptor */
usb_lock_port(port_dev);
/* (3) 使能端口,并且调用 hub_set_address() 给 Device 配置上新分配的 Address */
status = hub_port_init(hub, udev, port1, i);
usb_unlock_port(port_dev);
/* (4) 注册 `struct usb_device` */
status = usb_new_device(udev);
}
}
Device Deattach
hub_event() → port_event() → hub_port_connect_change() → hub_port_connect():
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
/* (1) 移除端口上的 `struct usb_device` */
if (udev) {
if (hcd->usb_phy && !hdev->parent)
usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
usb_disconnect(&port_dev->child);
}
}