色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

詳細(xì)解析Host各模塊的架構(gòu)和原理

Linux閱碼場(chǎng) ? 來源:Linux閱碼場(chǎng) ? 作者:Linux閱碼場(chǎng) ? 2022-06-09 09:57 ? 次閱讀

1.簡(jiǎn)介

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

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

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

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

由前幾節(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,#ifdef  CONFIG_PM  .suspend = usb_generic_driver_suspend,  .resume = usb_generic_driver_resume,#endif  .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

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

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

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

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

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

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

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

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 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):


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 驅(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

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

由 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:

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

5.1.2 Periodic Schedule

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

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。

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

Periodic Schedule 內(nèi)部實(shí)現(xiàn)如上圖所示,核心是兩級(jí)鏈表:

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

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

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

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

這里的調(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)

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 內(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

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • usb
    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)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    SiC模塊封裝技術(shù)解析

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

    AUTOSAR通信協(xié)議解析 如何實(shí)現(xiàn)AUTOSAR通信

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

    PLC數(shù)據(jù)采集模塊的編程方法解析

    PLC數(shù)據(jù)采集模塊的編程方法主要依賴于所使用的PLC品牌和型號(hào),以及具體的應(yīng)用場(chǎng)景和需求。以下是對(duì)PLC數(shù)據(jù)采集模塊編程方法的一般性解析: 一、PLC數(shù)據(jù)采集模塊概述 PLC數(shù)據(jù)采集
    的頭像 發(fā)表于 11-26 13:53 ?371次閱讀

    DSP功放HOSt和acc區(qū)別

    、基本概念 HOST :在DSP功放中,HOST通常指的是主機(jī)或中央處理器。它是整個(gè)系統(tǒng)的控制中心,負(fù)責(zé)接收和處理來自外部設(shè)備的音頻信號(hào),并將其轉(zhuǎn)換為適合功放放大的信號(hào)。HOST在DSP功放中扮演著至關(guān)重要
    的頭像 發(fā)表于 10-22 17:06 ?1885次閱讀

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

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

    自動(dòng)售貨機(jī)MDB協(xié)議中文解析(七)MDB-RS232控制紙幣器的詳細(xì)流程和解析

    自動(dòng)售貨機(jī)MDB協(xié)議中文解析(七)MDB-RS232控制紙幣器的詳細(xì)流程和解析
    的頭像 發(fā)表于 09-09 10:04 ?670次閱讀

    GPU云服務(wù)器架構(gòu)解析及應(yīng)用優(yōu)勢(shì)

    GPU云服務(wù)器作為一種高性能計(jì)算資源,近年來在人工智能、大數(shù)據(jù)分析、圖形渲染等領(lǐng)域得到了廣泛應(yīng)用。它結(jié)合了云計(jì)算的靈活性與GPU的強(qiáng)大計(jì)算能力,為企業(yè)和個(gè)人用戶提供了一種高效、便捷的計(jì)算解決方案。下面我們將從架構(gòu)解析和技術(shù)優(yōu)勢(shì)兩個(gè)方面來
    的頭像 發(fā)表于 08-14 09:43 ?455次閱讀

    SSD架構(gòu)與功能模塊詳解

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

    深度神經(jīng)網(wǎng)絡(luò)(DNN)架構(gòu)解析與優(yōu)化策略

    堆疊多個(gè)隱藏層,逐步提取和轉(zhuǎn)化輸入數(shù)據(jù)的特征,最終實(shí)現(xiàn)復(fù)雜的預(yù)測(cè)和分類任務(wù)。本文將對(duì)DNN的架構(gòu)進(jìn)行詳細(xì)解析,并探討其優(yōu)化策略,以期為相關(guān)研究和應(yīng)用提供參考。
    的頭像 發(fā)表于 07-09 11:00 ?2102次閱讀

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

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

    一文解析AI驅(qū)動(dòng)光模塊變革

    傳統(tǒng)的三層架構(gòu)已不再適應(yīng)日益增長(zhǎng)的數(shù)據(jù)中心流量需求,葉脊架構(gòu)的出現(xiàn)解決了這一問題。葉脊架構(gòu)下,光模塊數(shù)量可高達(dá)數(shù)十倍,以滿足大規(guī)模AI集群對(duì)大帶寬、低時(shí)延、無損網(wǎng)絡(luò)的需求。智算中心網(wǎng)絡(luò)
    發(fā)表于 04-22 10:42 ?777次閱讀
    一文<b class='flag-5'>解析</b>AI驅(qū)動(dòng)光<b class='flag-5'>模塊</b>變革

    網(wǎng)絡(luò)布局與光模塊配置需求深度解析

    以傳統(tǒng)三層架構(gòu)到葉脊架構(gòu)的轉(zhuǎn)變?yōu)槔~脊網(wǎng)絡(luò)架構(gòu)下,光模塊數(shù)量提升最高可達(dá)到數(shù)十倍。
    發(fā)表于 04-01 10:09 ?1912次閱讀
    網(wǎng)絡(luò)布局與光<b class='flag-5'>模塊</b>配置需求深度<b class='flag-5'>解析</b>

    SATA3.0 Host Controller IP介紹

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

    NVMe Host Controller IP實(shí)現(xiàn)高性能存儲(chǔ)解決方案

    電子發(fā)燒友網(wǎng)站提供《NVMe Host Controller IP實(shí)現(xiàn)高性能存儲(chǔ)解決方案.pdf》資料免費(fèi)下載
    發(fā)表于 02-21 14:28 ?2次下載

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

    Slave和USB OTG是與USB相關(guān)的術(shù)語,用來區(qū)分不同類型的USB設(shè)備和其功能。下面是對(duì)這三個(gè)術(shù)語的詳細(xì)解釋和它們之間的區(qū)別的詳盡說明。 1. USB HOST(USB主機(jī)): USB HOST
    的頭像 發(fā)表于 02-02 15:32 ?1.4w次閱讀
    主站蜘蛛池模板: yellow日本动漫观看免费 | 精品国产成人a区在线观看 精品国产成人AV在线看 | 精品国产自在现线拍400部 | 秋霞网在线伦理影片 | 精品 在线 视频 亚洲 | 亚洲一区精品在线 | 97国产精品视频在线观看 | 人妻少妇69式99偷拍 | 亚洲不卡一卡2卡三卡4卡5卡 | 超碰97av 在线人人操 | 日韩欧美1区 | 国产AV99激情久久无码天堂 | 久久亚洲免费视频 | 欧美在线看欧美视频免费 | 7723手机游戏破解版下载 | 国产午夜精品久久久久九九 | 亚洲欧美日韩在线码不卡 | 碰超成人在线公开免费视频 | 久久精品亚洲AV中文2区金莲 | 久久久久亚洲精品影视 | 亚洲无线观看国产 | 97人摸人人澡人人人超一碰 | 伊人久久电影网 | 把极品白丝老师啪到腿软 | 俄罗斯XXXXXL18 | 色偷偷男人天堂 | 亚洲中文字幕无码爆乳APP | 在线精品一卡乱码免费 | 97超视频在线观看 | 北岛玲手机在线观看视频观看 | 欧美国产精品久久久乱码 | 帝王被大臣们调教高肉 | 青草久久精品亚洲综合专区 | 高H辣肉办公室 | 成人无码在线视频区 | 国产成人无码WWW免费视频在线 | 成 人 网 站毛片 | 中文字幕免费视频精品一 | 国内精品视频久久久久免费 | xvideos中文版在线视频 | 少妇被躁爽到高潮无码久久 |