1.簡(jiǎn)介
整個(gè) USB 系統(tǒng)的通訊模型如上圖所示,本文詳細(xì)解析其中 Host 各模塊的架構(gòu)和原理 (圖中彩色部分)。
2. Usb Core 驅(qū)動(dòng)設(shè)備模型
由前幾節(jié)可知USB將Device進(jìn)一步細(xì)分成了3個(gè)層級(jí):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 完成的功能非常簡(jiǎn)單,基本就是幫 Usb Device 創(chuàng)建其包含的所有子 Usb Interface 的 Device,大部分場(chǎng)景下都是使用 usb_generic_driver。
-
Usb Interface Layer層。這一層的 Device 由上一級(jí) 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 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_device,會(huì)在兩種情況下被創(chuàng)建:
1、roothub device。在 HCD 驅(qū)動(dòng)注冊(cè)時(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) 然后注冊(cè) `usb_device` 結(jié)構(gòu):*/
usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()
2、普通 usb device。在 Hub 檢測(cè)到端口有設(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) 然后注冊(cè) `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 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_device_driver,使用 usb_register_device_driver() 函數(shù)進(jìn)行注冊(cè):
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);
}
注冊(cè)的 Usb Device Driver 驅(qū)動(dòng)非常少,一般情況下所有的 Usb Device Device 都會(huì)適配到 usb_generic_driver。因?yàn)檫@一層次驅(qū)動(dòng)的目的很單純,就是給 Usb Device 下所有的 Interface 創(chuàng)建對(duì)應(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) 注冊(cè) `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 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_interface,會(huì)在 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) 注冊(cè) `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 傳輸層之上,針對(duì) USB Device 的某個(gè)功能 Function 開發(fā)對(duì)應(yīng)的 USB 功能業(yè)務(wù)驅(qū)動(dòng),即常說的 USB Client Software。在 USB 定義中,一個(gè) Interface 就是一個(gè) Function。
Usb Interface Driver 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 struct usb_driver,使用 usb_register_driver() 函數(shù)進(jìn)行注冊(cè):
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è)最簡(jiǎn)單的 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é)中會(huì)進(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 適配成功以后,會(huì)從配置信息中獲取到當(dāng)前 Interface 包含了多少個(gè) Endpoint,以及每個(gè) Endpoint 的地址、傳輸類型、最大包長(zhǎng)等其他信息。Endpoint 是 USB 總線傳輸中最小的尋址單位,Interface Driver 利用對(duì)幾個(gè) Endpoint 的讀寫來驅(qū)動(dòng)具體的設(shè)備功能。
3.1 urb
對(duì)某個(gè) Endpoint 發(fā)起一次讀寫操作,具體工作使用 struct urb 數(shù)據(jù)結(jié)構(gòu)來承擔(dān)。
以下是一個(gè)對(duì) Endpoint 0 使用 urb 發(fā)起讀寫的一個(gè)簡(jiǎn)單實(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 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 請(qǐng)求,并且等待請(qǐng)求完成 */
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 請(qǐng)求掛載到 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í)行完成后,首先會(huì)調(diào)用 urb 的回調(diào)函數(shù),然后會(huì)發(fā)送 completion 信號(hào)解除這里的阻塞 */
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
對(duì)普通的 Usb device 來說,urb 最后會(huì)提交到 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)用對(duì)應(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) 對(duì)于 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) 對(duì)于 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 掛載到對(duì)應(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)中,會(huì)通過這些接口來通知 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) 軟件模擬對(duì) 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)會(huì)進(jìn)一步掃描端口。
-
在端口上有設(shè)備 deattach 時(shí),移除掉對(duì)應(yīng)的 usb device。如果是 hub device 進(jìn)一步移除其所有的子 usb device。
Hub 也是標(biāo)準(zhǔn)的 Usb Device,它也是標(biāo)準(zhǔn)的流程被上一級(jí)設(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
而對(duì)于 roothub 來說,對(duì)端口的操作的 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 int
ehci_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) 注冊(cè) `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),因此對(duì)CPU要求更高,但是自身的硬件會(huì)更廉價(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處理器對(duì)應(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
對(duì) 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 管理。所以對(duì)于硬件上從 OHCI 升級(jí)到 EHCI,而軟件上只有 OHCI 驅(qū)動(dòng)而沒有 EHCI 驅(qū)動(dòng)的系統(tǒng)來說是透明的,它繼續(xù)把 EHCI 當(dāng)成 OHCI 硬件來使用就行了,保持完美的向前兼容。
-
如果系統(tǒng)軟件上啟用了 EHCI 驅(qū)動(dòng),它首先會(huì)把所有端口的Owner配置成 EHCI 管理。如果 EHCI 驅(qū)動(dòng)發(fā)現(xiàn)端口連接且速率是全速或者慢速,則把端口的Owner配置成 OHCI 管理。
對(duì)于 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。用來傳輸對(duì)時(shí)間延遲要求高的 Endpoint 數(shù)據(jù),包括 Isochronous Transfer 和 Interrupt Transfer。
-
Asynchronous Schedule。用來傳輸對(duì)時(shí)間延遲要求不高的 Endpoint 數(shù)據(jù),包括 Control Transfer 和 Bulk Transfer。
Periodic Schedule 內(nèi)部實(shí)現(xiàn)如上圖所示,核心是兩級(jí)鏈表:
-
1、第一級(jí)鏈表如上圖綠色所示。是各種傳輸結(jié)構(gòu)的實(shí)際描述符,主要包含以下幾種類型的描述符:
-
2、第二級(jí)鏈表如上圖橙色所示。是一個(gè)指針數(shù)組,數(shù)組中保存的是指向第一級(jí)鏈表的指針。這里每個(gè)數(shù)組成員代表一個(gè)時(shí)間分片 Frame/Micro-Frame 的起始位置,每個(gè)時(shí)間片會(huì)根據(jù)指針傳輸?shù)谝患?jí)鏈表中的數(shù)據(jù),直到第一級(jí)鏈表的結(jié)尾。指針的格式如下:
這里的調(diào)度思想就是:第一級(jí)鏈表是一個(gè)傳輸數(shù)據(jù)全集,第二級(jí)鏈表決定了某個(gè)時(shí)間片里要傳輸?shù)臄?shù)據(jù)。這樣合理的安排二級(jí)鏈表的指針,比如間隔8次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是8,間隔4次指向同一位置這部分?jǐn)?shù)據(jù)的interval就是4。 第一級(jí)鏈表也是要根據(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)非常的簡(jiǎn)單就只有一級(jí)鏈表,鏈表中只有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、注冊(cè)標(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 注冊(cè) 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) 注冊(cè)標(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)會(huì)調(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) 然后注冊(cè) `usb_device` 結(jié)構(gòu): */
usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()
然后因?yàn)?roothub 并不是在 Usb 物理總線上,所以對(duì)它的查詢和配置需要特殊處理。詳見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) 注冊(cè) 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文章
7978瀏覽量
265530 -
Host
+關(guān)注
關(guān)注
0文章
32瀏覽量
34653
原文標(biāo)題:Linux usb Host 詳解
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論