1.簡介
整個(gè) USB 系統(tǒng)的通訊模型如上圖所示,本文詳細(xì)解析其中 Host 各模塊的架構(gòu)和原理 (圖中彩色部分)。

2. Usb Core 驅(qū)動(dòng)設(shè)備模型

由前幾節(jié)可知USB將Device進(jìn)一步細(xì)分成了3個(gè)層級:Configuration 配置、Interface 接口、Endpoint 端點(diǎn)。
Usb Core 為其中兩個(gè)層次提供了 Device + Driver 的設(shè)備驅(qū)動(dòng)模型,這兩個(gè)層次分別是 Usb Device Layer 和 Usb Interface Layer 層,一個(gè)Usb Device包含一個(gè)或多個(gè)Usb Interface。其中:
-
Usb Device Layer層。這一層的 Device 由 Hub 創(chuàng)建,Hub 本身也是一種 Usb Device;這一層的 Driver 完成的功能非常簡單,基本就是幫 Usb Device 創(chuàng)建其包含的所有子 Usb Interface 的 Device,大部分場景下都是使用 usb_generic_driver。
-
Usb Interface Layer層。這一層的 Device 由上一級 Usb Device 在驅(qū)動(dòng) probe() 時(shí)創(chuàng)建;而這一層的 Driver 就是普通的業(yè)務(wù) Usb 驅(qū)動(dòng),即 Usb 協(xié)議中所說的 Client Software。
2.1 Usb Device Layer
2.1.1 device (struct usb_device)
Usb Device Device 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_device,會在兩種情況下被創(chuàng)建:
1、roothub device。在 HCD 驅(qū)動(dòng)注冊時(shí)創(chuàng)建:
/* (1) 首先創(chuàng)建和初始化 `usb_device` 結(jié)構(gòu):*/usb_add_hcd() → usb_alloc_dev():struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1){/* (1.1) dev 總線初始化為 usb_bus_type */dev->dev.bus = &usb_bus_type;/* (1.2) dev 類型初始化為 usb_device_type,標(biāo)明自己是一個(gè) usb device */dev->dev.type = &usb_device_type;dev->dev.groups = usb_device_groups;}/* (2) 然后注冊 `usb_device` 結(jié)構(gòu):*/usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()
2、普通 usb device。在 Hub 檢測到端口有設(shè)備 attach 時(shí)創(chuàng)建:
/* (1) 首先創(chuàng)建和初始化 `usb_device` 結(jié)構(gòu):*/hub_event() → port_event() → hub_port_connect_change() → hub_port_connect() → usb_alloc_dev()/* (2) 然后注冊 `usb_device` 結(jié)構(gòu):*/hub_event()→port_event()→hub_port_connect_change()→hub_port_connect()→usb_new_device()→device_add()
2.1.2 driver (struct usb_device_driver)
Usb Device Driver 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_device_driver,使用 usb_register_device_driver() 函數(shù)進(jìn)行注冊:
int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner){/* (1) 設(shè)置for_devices標(biāo)志為1,表面這個(gè)驅(qū)動(dòng)時(shí)給 usb device 使用的 */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;new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;retval = driver_register(&new_udriver->drvwrap.driver);}
注冊的 Usb Device Driver 驅(qū)動(dòng)非常少,一般情況下所有的 Usb Device Device 都會適配到 usb_generic_driver。因?yàn)檫@一層次驅(qū)動(dòng)的目的很單純,就是給 Usb Device 下所有的 Interface 創(chuàng)建對應(yīng)的 Usb Interface Device。
usb_init() → usb_register_device_driver() :static int __init usb_init(void){retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);}struct usb_device_driver usb_generic_driver = {.name = "usb",.match = usb_generic_driver_match,.probe = usb_generic_driver_probe,.disconnect = usb_generic_driver_disconnect,.suspend = usb_generic_driver_suspend,.resume = usb_generic_driver_resume,.supports_autosuspend = 1,};
驅(qū)動(dòng) probe() 過程:
usb_probe_device() → usb_generic_driver_probe() → usb_set_configuration():int usb_set_configuration(struct usb_device *dev, int configuration){/* (1) 創(chuàng)建和初始化 `struct usb_interface` */for (i = 0; i < nintf; ++i) {/* (1.1) dev 總線初始化為 usb_bus_type */intf->dev.bus = &usb_bus_type;/* (1.2) dev 類型初始化為 usb_if_device_type,標(biāo)明自己是一個(gè) usb interface */intf->dev.type = &usb_if_device_type;intf->dev.groups = usb_interface_groups;}/* (2) 注冊 `struct usb_interface` */for (i = 0; i < nintf; ++i) {ret = device_add(&intf->dev);}}
2.1.3 bus (usb_bus_type)
可以看到 struct usb_device 和 struct usb_interface 使用的總線都是 usb_bus_type。他們是通過字段 dev.type 來區(qū)分的:
/* (1) `struct usb_device` 的 `dev.type` 值為 `usb_device_type`:*/usb_add_hcd() → usb_alloc_dev():struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1){dev->dev.type = &usb_device_type;}/* (2) `struct usb_interface` 的 `dev.type` 值為 `usb_if_device_type` */usb_probe_device() → usb_generic_driver_probe() → usb_set_configuration():int usb_set_configuration(struct usb_device *dev, int configuration){for (i = 0; i < nintf; ++i) {intf->dev.type = &usb_if_device_type;}}static inline int is_usb_device(const struct device *dev){/* (3) 判斷當(dāng)前 Device 是否為 Usb Device */return dev->type == &usb_device_type;}static inline int is_usb_interface(const struct device *dev){/* (4) 判斷當(dāng)前 Device 是否為 Usb Interface */return dev->type == &usb_if_device_type;}
另外 struct usb_device_driver 和 struct usb_driver 使用的總線都是 usb_bus_type。他們是通過字段 drvwrap.for_devices 來區(qū)分的:
/* (1) `struct usb_device_driver` 的 `drvwrap.for_devices` 值為 1:*/int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner){new_udriver->drvwrap.for_devices = 1;}/* (2) `struct usb_driver` 的 `drvwrap.for_devices` 值為 0:*/int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name){new_driver->drvwrap.for_devices = 0;}/* (3) 判斷當(dāng)前 Driver 是適配 Usb Device 還是 Usb Interface */static inline int is_usb_device_driver(struct device_driver *drv){return container_of(drv, struct usbdrv_wrap, driver)->for_devices;}
在 usb_bus_type 的 match() 函數(shù)中利用 dev.type 進(jìn)行判別分開處理:
struct bus_type usb_bus_type = {.name = "usb",.match = usb_device_match,.uevent = usb_uevent,.need_parent_lock = true,};static int usb_device_match(struct device *dev, struct device_driver *drv){/* devices and interfaces are handled separately *//* (1) Device 是 `Usb Device` 的處理 */if (is_usb_device(dev)) {struct usb_device *udev;struct usb_device_driver *udrv;/* interface drivers never match devices *//* (1.1) 只查找 `Usb Device` 的 Driver */if (!is_usb_device_driver(drv))return 0;udev = to_usb_device(dev);udrv = to_usb_device_driver(drv);/* If the device driver under consideration does not have a* id_table or a match function, then let the driver's probe* function decide.*/if (!udrv->id_table && !udrv->match)return 1;return usb_driver_applicable(udev, udrv);/* (2) Device 是 `Usb Interface` 的處理 */} else if (is_usb_interface(dev)) {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces *//* (2.1) 只查找 `Usb Interface` 的 Driver */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 Interface Layer
2.2.1 device (struct usb_interface)
如上一節(jié)描述,Usb Interface Device 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_interface,會在 Usb Device Driver 驅(qū)動(dòng) probe() 時(shí) 被創(chuàng)建:
usb_probe_device() → usb_generic_driver_probe() → usb_set_configuration():int usb_set_configuration(struct usb_device *dev, int configuration){/* (1) 創(chuàng)建和初始化 `struct usb_interface` */for (i = 0; i < nintf; ++i) {/* (1.1) dev 總線初始化為 usb_bus_type */intf->dev.bus = &usb_bus_type;/* (1.2) dev 類型初始化為 usb_if_device_type,標(biāo)明自己是一個(gè) usb interface */intf->dev.type = &usb_if_device_type;intf->dev.groups = usb_interface_groups;}/* (2) 注冊 `struct usb_interface` */for (i = 0; i < nintf; ++i) {ret = device_add(&intf->dev);}}
2.2.2 driver (struct usb_driver)
Usb Interface 這一層次的驅(qū)動(dòng)就非常的多了,這一層主要是在 USB 傳輸層之上,針對 USB Device 的某個(gè)功能 Function 開發(fā)對應(yīng)的 USB 功能業(yè)務(wù)驅(qū)動(dòng),即常說的 USB Client Software。在 USB 定義中,一個(gè) Interface 就是一個(gè) Function。
Usb Interface Driver 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_driver,使用 usb_register_driver() 函數(shù)進(jìn)行注冊:
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name){/* (1) 設(shè)置for_devices標(biāo)志為0,表面這個(gè)驅(qū)動(dòng)時(shí)給 usb interface 使用的 */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;new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver);}
一個(gè)最簡單的 Usb Interface Driver 是 usb_mouse_driver:
static const struct usb_device_id usb_mouse_id_table[] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },{ } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);static struct usb_driver usb_mouse_driver = {.name = "usbmouse",.probe = usb_mouse_probe,.disconnect = usb_mouse_disconnect,.id_table = usb_mouse_id_table,};module_usb_driver(usb_mouse_driver);
在后面的章節(jié)中會進(jìn)一步詳細(xì)分析這個(gè)驅(qū)動(dòng)的實(shí)現(xiàn)。
2.2.3 bus (usb_bus_type)
Usb Interface 這一層次總線也是 usb_bus_type,上一節(jié)已經(jīng)分析,這里就不重復(fù)解析了。
3. USB Request Block

Usb Core 除了提供上一節(jié)描述的設(shè)備驅(qū)動(dòng)模型以外,另一個(gè)重要的作用就是要給 Usb Interface Driver 提供讀寫 USB 數(shù)據(jù)的 API,這一任務(wù)是圍繞著 USB Request Block 來完成的。
Usb Interface Driver 適配成功以后,會從配置信息中獲取到當(dāng)前 Interface 包含了多少個(gè) Endpoint,以及每個(gè) Endpoint 的地址、傳輸類型、最大包長等其他信息。Endpoint 是 USB 總線傳輸中最小的尋址單位,Interface Driver 利用對幾個(gè) Endpoint 的讀寫來驅(qū)動(dòng)具體的設(shè)備功能。
3.1 urb
對某個(gè) Endpoint 發(fā)起一次讀寫操作,具體工作使用 struct urb 數(shù)據(jù)結(jié)構(gòu)來承擔(dān)。
以下是一個(gè)對 Endpoint 0 使用 urb 發(fā)起讀寫的一個(gè)簡單實(shí)例:
static int usb_internal_control_msg(struct usb_device *usb_dev,unsigned int pipe,struct usb_ctrlrequest *cmd,void *data, int len, int timeout){struct urb *urb;int retv;int length;/* (1) 分配一個(gè) urb 內(nèi)存空間 */urb = usb_alloc_urb(0, GFP_NOIO);if (!urb)return -ENOMEM;/* (2) 填充 urb 內(nèi)容,最核心的有3方面:1、總線地址:Device Num + Endpoint Num2、數(shù)據(jù):data + len3、回調(diào)函數(shù):usb_api_blocking_completion*/usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,len, usb_api_blocking_completion, NULL);/* (3) 發(fā)送 urb 請求,并且等待請求完成 */retv = usb_start_wait_urb(urb, timeout, &length);if (retv < 0)return retv;elsereturn length;}↓static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length){struct api_context ctx;unsigned long expire;int retval;init_completion(&ctx.done);urb->context = &ctx;urb->actual_length = 0;/* (3.1) 把 urb 請求掛載到 hcd 的隊(duì)列當(dāng)中 */retval = usb_submit_urb(urb, GFP_NOIO);if (unlikely(retval))goto out;expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;/* (3.2) 當(dāng) urb 執(zhí)行完成后,首先會調(diào)用 urb 的回調(diào)函數(shù),然后會發(fā)送 completion 信號解除這里的阻塞 */if (!wait_for_completion_timeout(&ctx.done, expire)) {usb_kill_urb(urb);retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);dev_dbg(&urb->dev->dev,"%s timed out on ep%d%s len=%u/%u ",current->comm,usb_endpoint_num(&urb->ep->desc),usb_urb_dir_in(urb) ? "in" : "out",urb->actual_length,urb->transfer_buffer_length);} elseretval = ctx.status;out:if (actual_length)*actual_length = urb->actual_length;usb_free_urb(urb);return retval;}
3.2 normal device urb_enqueue
對普通的 Usb device 來說,urb 最后會提交到 Host Controller 的收發(fā)隊(duì)列上面,由 HC 完成實(shí)際的 USB 傳輸:
usb_submit_urb() → usb_hcd_submit_urb():int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags){/* (1) 如果是 roothub 走特殊的路徑 */if (is_root_hub(urb->dev)) {status = rh_urb_enqueue(hcd, urb);/* (2) 如果是普通 device 調(diào)用對應(yīng)的 hcd 的 urb_enqueue() 函數(shù) */} else {status = map_urb_for_dma(hcd, urb, mem_flags);if (likely(status == 0)) {status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);if (unlikely(status))unmap_urb_for_dma(hcd, urb);}}}
3.3 roothub device urb_enqueue
特別需要注意的是 roothub 它是一個(gè)虛擬的 usb device,實(shí)際上它并不在usb總線上而是在 host 內(nèi)部,所以相應(yīng)的 urb 需要特殊處理,而不能使用 hcd 把數(shù)據(jù)發(fā)送到 Usb 總線上去。
usb_submit_urb() → usb_hcd_submit_urb() → rh_urb_enqueue():static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb){/* (1) 對于 int 類型的數(shù)據(jù),被掛載到 hcd->status_urb 指針上面通常 roothub 驅(qū)動(dòng)用這個(gè) urb 來查詢 roothub 的端口狀態(tài)*/if (usb_endpoint_xfer_int(&urb->ep->desc))return rh_queue_status (hcd, urb);/* (2) 對于 control 類型的數(shù)據(jù),是想讀取 roothub ep0 上的配置信息使用軟件來模擬這類操作的響應(yīng)*/if (usb_endpoint_xfer_control(&urb->ep->desc))return rh_call_control (hcd, urb);return -EINVAL;}|→static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb){/* (1.1) 將 urb 掛載到對應(yīng)的 ep 鏈表中 */retval = usb_hcd_link_urb_to_ep(hcd, urb);if (retval)goto done;/* (1.2) 將 urb 賦值給 hcd->status_urb在 hcd 驅(qū)動(dòng)中,會通過這些接口來通知 roothub 的端口狀態(tài)變化*/hcd->status_urb = urb;urb->hcpriv = hcd; /* indicate it's queued */if (!hcd->uses_new_polling)mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));}|→static int rh_call_control (struct usb_hcd *hcd, struct urb *urb){/* (2.1) 軟件模擬對 roothub 配置讀寫的響應(yīng) */}
4. Usb Hub Driver

普通的 Usb Device 通過內(nèi)部的 Interface 提供各種業(yè)務(wù)功能。而 Hub 這類特殊的 Usb Device 功能就一種,那就是監(jiān)控端口的狀態(tài)變化:
-
在端口上有設(shè)備 attach 時(shí),創(chuàng)建新的 usb device,給其適配驅(qū)動(dòng)。如果是 hub device,子 usb 驅(qū)動(dòng)會進(jìn)一步掃描端口。
-
在端口上有設(shè)備 deattach 時(shí),移除掉對應(yīng)的 usb device。如果是 hub device 進(jìn)一步移除其所有的子 usb device。
Hub 也是標(biāo)準(zhǔn)的 Usb Device,它也是標(biāo)準(zhǔn)的流程被上一級設(shè)備發(fā)現(xiàn)后創(chuàng)建 Usb Device → 創(chuàng)建 Usb Interface,然后被 Usb Hub Interface Driver 給適配到。系統(tǒng)中只有一個(gè) Hub 驅(qū)動(dòng):
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 驅(qū)動(dòng)啟動(dòng)以后,只做一件事情發(fā)送一個(gè)查詢端口狀態(tài)的 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 的端口狀態(tài)urb 的回調(diào)函數(shù)是 hub_irq()*/usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,hub, endpoint->bInterval);/* (3) 發(fā)送 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);}
4.1 normal hub port op
在普通的 hub 中,端口操作是通過標(biāo)準(zhǔn)的 urb 發(fā)起 usb ep0 讀寫。分為兩類:
-
1、通過輪詢讀取 Hub Class-specific Requests 配置來查詢端口的狀態(tài):

-
2、設(shè)置和使能端口也是通過 Hub Class-specific Requests 中相應(yīng)的命令實(shí)現(xiàn)的:

4.2 rootHub port op
而對于 roothub 來說,對端口的操作的 urb 都需要特殊處理 (以 EHCI 的驅(qū)動(dòng)為例):
-
1、端口狀態(tài)的變化可以通過 HCD 觸發(fā)中斷再上報(bào):
ehci_irq() → usb_hcd_poll_rh_status() :void usb_hcd_poll_rh_status(struct usb_hcd *hcd){/* (1) 獲取端口狀態(tài)的變化 */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) 通過回復(fù) hcd->status_urb 來進(jìn)行上報(bào) */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 intehci_hub_status_data (struct usb_hcd *hcd, char *buf){/* (1.1) 通過 HCD 驅(qū)動(dòng),獲取 roothub 端口的狀態(tài) */}
-
2、設(shè)置和使能端口需要嫁接到 HCD 驅(qū)動(dòng)相關(guān)函數(shù)上實(shí)現(xiàn):
→ 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 驅(qū)動(dòng),設(shè)置 roothub 的端口 */}
4.3 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` 數(shù)據(jù)結(jié)構(gòu) */udev = usb_alloc_dev(hdev, hdev->bus, port1);if (!udev) {dev_err(&port_dev->dev,"couldn't allocate usb_device ");goto done;}/* (2) 給新的 Device 分配一個(gè)新的 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) 使能端口,并且調(diào)用 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);}}
4.4 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);}}
5. Usb Host Controller Driver
Usb Host Controller 是主機(jī)側(cè)的硬件實(shí)現(xiàn),主要分為以下種類:
-
Usb1.0 有兩種控制器標(biāo)準(zhǔn):OHCI 康柏的開放主機(jī)控制器接口,UHCI Intel的通用主機(jī)控制器接口。它們的主要區(qū)別是UHCI更加依賴軟件驅(qū)動(dòng),因此對CPU要求更高,但是自身的硬件會更廉價(jià)。
-
Usb2.0 只有一種控制器標(biāo)準(zhǔn):EHCI。因?yàn)?EHCI 只支持高速傳輸,所以EHCI控制器包括四個(gè)虛擬的全速或者慢速控制器。EHCI主要用于usb 2.0,老的Usb1.1用OHCI和UHCI。EHCI為了兼容Usb1.1,將老的OHCI和UHCI合并到EHCI規(guī)范里。
-
Usb3.0 控制器標(biāo)準(zhǔn):XHCI。XHCI是Intel最新開發(fā)的主機(jī)控制器接口,廣泛用戶Intel六代Skylake處理器對應(yīng)的100系列主板上,支持USB3.0接口,往下也兼容USB2.0。XHCI英文全稱eXtensible Host Controller Interface,是一種可擴(kuò)展的主機(jī)控制器接口,是Intel開發(fā)的USB主機(jī)控制器。Intel 系列芯片的USB協(xié)議采用的就是XHCI主控,主要面向USB 3.0標(biāo)準(zhǔn)的,同時(shí)也兼容2.0以下的設(shè)備。
我們以應(yīng)用最廣泛的 EHCI 為例,分析其軟硬件實(shí)現(xiàn)的架構(gòu)。
5.1 ehci hardware
5.1.1 compatible usb1.0
對 EHCI 來說,它向下兼容的方案是非常有特點(diǎn)的。因?yàn)?EHCI 只支持 Usb2.0 高速傳輸,為了向下兼容 Usb1.1,它直接在內(nèi)部集成最多4個(gè)全速或者慢速控制器 OHCI。在 EHCI 協(xié)議內(nèi)稱這種伴生的 OHCI 控制器為 companion host controllers。

由 EHCI 驅(qū)動(dòng)根據(jù)端口速率情況來決定由誰來處理:
-
每個(gè)端口有一個(gè) Owner 屬性,用來決定是 EHCI 管理還是 OHCI 管理。就是一個(gè) Switch 開關(guān),決定 USB 數(shù)據(jù)切到哪邊處理。
-
初始狀態(tài)時(shí)端口默認(rèn)屬于 OHCI 管理。所以對于硬件上從 OHCI 升級到 EHCI,而軟件上只有 OHCI 驅(qū)動(dòng)而沒有 EHCI 驅(qū)動(dòng)的系統(tǒng)來說是透明的,它繼續(xù)把 EHCI 當(dāng)成 OHCI 硬件來使用就行了,保持完美的向前兼容。
-
如果系統(tǒng)軟件上啟用了 EHCI 驅(qū)動(dòng),它首先會把所有端口的Owner配置成 EHCI 管理。如果 EHCI 驅(qū)動(dòng)發(fā)現(xiàn)端口連接且速率是全速或者慢速,則把端口的Owner配置成 OHCI 管理。
對于 EHCI 這種包含兩種控制器的兼容方式,軟件上需要同時(shí)啟動(dòng) EHCI Driver 和 OHCI Driver,才能完整的兼容 Usb1.0 和 Usb2.0:

5.1.2 Periodic Schedule

EHCI 把數(shù)據(jù)傳輸分成了兩類來進(jìn)行調(diào)度:
-
Periodic Schedule。用來傳輸對時(shí)間延遲要求高的 Endpoint 數(shù)據(jù),包括 Isochronous Transfer 和 Interrupt Transfer。
-
Asynchronous Schedule。用來傳輸對時(shí)間延遲要求不高的 Endpoint 數(shù)據(jù),包括 Control Transfer 和 Bulk Transfer。

Periodic Schedule 內(nèi)部實(shí)現(xiàn)如上圖所示,核心是兩級鏈表:
-
1、第一級鏈表如上圖綠色所示。是各種傳輸結(jié)構(gòu)的實(shí)際描述符,主要包含以下幾種類型的描述符:

-
2、第二級鏈表如上圖橙色所示。是一個(gè)指針數(shù)組,數(shù)組中保存的是指向第一級鏈表的指針。這里每個(gè)數(shù)組成員代表一個(gè)時(shí)間分片 Frame/Micro-Frame 的起始位置,每個(gè)時(shí)間片會根據(jù)指針傳輸?shù)谝患夋湵碇械臄?shù)據(jù),直到第一級鏈表的結(jié)尾。指針的格式如下:

這里的調(diào)度思想就是:第一級鏈表是一個(gè)傳輸數(shù)據(jù)全集,第二級鏈表決定了某個(gè)時(shí)間片里要傳輸?shù)臄?shù)據(jù)。這樣合理的安排二級鏈表的指針,比如間隔8次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是8,間隔4次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是4。 第一級鏈表也是要根據(jù)interval排序的。
Periodic Schedule 中幾個(gè)核心的描述符如下:
1、Isochronous (High-Speed) Transfer Descriptor (iTD)

2、Queue Head

2.1、Queue Element Transfer Descriptor (qTD)

5.1.3 Asynchronous Schedule

Asynchronous Schedule 內(nèi)部實(shí)現(xiàn)非常的簡單就只有一級鏈表,鏈表中只有Queue Head類型的描述符。每個(gè)時(shí)間片內(nèi)傳輸完 Period 數(shù)據(jù)以后,再盡可能的傳輸 Asynchronous 數(shù)據(jù)即可。
5.2 ehci driver
ehci driver 負(fù)責(zé)把 echi 功能封裝成標(biāo)準(zhǔn)的 hcd 驅(qū)動(dòng)。它主要完成兩項(xiàng)工作:
-
1、注冊標(biāo)準(zhǔn)的 hcd 驅(qū)動(dòng)。把 Client Software 傳說下來的 urb 映射到 EHCI 的鏈表中進(jìn)行傳輸。
-
2、創(chuàng)建一個(gè)虛擬的根 hub 設(shè)備,即 roothub。
5.2.1 urb transfer
ehci 注冊 hcd 驅(qū)動(dòng):
static int ehci_platform_probe(struct platform_device *dev){/* (1) 分配 hcd,并且把 hcd->driver 初始化成 ehci_hc_driver */ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,dev_name(&dev->dev));/* (2) 注冊標(biāo)準(zhǔn)的 hcd 驅(qū)動(dòng) */err = usb_add_hcd(hcd, irq, IRQF_SHARED);}
hcd 驅(qū)動(dòng)向上提供了標(biāo)準(zhǔn)接口,最終的實(shí)現(xiàn)會調(diào)用到 ehci_hc_driver 當(dāng)中。
static const struct hc_driver ehci_hc_driver = {.description = hcd_name,.product_desc = "EHCI Host Controller",.hcd_priv_size = sizeof(struct ehci_hcd),/** generic hardware linkage*/.irq = ehci_irq,.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,/** basic lifecycle operations*/.reset = ehci_setup,.start = ehci_run,.stop = ehci_stop,.shutdown = ehci_shutdown,/** managing i/o requests and associated device resources*/.urb_enqueue = ehci_urb_enqueue,.urb_dequeue = ehci_urb_dequeue,.endpoint_disable = ehci_endpoint_disable,.endpoint_reset = ehci_endpoint_reset,.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,/** scheduling support*/.get_frame_number = ehci_get_frame,/** root hub support*/.hub_status_data = ehci_hub_status_data,.hub_control = ehci_hub_control,.bus_suspend = ehci_bus_suspend,.bus_resume = ehci_bus_resume,.relinquish_port = ehci_relinquish_port,.port_handed_over = ehci_port_handed_over,.get_resuming_ports = ehci_get_resuming_ports,/** device support*/.free_dev = ehci_remove_device,};
在 urb transfer 過程中,最核心的是調(diào)用上述的 ehci_urb_enqueue() 和 ehci_urb_dequeue() 函數(shù)。
5.2.2 roothub
首先創(chuàng)建虛擬的 roothub:
/* (1) 首先創(chuàng)建和初始化 `usb_device` 結(jié)構(gòu): */ehci_platform_probe() → usb_add_hcd() → usb_alloc_dev():struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1){/* (1.1) dev 總線初始化為 usb_bus_type */dev->dev.bus = &usb_bus_type;/* (1.2) dev 類型初始化為 usb_device_type,標(biāo)明自己是一個(gè) usb device */dev->dev.type = &usb_device_type;dev->dev.groups = usb_device_groups;}/* (2) 然后注冊 `usb_device` 結(jié)構(gòu): */usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()
然后因?yàn)?roothub 并不是在 Usb 物理總線上,所以對它的查詢和配置需要特殊處理。詳見Usb Hub Driver這一節(jié)。
6. Usb Client Software
這里再詳細(xì)分析一下典型的 Usb Client Software 即 usb mouse 驅(qū)動(dòng),看看它是怎么利用 urb 讀取 usb 設(shè)備數(shù)據(jù)的。
static const struct usb_device_id usb_mouse_id_table[] = {/* (1) 驅(qū)動(dòng)可以適配的 interface 列表 */{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },{ } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);static struct usb_driver usb_mouse_driver = {.name = "usbmouse",.probe = usb_mouse_probe,.disconnect = usb_mouse_disconnect,.id_table = usb_mouse_id_table,};module_usb_driver(usb_mouse_driver);
1、首先根據(jù)得到的 endpoint 準(zhǔn)備好 urb,創(chuàng)建好 input 設(shè)備:
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id){struct usb_device *dev = interface_to_usbdev(intf);struct usb_host_interface *interface;struct usb_endpoint_descriptor *endpoint;struct usb_mouse *mouse;struct input_dev *input_dev;int pipe, maxp;int error = -ENOMEM;interface = intf->cur_altsetting;if (interface->desc.bNumEndpoints != 1)return -ENODEV;/* (1) 得到當(dāng)前 interface 中的第一個(gè) endpoint,mouse設(shè)備只需一個(gè) endpoint */endpoint = &interface->endpoint[0].desc;if (!usb_endpoint_is_int_in(endpoint))return -ENODEV;pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);/* (2.1) 分配 input device */input_dev = input_allocate_device();if (!mouse || !input_dev)goto fail1;mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);if (!mouse->data)goto fail1;/* (3.1) 分配 urb */mouse->irq = usb_alloc_urb(0, GFP_KERNEL);if (!mouse->irq)goto fail2;mouse->usbdev = dev;mouse->dev = input_dev;if (dev->manufacturer)strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));if (dev->product) {if (dev->manufacturer)strlcat(mouse->name, " ", sizeof(mouse->name));strlcat(mouse->name, dev->product, sizeof(mouse->name));}if (!strlen(mouse->name))snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));usb_make_path(dev, mouse->phys, sizeof(mouse->phys));strlcat(mouse->phys, "/input0", sizeof(mouse->phys));/* (2.2) 初始化 input device */input_dev->name = mouse->name;input_dev->phys = mouse->phys;usb_to_input_id(dev, &input_dev->id);input_dev->dev.parent = &intf->dev;input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |BIT_MASK(BTN_EXTRA);input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);input_set_drvdata(input_dev, mouse);input_dev->open = usb_mouse_open;input_dev->close = usb_mouse_close;/* (3.2) 初始化 urb */usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);mouse->irq->transfer_dma = mouse->data_dma;mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* (2.3) 注冊 input device */error = input_register_device(mouse->dev);if (error)goto fail3;usb_set_intfdata(intf, mouse);return 0;fail3:usb_free_urb(mouse->irq);fail2:usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);fail1:input_free_device(input_dev);kfree(mouse);return error;}
2、在 input device 被 open 時(shí)提交 urb 啟動(dòng)傳輸:
static int usb_mouse_open(struct input_dev *dev){struct usb_mouse *mouse = input_get_drvdata(dev);mouse->irq->dev = mouse->usbdev;/* (1) 提交初始化好的 usb,開始查詢數(shù)據(jù) */if (usb_submit_urb(mouse->irq, GFP_KERNEL))return -EIO;return 0;}
3、在傳輸完 urb 的回調(diào)函數(shù)中,根據(jù)讀回的數(shù)據(jù)上報(bào) input 事件,并且重新提交 urb 繼續(xù)查詢:
static void usb_mouse_irq(struct urb *urb){struct usb_mouse *mouse = urb->context;signed char *data = mouse->data;struct input_dev *dev = mouse->dev;int status;switch (urb->status) {case 0: /* success */break;case -ECONNRESET: /* unlink */case -ENOENT:case -ESHUTDOWN:return;/* -EPIPE: should clear the halt */default: /* error */goto resubmit;}/* (1) 根據(jù) urb 讀回的數(shù)據(jù),上報(bào) input event */input_report_key(dev, BTN_LEFT, data[0] & 0x01);input_report_key(dev, BTN_RIGHT, data[0] & 0x02);input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);input_report_key(dev, BTN_SIDE, data[0] & 0x08);input_report_key(dev, BTN_EXTRA, data[0] & 0x10);input_report_rel(dev, REL_X, data[1]);input_report_rel(dev, REL_Y, data[2]);input_report_rel(dev, REL_WHEEL, data[3]);input_sync(dev);resubmit:/* (2) 重新提交 urb 繼續(xù)查詢 */status = usb_submit_urb (urb, GFP_ATOMIC);if (status)dev_err(&mouse->usbdev->dev,"can't resubmit intr, %s-%s/input0, status %d ",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);}
參考資料
1.Enhanced Host Controller Interface Specification
2.USB 2.0 Specification
審核編輯 :李倩
-
usb
+關(guān)注
關(guān)注
60文章
8441瀏覽量
284684 -
Host
+關(guān)注
關(guān)注
0文章
32瀏覽量
35527
原文標(biāo)題:Linux usb Host 詳解
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
德州儀器TPSM843B22E :高效開關(guān)電源模塊的詳細(xì)解析
2025年國產(chǎn)音頻傳輸模塊產(chǎn)品選購全攻略與技術(shù)方案解析
Windows平臺EtherCAT實(shí)時(shí)控制:從抖動(dòng)抑制到虛擬化架構(gòu)解析
ADL5601射頻增益模塊的特性與應(yīng)用解析
DA14695MOD:高效藍(lán)牙5.2模塊的全面解析
固態(tài)變壓器SST的拓?fù)?b class='flag-5'>架構(gòu)深度解析與基本半導(dǎo)體SiC模塊的工程應(yīng)用研究
解析液晶模塊的技術(shù)架構(gòu)和關(guān)鍵作用
解析 onsemi SiC 功率MOSFET模塊NVXK2PR80WXT2
使用nRF52840芯片的USB Host 功能參考例程
GPU架構(gòu)深度解析
詳細(xì)解析Host各模塊的架構(gòu)和原理
評論