在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

詳細解析Host各模塊的架構和原理

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:Linux閱碼場 ? 2022-06-09 09:57 ? 次閱讀

1.簡介

整個 USB 系統的通訊模型如上圖所示,本文詳細解析其中 Host 各模塊的架構和原理 (圖中彩色部分)。

8021c06a-e78a-11ec-ba43-dac502259ad0.png

2. Usb Core 驅動設備模型

80683874-e78a-11ec-ba43-dac502259ad0.png

由前幾節可知USB將Device進一步細分成了3個層級:Configuration 配置、Interface 接口、Endpoint 端點。

Usb Core 為其中兩個層次提供了 Device + Driver 的設備驅動模型,這兩個層次分別是 Usb Device Layer 和 Usb Interface Layer 層,一個Usb Device包含一個或多個Usb Interface。其中:

  • Usb Device Layer層。這一層的 Device 由 Hub 創建,Hub 本身也是一種 Usb Device;這一層的 Driver 完成的功能非常簡單,基本就是幫 Usb Device 創建其包含的所有子 Usb Interface 的 Device,大部分場景下都是使用 usb_generic_driver。

  • Usb Interface Layer層。這一層的 Device 由上一級 Usb Device 在驅動 probe() 時創建;而這一層的 Driver 就是普通的業務 Usb 驅動,即 Usb 協議中所說的 Client Software。

2.1 Usb Device Layer

2.1.1 device (struct usb_device)

Usb Device Device 對應的數據結構為 struct usb_device,會在兩種情況下被創建:

1、roothub device。在 HCD 驅動注冊時創建:


/* (1) 首先創建和初始化 `usb_device` 結構:*/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,標明自己是一個 usb device */  dev->dev.type = &usb_device_type;  dev->dev.groups = usb_device_groups;
}
/* (2) 然后注冊  `usb_device` 結構:*/usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()

2、普通 usb device。在 Hub 檢測端口有設備 attach 時創建:


/* (1) 首先創建和初始化 `usb_device` 結構:*/hub_event() → port_event() → hub_port_connect_change() → hub_port_connect() → usb_alloc_dev()
/* (2) 然后注冊  `usb_device` 結構:*/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 對應的數據結構為 struct usb_device_driver,使用 usb_register_device_driver() 函數進行注冊:


int usb_register_device_driver(struct usb_device_driver *new_udriver,    struct module *owner){
  /* (1) 設置for_devices標志為1,表面這個驅動時給 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 驅動非常少,一般情況下所有的 Usb Device Device 都會適配到 usb_generic_driver。因為這一層次驅動的目的很單純,就是給 Usb Device 下所有的 Interface 創建對應的 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,#ifdef  CONFIG_PM  .suspend = usb_generic_driver_suspend,  .resume = usb_generic_driver_resume,#endif  .supports_autosuspend = 1,};

驅動 probe() 過程:


usb_probe_device() → usb_generic_driver_probe() → usb_set_configuration():
int usb_set_configuration(struct usb_device *dev, int configuration){
  /* (1) 創建和初始化 `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,標明自己是一個 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 來區分的:


/* (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) 判斷當前 Device 是否為 Usb Device */  return dev->type == &usb_device_type;}
static inline int is_usb_interface(const struct device *dev){  /* (4) 判斷當前 Device 是否為 Usb Interface */  return dev->type == &usb_if_device_type;}

另外 struct usb_device_driver 和 struct usb_driver 使用的總線都是 usb_bus_type。他們是通過字段 drvwrap.for_devices 來區分的:


/* (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) 判斷當前 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() 函數中利用 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)

如上一節描述,Usb Interface Device 對應的數據結構為 struct usb_interface,會在 Usb Device Driver 驅動 probe() 時 被創建:


usb_probe_device() → usb_generic_driver_probe() → usb_set_configuration():
int usb_set_configuration(struct usb_device *dev, int configuration){
  /* (1) 創建和初始化 `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,標明自己是一個 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 這一層次的驅動就非常的多了,這一層主要是在 USB 傳輸層之上,針對 USB Device 的某個功能 Function 開發對應的 USB 功能業務驅動,即常說的 USB Client Software。在 USB 定義中,一個 Interface 就是一個 Function。

Usb Interface Driver 對應的數據結構為 struct usb_driver,使用 usb_register_driver() 函數進行注冊:


int usb_register_driver(struct usb_driver *new_driver, struct module *owner,      const char *mod_name){
  /* (1) 設置for_devices標志為0,表面這個驅動時給 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);

在后面的章節中會進一步詳細分析這個驅動的實現。

2.2.3 bus (usb_bus_type)

Usb Interface 這一層次總線也是 usb_bus_type,上一節已經分析,這里就不重復解析了。

3. USB Request Block

80a4dcfc-e78a-11ec-ba43-dac502259ad0.png

Usb Core 除了提供上一節描述的設備驅動模型以外,另一個重要的作用就是要給 Usb Interface Driver 提供讀寫 USB 數據的 API,這一任務是圍繞著 USB Request Block 來完成的。

Usb Interface Driver 適配成功以后,會從配置信息中獲取到當前 Interface 包含了多少個 Endpoint,以及每個 Endpoint 的地址、傳輸類型、最大包長等其他信息。Endpoint 是 USB 總線傳輸中最小的尋址單位,Interface Driver 利用對幾個 Endpoint 的讀寫來驅動具體的設備功能。

3.1 urb

對某個 Endpoint 發起一次讀寫操作,具體工作使用 struct urb 數據結構來承擔。

以下是一個對 Endpoint 0 使用 urb 發起讀寫的一個簡單實例:


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 內存空間 */  urb = usb_alloc_urb(0, GFP_NOIO);  if (!urb)    return -ENOMEM;
  /* (2) 填充 urb 內容,最核心的有3方面:      1、總線地址:Device Num + Endpoint Num      2、數據:data + len      3、回調函數:usb_api_blocking_completion   */  usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,           len, usb_api_blocking_completion, NULL);
  /* (3) 發送 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 的隊列當中 */  retval = usb_submit_urb(urb, GFP_NOIO);  if (unlikely(retval))    goto out;
  expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;  /* (3.2) 當 urb 執行完成后,首先會調用 urb 的回調函數,然后會發送 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 的收發隊列上面,由 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 調用對應的 hcd 的 urb_enqueue() 函數 */  } 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 內部,所以相應的 urb 需要特殊處理,而不能使用 hcd 把數據發送到 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 類型的數據,被掛載到 hcd->status_urb 指針上面      通常 roothub 驅動用這個 urb 來查詢 roothub 的端口狀態   */  if (usb_endpoint_xfer_int(&urb->ep->desc))    return rh_queue_status (hcd, urb);
  /* (2) 對于 control 類型的數據,是想讀取 roothub ep0 上的配置信息      使用軟件來模擬這類操作的響應   */  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 掛載到對應的 ep 鏈表中 */  retval = usb_hcd_link_urb_to_ep(hcd, urb);  if (retval)    goto done;
  /* (1.2) 將 urb 賦值給 hcd->status_urb      在 hcd 驅動中,會通過這些接口來通知 roothub 的端口狀態變化   */  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 配置讀寫的響應 */}

4. Usb Hub Driver

8107fcec-e78a-11ec-ba43-dac502259ad0.png

普通的 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);}

4.1 normal hub port op

在普通的 hub 中,端口操作是通過標準的 urb 發起 usb ep0 讀寫。分為兩類:

  • 1、通過輪詢讀取 Hub Class-specific Requests 配置來查詢端口的狀態:

812cf65a-e78a-11ec-ba43-dac502259ad0.png

  • 2、設置和使能端口也是通過 Hub Class-specific Requests 中相應的命令實現的:

81a1b80a-e78a-11ec-ba43-dac502259ad0.png

4.2 rootHub port op

而對于 roothub 來說,對端口的操作的 urb 都需要特殊處理 (以 EHCI 的驅動為例):

  • 1、端口狀態的變化可以通過 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 intehci_hub_status_data (struct usb_hcd *hcd, char *buf){  /* (1.1) 通過 HCD 驅動,獲取 roothub 端口的狀態 */}

  • 2、設置和使能端口需要嫁接到 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 的端口 */}

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` 數據結構 */    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) 使能端口,并且調用 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 是主機側的硬件實現,主要分為以下種類:

  • Usb1.0 有兩種控制器標準:OHCI 康柏的開放主機控制器接口,UHCI Intel的通用主機控制器接口。它們的主要區別是UHCI更加依賴軟件驅動,因此對CPU要求更高,但是自身的硬件會更廉價。

  • Usb2.0 只有一種控制器標準:EHCI。因為 EHCI 只支持高速傳輸,所以EHCI控制器包括四個虛擬的全速或者慢速控制器。EHCI主要用于usb 2.0,老的Usb1.1用OHCI和UHCI。EHCI為了兼容Usb1.1,將老的OHCI和UHCI合并到EHCI規范里。

  • Usb3.0 控制器標準:XHCI。XHCI是Intel最新開發的主機控制器接口,廣泛用戶Intel六代Skylake處理器對應的100系列主板上,支持USB3.0接口,往下也兼容USB2.0。XHCI英文全稱eXtensible Host Controller Interface,是一種可擴展的主機控制器接口,是Intel開發的USB主機控制器。Intel 系列芯片的USB協議采用的就是XHCI主控,主要面向USB 3.0標準的,同時也兼容2.0以下的設備。

我們以應用最廣泛的 EHCI 為例,分析其軟硬件實現的架構。

5.1 ehci hardware

5.1.1 compatible usb1.0

對 EHCI 來說,它向下兼容的方案是非常有特點的。因為 EHCI 只支持 Usb2.0 高速傳輸,為了向下兼容 Usb1.1,它直接在內部集成最多4個全速或者慢速控制器 OHCI。在 EHCI 協議內稱這種伴生的 OHCI 控制器為 companion host controllers

81dcb81a-e78a-11ec-ba43-dac502259ad0.png

由 EHCI 驅動根據端口速率情況來決定由誰來處理:

  • 每個端口有一個 Owner 屬性,用來決定是 EHCI 管理還是 OHCI 管理。就是一個 Switch 開關,決定 USB 數據切到哪邊處理。

  • 初始狀態時端口默認屬于 OHCI 管理。所以對于硬件上從 OHCI 升級到 EHCI,而軟件上只有 OHCI 驅動而沒有 EHCI 驅動的系統來說是透明的,它繼續把 EHCI 當成 OHCI 硬件來使用就行了,保持完美的向前兼容。

  • 如果系統軟件上啟用了 EHCI 驅動,它首先會把所有端口的Owner配置成 EHCI 管理。如果 EHCI 驅動發現端口連接且速率是全速或者慢速,則把端口的Owner配置成 OHCI 管理。

對于 EHCI 這種包含兩種控制器的兼容方式,軟件上需要同時啟動 EHCI Driver 和 OHCI Driver,才能完整的兼容 Usb1.0 和 Usb2.0:

821dcaf8-e78a-11ec-ba43-dac502259ad0.png

5.1.2 Periodic Schedule

82557ac0-e78a-11ec-ba43-dac502259ad0.png

EHCI 把數據傳輸分成了兩類來進行調度:

  • Periodic Schedule。用來傳輸對時間延遲要求高的 Endpoint 數據,包括 Isochronous Transfer 和 Interrupt Transfer。

  • Asynchronous Schedule。用來傳輸對時間延遲要求不高的 Endpoint 數據,包括 Control Transfer 和 Bulk Transfer。

828cfafe-e78a-11ec-ba43-dac502259ad0.png

Periodic Schedule 內部實現如上圖所示,核心是兩級鏈表:

  • 1、第一級鏈表如上圖綠色所示。是各種傳輸結構的實際描述符,主要包含以下幾種類型的描述符:

82ee8e22-e78a-11ec-ba43-dac502259ad0.png

  • 2、第二級鏈表如上圖橙色所示。是一個指針數組,數組中保存的是指向第一級鏈表的指針。這里每個數組成員代表一個時間分片 Frame/Micro-Frame 的起始位置,每個時間片會根據指針傳輸第一級鏈表中的數據,直到第一級鏈表的結尾。指針的格式如下:

8326e3bc-e78a-11ec-ba43-dac502259ad0.png

這里的調度思想就是:第一級鏈表是一個傳輸數據全集,第二級鏈表決定了某個時間片里要傳輸的數據。這樣合理的安排二級鏈表的指針,比如間隔8次指向同一位置這部分數據的interval就是8,間隔4次指向同一位置這部分數據的interval就是4。 第一級鏈表也是要根據interval排序的。

Periodic Schedule 中幾個核心的描述符如下:

1、Isochronous (High-Speed) Transfer Descriptor (iTD)

8396da32-e78a-11ec-ba43-dac502259ad0.png

2、Queue Head

83da8c32-e78a-11ec-ba43-dac502259ad0.png

2.1、Queue Element Transfer Descriptor (qTD)

8421ea8c-e78a-11ec-ba43-dac502259ad0.png

5.1.3 Asynchronous Schedule

8457648c-e78a-11ec-ba43-dac502259ad0.png

Asynchronous Schedule 內部實現非常的簡單就只有一級鏈表,鏈表中只有Queue Head類型的描述符。每個時間片內傳輸完 Period 數據以后,再盡可能的傳輸 Asynchronous 數據即可。

5.2 ehci driver

ehci driver 負責把 echi 功能封裝成標準的 hcd 驅動。它主要完成兩項工作:

  • 1、注冊標準的 hcd 驅動。把 Client Software 傳說下來的 urb 映射到 EHCI 的鏈表中進行傳輸。

  • 2、創建一個虛擬的根 hub 設備,即 roothub。

5.2.1 urb transfer

ehci 注冊 hcd 驅動:


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) 注冊標準的 hcd 驅動 */  err = usb_add_hcd(hcd, irq, IRQF_SHARED);}

hcd 驅動向上提供了標準接口,最終的實現會調用到 ehci_hc_driver 當中。


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 過程中,最核心的是調用上述的 ehci_urb_enqueue() 和 ehci_urb_dequeue() 函數。

5.2.2 roothub

首先創建虛擬的 roothub:


/* (1) 首先創建和初始化 `usb_device` 結構: */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,標明自己是一個 usb device */  dev->dev.type = &usb_device_type;  dev->dev.groups = usb_device_groups;
}
/* (2) 然后注冊  `usb_device` 結構: */usb_add_hcd()→register_root_hub()→usb_new_device()→device_add()

然后因為 roothub 并不是在 Usb 物理總線上,所以對它的查詢和配置需要特殊處理。詳見Usb Hub Driver這一節。

6. Usb Client Software

這里再詳細分析一下典型的 Usb Client Software 即 usb mouse 驅動,看看它是怎么利用 urb 讀取 usb 設備數據的。


static const struct usb_device_id usb_mouse_id_table[] = {  /* (1) 驅動可以適配的 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、首先根據得到的 endpoint 準備好 urb,創建好 input 設備:


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) 得到當前 interface 中的第一個 endpoint,mouse設備只需一個 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,開始查詢數據 */  if (usb_submit_urb(mouse->irq, GFP_KERNEL))    return -EIO;
  return 0;}

3、在傳輸完 urb 的回調函數中,根據讀回的數據上報 input 事件,并且重新提交 urb 繼續查詢:


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) 根據 urb 讀回的數據,上報 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 繼續查詢 */  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
    usb
    +關注

    關注

    60

    文章

    7963

    瀏覽量

    265270
  • Host
    +關注

    關注

    0

    文章

    32

    瀏覽量

    34644

原文標題:Linux usb Host 詳解

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    SiC模塊封裝技術解析

    昨天比較詳細的描寫了半導體材料中襯板和基板的選擇,里面提到了功率器件在新能源汽車中的應用,那么功率器件的襯板和基板的選擇也是在半導體的工藝中比較重要的部分,而對于模塊來說,梵易之前對IGBT模塊有比
    的頭像 發表于 01-02 10:20 ?242次閱讀
    SiC<b class='flag-5'>模塊</b>封裝技術<b class='flag-5'>解析</b>

    AUTOSAR通信協議解析 如何實現AUTOSAR通信

    AUTOSAR(Automotive Open System Architecture)即汽車開放系統架構,該架構支持汽車電子控制單元(ECU)之間的通信,實現了高度模塊化和可重用性。AUTOSAR
    的頭像 發表于 12-17 14:54 ?823次閱讀

    PLC數據采集模塊的編程方法解析

    PLC數據采集模塊的編程方法主要依賴于所使用的PLC品牌和型號,以及具體的應用場景和需求。以下是對PLC數據采集模塊編程方法的一般性解析: 一、PLC數據采集模塊概述 PLC數據采集
    的頭像 發表于 11-26 13:53 ?328次閱讀

    DSP功放HOSt和acc區別

    、基本概念 HOST :在DSP功放中,HOST通常指的是主機或中央處理器。它是整個系統的控制中心,負責接收和處理來自外部設備的音頻信號,并將其轉換為適合功放放大的信號。HOST在DSP功放中扮演著至關重要
    的頭像 發表于 10-22 17:06 ?1741次閱讀

    嵌入式MXM模塊(NVIDIA安培架構)

    電子發燒友網站提供《嵌入式MXM模塊(NVIDIA安培架構).pdf》資料免費下載
    發表于 10-09 11:09 ?0次下載

    自動售貨機MDB協議中文解析(七)MDB-RS232控制紙幣器的詳細流程和解析

    自動售貨機MDB協議中文解析(七)MDB-RS232控制紙幣器的詳細流程和解析
    的頭像 發表于 09-09 10:04 ?650次閱讀

    GPU云服務器架構解析及應用優勢

    GPU云服務器作為一種高性能計算資源,近年來在人工智能、大數據分析、圖形渲染等領域得到了廣泛應用。它結合了云計算的靈活性與GPU的強大計算能力,為企業和個人用戶提供了一種高效、便捷的計算解決方案。下面我們將從架構解析和技術優勢兩個方面來
    的頭像 發表于 08-14 09:43 ?436次閱讀

    SSD架構與功能模塊詳解

    在之前的系列文章中,我們介紹了固態硬盤的系列知識,包括閃存的介質、原理,以及作為SSD大腦的控制器設計,本文將詳細介紹SSD架構以及功能模塊
    的頭像 發表于 07-27 10:30 ?1092次閱讀
    SSD<b class='flag-5'>架構</b>與功能<b class='flag-5'>模塊</b>詳解

    深度神經網絡(DNN)架構解析與優化策略

    堆疊多個隱藏層,逐步提取和轉化輸入數據的特征,最終實現復雜的預測和分類任務。本文將對DNN的架構進行詳細解析,并探討其優化策略,以期為相關研究和應用提供參考。
    的頭像 發表于 07-09 11:00 ?2040次閱讀

    STM32跟wifi模塊通過USB在沒有host的前提下如何交互呢?

    后不需要再插拔。是不是可以通過USB簡單的進行設備與設備的連接,不用考慮是否是host? 2. 如果1假設成立,那STM32跟wifi模塊通過USB在沒有host的前提下如何交互呢?
    發表于 05-13 08:25

    一文解析AI驅動光模塊變革

    傳統的三層架構已不再適應日益增長的數據中心流量需求,葉脊架構的出現解決了這一問題。葉脊架構下,光模塊數量可高達數十倍,以滿足大規模AI集群對大帶寬、低時延、無損網絡的需求。智算中心網絡
    發表于 04-22 10:42 ?766次閱讀
    一文<b class='flag-5'>解析</b>AI驅動光<b class='flag-5'>模塊</b>變革

    網絡布局與光模塊配置需求深度解析

    以傳統三層架構到葉脊架構的轉變為例,葉脊網絡架構下,光模塊數量提升最高可達到數十倍。
    發表于 04-01 10:09 ?1897次閱讀
    網絡布局與光<b class='flag-5'>模塊</b>配置需求深度<b class='flag-5'>解析</b>

    SATA3.0 Host Controller IP介紹

    SATA3.0 Host IP不僅實現了SATA協議的PHY(物理層)、link(鏈路層)和TRN(傳輸層),并且實現了CMD(命令層)和APP(應用層),支持1.5、3和6Gbps傳輸速率
    發表于 02-27 15:53 ?0次下載

    NVMe Host Controller IP實現高性能存儲解決方案

    電子發燒友網站提供《NVMe Host Controller IP實現高性能存儲解決方案.pdf》資料免費下載
    發表于 02-21 14:28 ?2次下載

    什么是USB HOST、USB Slave和USB OTG?它們之間有什么區別?

    Slave和USB OTG是與USB相關的術語,用來區分不同類型的USB設備和其功能。下面是對這三個術語的詳細解釋和它們之間的區別的詳盡說明。 1. USB HOST(USB主機): USB HOST
    的頭像 發表于 02-02 15:32 ?1.4w次閱讀
    主站蜘蛛池模板: 亚洲国产女人aaa毛片在线| 日产精品卡二卡三卡四卡无卡乱码| 夜夜爱夜夜操| 天天色成人| 精品亚洲大全| 亚洲qingse中文在线| 6080yy午夜不卡一二三区| 国模欢欢炮交啪啪150| 亚洲三级在线看| 四虎最新在线| 免费观看理论片毛片| 国产精品伦子一区二区三区| 综合网在线观看| 日本特黄视频| 在线免费看影视网站| 婷婷在线网站| 亚洲综合香蕉| 欧美午夜大片| 甘婷婷一级毛片免费看| 天天爱天天干天天操| 干干天天| 男女交性视频免费| 天堂网av2014| 精品在线视频一区| 又黄又爽又猛午夜性色播在线播放| 日日操天天射| 69日本xxxxxxxxx78| 性欧美在线| 看免费黄色大片| 午夜影院免费视频| good韩国理论在线三级| 免费久久精品国产片香蕉| 特黄一级毛片| 黄www色| 天天干天天做天天射| 欧美性猛交xxxx免费| 亚洲综合日韩欧美一区二区三| 欧美伊人久久综合网| 4虎影院在线观看| 丰满放荡岳乱妇91www| 国产尤物在线视频|