USB Hub Driver
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); } }