1.簡介
整個 USB 系統(tǒng)的通訊模型如上圖所示,本文詳細(xì)解析其中 Host 各模塊的架構(gòu)和原理 (圖中彩色部分)。
2. Usb Core 驅(qū)動設(shè)備模型
由前幾節(jié)可知USB將Device進一步細(xì)分成了3個層級:Configuration 配置、Interface 接口、Endpoint 端點。
Usb Core 為其中兩個層次提供了 Device + Driver 的設(shè)備驅(qū)動模型,這兩個層次分別是 Usb Device Layer 和 Usb Interface Layer 層,一個Usb Device包含一個或多個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ū)動 probe() 時創(chuàng)建;而這一層的 Driver 就是普通的業(yè)務(wù) Usb 驅(qū)動,即 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ū)動注冊時創(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)明自己是一個 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 時創(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ù)進行注冊:
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
{
/* (1) 設(shè)置for_devices標(biāo)志為1,表面這個驅(qū)動時給 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ū)動非常少,一般情況下所有的 Usb Device Device 都會適配到 usb_generic_driver。因為這一層次驅(qū)動的目的很單純,就是給 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ū)動 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)明自己是一個 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 進行判別分開處理:
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ū)動 probe() 時 被創(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)明自己是一個 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ū)動就非常的多了,這一層主要是在 USB 傳輸層之上,針對 USB Device 的某個功能 Function 開發(fā)對應(yīng)的 USB 功能業(yè)務(wù)驅(qū)動,即常說的 USB Client Software。在 USB 定義中,一個 Interface 就是一個 Function。
Usb Interface Driver 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_driver,使用 usb_register_driver() 函數(shù)進行注冊:
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
/* (1) 設(shè)置for_devices標(biāo)志為0,表面這個驅(qū)動時給 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);
}
一個最簡單的 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é)中會進一步詳細(xì)分析這個驅(qū)動的實現(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ū)動模型以外,另一個重要的作用就是要給 Usb Interface Driver 提供讀寫 USB 數(shù)據(jù)的 API,這一任務(wù)是圍繞著 USB Request Block 來完成的。
Usb Interface Driver 適配成功以后,會從配置信息中獲取到當(dāng)前 Interface 包含了多少個 Endpoint,以及每個 Endpoint 的地址、傳輸類型、最大包長等其他信息。Endpoint 是 USB 總線傳輸中最小的尋址單位,Interface Driver 利用對幾個 Endpoint 的讀寫來驅(qū)動具體的設(shè)備功能。
3.1 urb
對某個 Endpoint 發(fā)起一次讀寫操作,具體工作使用 struct urb 數(shù)據(jù)結(jié)構(gòu)來承擔(dān)。
以下是一個對 Endpoint 0 使用 urb 發(fā)起讀寫的一個簡單實例:
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) 分配一個 urb 內(nèi)存空間 */
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
return -ENOMEM;
/* (2) 填充 urb 內(nèi)容,最核心的有3方面:
1、總線地址:Device Num + Endpoint Num
2、數(shù)據(jù):data + len
3、回調(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;
else
return 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 的隊列當(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);
} else
retval = 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ā)隊列上面,由 HC 完成實際的 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 它是一個虛擬的 usb device,實際上它并不在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ū)動用這個 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ū)動中,會通過這些接口來通知 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 時,創(chuàng)建新的 usb device,給其適配驅(qū)動。如果是 hub device,子 usb 驅(qū)動會進一步掃描端口。
-
在端口上有設(shè)備 deattach 時,移除掉對應(yīng)的 usb device。如果是 hub device 進一步移除其所有的子 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)中只有一個 Hub 驅(qū)動:
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ū)動啟動以后,只做一件事情發(fā)送一個查詢端口狀態(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)的命令實現(xiàn)的:
4.2 rootHub port op
而對于 roothub 來說,對端口的操作的 urb 都需要特殊處理 (以 EHCI 的驅(qū)動為例):
-
1、端口狀態(tài)的變化可以通過 HCD 觸發(fā)中斷再上報:
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 來進行上報 */
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 驅(qū)動,獲取 roothub 端口的狀態(tài) */
}
-
2、設(shè)置和使能端口需要嫁接到 HCD 驅(qū)動相關(guān)函數(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ū)動,設(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 分配一個新的 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 是主機側(cè)的硬件實現(xiàn),主要分為以下種類:
-
Usb1.0 有兩種控制器標(biāo)準(zhǔn):OHCI 康柏的開放主機控制器接口,UHCI Intel的通用主機控制器接口。它們的主要區(qū)別是UHCI更加依賴軟件驅(qū)動,因此對CPU要求更高,但是自身的硬件會更廉價。
-
Usb2.0 只有一種控制器標(biāo)準(zhǔn):EHCI。因為 EHCI 只支持高速傳輸,所以EHCI控制器包括四個虛擬的全速或者慢速控制器。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ā)的主機控制器接口,廣泛用戶Intel六代Skylake處理器對應(yīng)的100系列主板上,支持USB3.0接口,往下也兼容USB2.0。XHCI英文全稱eXtensible Host Controller Interface,是一種可擴展的主機控制器接口,是Intel開發(fā)的USB主機控制器。Intel 系列芯片的USB協(xié)議采用的就是XHCI主控,主要面向USB 3.0標(biāo)準(zhǔn)的,同時也兼容2.0以下的設(shè)備。
我們以應(yīng)用最廣泛的 EHCI 為例,分析其軟硬件實現(xiàn)的架構(gòu)。
5.1 ehci hardware
5.1.1 compatible usb1.0
對 EHCI 來說,它向下兼容的方案是非常有特點的。因為 EHCI 只支持 Usb2.0 高速傳輸,為了向下兼容 Usb1.1,它直接在內(nèi)部集成最多4個全速或者慢速控制器 OHCI。在 EHCI 協(xié)議內(nèi)稱這種伴生的 OHCI 控制器為 companion host controllers。
由 EHCI 驅(qū)動根據(jù)端口速率情況來決定由誰來處理:
-
每個端口有一個 Owner 屬性,用來決定是 EHCI 管理還是 OHCI 管理。就是一個 Switch 開關(guān),決定 USB 數(shù)據(jù)切到哪邊處理。
-
初始狀態(tài)時端口默認(rèn)屬于 OHCI 管理。所以對于硬件上從 OHCI 升級到 EHCI,而軟件上只有 OHCI 驅(qū)動而沒有 EHCI 驅(qū)動的系統(tǒng)來說是透明的,它繼續(xù)把 EHCI 當(dāng)成 OHCI 硬件來使用就行了,保持完美的向前兼容。
-
如果系統(tǒng)軟件上啟用了 EHCI 驅(qū)動,它首先會把所有端口的Owner配置成 EHCI 管理。如果 EHCI 驅(qū)動發(fā)現(xiàn)端口連接且速率是全速或者慢速,則把端口的Owner配置成 OHCI 管理。
對于 EHCI 這種包含兩種控制器的兼容方式,軟件上需要同時啟動 EHCI Driver 和 OHCI Driver,才能完整的兼容 Usb1.0 和 Usb2.0:
5.1.2 Periodic Schedule
EHCI 把數(shù)據(jù)傳輸分成了兩類來進行調(diào)度:
-
Periodic Schedule。用來傳輸對時間延遲要求高的 Endpoint 數(shù)據(jù),包括 Isochronous Transfer 和 Interrupt Transfer。
-
Asynchronous Schedule。用來傳輸對時間延遲要求不高的 Endpoint 數(shù)據(jù),包括 Control Transfer 和 Bulk Transfer。
Periodic Schedule 內(nèi)部實現(xiàn)如上圖所示,核心是兩級鏈表:
-
1、第一級鏈表如上圖綠色所示。是各種傳輸結(jié)構(gòu)的實際描述符,主要包含以下幾種類型的描述符:
-
2、第二級鏈表如上圖橙色所示。是一個指針數(shù)組,數(shù)組中保存的是指向第一級鏈表的指針。這里每個數(shù)組成員代表一個時間分片 Frame/Micro-Frame 的起始位置,每個時間片會根據(jù)指針傳輸?shù)谝患夋湵碇械臄?shù)據(jù),直到第一級鏈表的結(jié)尾。指針的格式如下:
這里的調(diào)度思想就是:第一級鏈表是一個傳輸數(shù)據(jù)全集,第二級鏈表決定了某個時間片里要傳輸?shù)臄?shù)據(jù)。這樣合理的安排二級鏈表的指針,比如間隔8次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是8,間隔4次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是4。 第一級鏈表也是要根據(jù)interval排序的。
Periodic Schedule 中幾個核心的描述符如下:
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)部實現(xiàn)非常的簡單就只有一級鏈表,鏈表中只有Queue Head類型的描述符。每個時間片內(nèi)傳輸完 Period 數(shù)據(jù)以后,再盡可能的傳輸 Asynchronous 數(shù)據(jù)即可。
5.2 ehci driver
ehci driver 負(fù)責(zé)把 echi 功能封裝成標(biāo)準(zhǔn)的 hcd 驅(qū)動。它主要完成兩項工作:
-
1、注冊標(biāo)準(zhǔn)的 hcd 驅(qū)動。把 Client Software 傳說下來的 urb 映射到 EHCI 的鏈表中進行傳輸。
-
2、創(chuàng)建一個虛擬的根 hub 設(shè)備,即 roothub。
5.2.1 urb transfer
ehci 注冊 hcd 驅(qū)動:
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ū)動 */
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
}
hcd 驅(qū)動向上提供了標(biāo)準(zhǔn)接口,最終的實現(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)明自己是一個 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()
然后因為 roothub 并不是在 Usb 物理總線上,所以對它的查詢和配置需要特殊處理。詳見Usb Hub Driver這一節(jié)。
6. Usb Client Software
這里再詳細(xì)分析一下典型的 Usb Client Software 即 usb mouse 驅(qū)動,看看它是怎么利用 urb 讀取 usb 設(shè)備數(shù)據(jù)的。
static const struct usb_device_id usb_mouse_id_table[] = {
/* (1) 驅(qū)動可以適配的 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 中的第一個 endpoint,mouse設(shè)備只需一個 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 時提交 urb 啟動傳輸:
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ù)上報 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ù),上報 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文章
8190瀏覽量
272916 -
Host
+關(guān)注
關(guān)注
0文章
32瀏覽量
35111
原文標(biāo)題:Linux usb Host 詳解
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
GPU架構(gòu)深度解析

NVMe控制器之完成信息解析模塊
解鎖未來汽車電子技術(shù):軟件定義車輛與區(qū)域架構(gòu)深度解析
NVIDIA Blackwell數(shù)據(jù)手冊與NVIDIA Blackwell架構(gòu)技術(shù)解析
博世GTM IP模塊架構(gòu)介紹

博世GTM IP模塊的核心功能及架構(gòu)解析

國外物理服務(wù)器詳細(xì)解析
硅谷云平臺詳細(xì)解析
SiC模塊封裝技術(shù)解析

AUTOSAR通信協(xié)議解析 如何實現(xiàn)AUTOSAR通信
GPU云服務(wù)器架構(gòu)解析及應(yīng)用優(yōu)勢
SSD架構(gòu)與功能模塊詳解

評論