1. 程式人生 > >usb的hub分析

usb的hub分析

1:usb入口函式:usb_init

參考文件:

註冊一個匯流排bus_register:bus_type:usb_bus_type

呼叫usb_hub_init()用來建立hub初始化,註冊一個基於usb的urb的usb_driver結構體,usb_driver的open函式hub_probe:

hub_probe中呼叫hub_configure函式。

每當裝置連線到usb介面時,usb匯流排在查詢hub狀態資訊會觸發hub的中斷hub_irq,利用kick_khubd將hub結構通過event_list新增到khubd的佇列hub_event_list,然後喚醒khudb,進入hub_e_vents函式,處理khudb事件佇列,從khubd的hub_event_list中的每個usb_hub資料結構。

建立一個hub_thread執行緒,呼叫hub_events();呼叫hub_port_connect_change(物理邏輯更改就呼叫該函式)

usb_alloc_dev()然後對每個函式呼叫usb的裝置建構函式hub_port_init(復位usb3.0得到裝置描述符),

檢查裝置的執行速度check_highspeed()

usb_new_device()建立一個新的device裝置。

 Hub_probe流程圖:

 關於usb_host的驅動:(ehci)

usb_register_device_driver(&usb_generic_driver, THIS_MODULE); 註冊一個裝置驅動:usb_generic_driver

usb_device_driver:定義一個usb裝置驅動 

ehci設備註冊:

static struct platform_device hiusb_ehci_platdev = {     .name = "hiusb-ehci", //匹配裝置驅動     .id = 0,     .dev = {         .platform_data     = NULL,         .dma_mask          = &usb_dmamask,         .coherent_dma_mask = DMA_BIT_MASK(32),         .release           = usb_ehci_platdev_release,     },     .num_resources = ARRAY_SIZE(hiusb_ehci_res),     .resource      = hiusb_ehci_res, };

裝置資源:

static struct resource hiusb_ehci_res[] = {     [0] = {         .start = CONFIG_HIUSB_EHCI_IOBASE,         .end   = CONFIG_HIUSB_EHCI_IOBASE             + CONFIG_HIUSB_EHCI_IOSIZE - 1,         .flags = IORESOURCE_MEM,     },     [1] = {         .start = CONFIG_HIUSB_EHCI_IRQNUM,         .end   = CONFIG_HIUSB_EHCI_IRQNUM,         .flags = IORESOURCE_IRQ,     }, };

平臺驅動註冊:

static struct platform_driver hiusb_ehci_hcd_driver = {     .probe         = hiusb_ehci_hcd_drv_probe,     .remove        = hiusb_ehci_hcd_drv_remove,     .shutdown      = usb_hcd_platform_shutdown,     .driver = {         .name  = "hiusb-ehci",         .owner = THIS_MODULE,         .pm    = HIUSB_EHCI_PMOPS,     } };

平臺裝置和平臺驅動通過hiusb-ehci匹配,匹配成功呼叫平臺驅動的hiusb_ehci_hcd_drv_probe函式

平臺探測函式

static int hiusb_ehci_hcd_drv_probe(struct platform_device *pdev) {     struct usb_hcd *hcd;     struct ehci_hcd *ehci;     struct resource *res;     int ret;

    if (usb_disabled())         return -ENODEV;

    if (pdev->resource[1].flags != IORESOURCE_IRQ) {         pr_debug("resource[1] is not IORESOURCE_IRQ");         return -ENOMEM;     }     hcd = usb_create_hcd(&hiusb_ehci_hc_driver, &pdev->dev, "hiusb-ehci");     if (!hcd)         return -ENOMEM;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);     hcd->rsrc_start = res->start;     hcd->rsrc_len = resource_size(res);

    if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {         pr_debug("request_mem_region failed");         ret = -EBUSY;         goto err1;     }

    hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);     if (!hcd->regs) {         pr_debug("ioremap failed");         ret = -ENOMEM;         goto err2;     }

    hiusb_start_hcd();

    ehci = hcd_to_ehci(hcd);     ehci->caps = hcd->regs;     ehci->regs = hcd->regs +         HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));

    /* cache this readonly data; minimize chip reads */     ehci->hcs_params = readl(&ehci->caps->hcs_params);

    ret = usb_add_hcd(hcd, pdev->resource[1].start,               IRQF_DISABLED | IRQF_SHARED);     if (ret == 0) {         platform_set_drvdata(pdev, hcd);         return ret;     }

    hiusb_stop_hcd();     iounmap(hcd->regs); err2:     release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1:     usb_put_hcd(hcd);     return ret; }

探測函式通過usb_create_hcd呼叫了ehci的主機控制驅動:

static const struct hc_driver hiusb_ehci_hc_driver = {     .description        = hcd_name, //hcd主機控制器名稱     .product_desc        = "HIUSB EHCI",     .hcd_priv_size        = sizeof(struct ehci_hcd), //hcd主機控制器大小

    /*      * generic hardware linkage      */     .irq            = ehci_irq,//ehci的硬體中斷     .flags            = HCD_MEMORY | HCD_USB2, //usb2.0

    /*      * basic lifecycle operations      *      * FIXME -- ehci_init() doesn't do enough here.      * See ehci-ppc-soc for a complete implementation.      */     .reset            = hiusb_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請求佇列     .urb_dequeue        = ehci_urb_dequeue, //釋放佇列     .endpoint_disable    = ehci_endpoint_disable, //端點禁止     .endpoint_reset        = ehci_endpoint_reset,

    /*      * scheduling support      */     .get_frame_number    = ehci_get_frame,

    /*      * root hub support      */     .hub_status_data    = ehci_hub_status_data, //獲取hub狀態     .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,

    .clear_tt_buffer_complete    = ehci_clear_tt_buffer_complete, };

usb_create_hcd:usb主機控制器建立(hcd.c)

struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,         struct device *dev, const char *bus_name,         struct usb_hcd *primary_hcd) {     struct usb_hcd *hcd;

    hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);     if (!hcd) {         dev_dbg (dev, "hcd alloc failed\n");         return NULL;     }     if (primary_hcd == NULL) {         hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),                 GFP_KERNEL);         if (!hcd->bandwidth_mutex) {             kfree(hcd);             dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");             return NULL;         }         mutex_init(hcd->bandwidth_mutex);         dev_set_drvdata(dev, hcd);     } else {         hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;         hcd->primary_hcd = primary_hcd;         primary_hcd->primary_hcd = primary_hcd;         hcd->shared_hcd = primary_hcd;         primary_hcd->shared_hcd = hcd;     }

    kref_init(&hcd->kref);

  1.     usb_bus_init(&hcd->self); //usb_hcd作為一個匯流排初始化

    hcd->self.controller = dev;     hcd->self.bus_name = bus_name;     hcd->self.uses_dma = (dev->dma_mask != NULL);

    init_timer(&hcd->rh_timer);     hcd->rh_timer.function = rh_timer_func;     hcd->rh_timer.data = (unsigned long) hcd; #ifdef CONFIG_USB_SUSPEND     INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif

    hcd->driver = driver;//usb_hcd主機控制器繫結主機控制器驅動driver     hcd->speed = driver->flags & HCD_MASK;     hcd->product_desc = (driver->product_desc) ? driver->product_desc :             "USB Host Controller";     return hcd; }

a> struct usb_hcd usb主機控制器

b> struct hc_driver 主機控制器驅動

c> struct usb_bus self 匯流排

d> struct usb_device *rhdev 裝置

usb主機控制器新增 int usb_add_hcd(struct usb_hcd *hcd,         unsigned int irqnum, unsigned long irqflags) {     int retval;     struct usb_device *rhdev;

    dev_info(hcd->self.controller, "%s\n", hcd->product_desc);

    /* Keep old behaviour if authorized_default is not in [0, 1]. */     if (authorized_default < 0 || authorized_default > 1)         hcd->authorized_default = hcd->wireless? 0 : 1;     else         hcd->authorized_default = authorized_default;     set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);

    /* HC is in reset state, but accessible.  Now do the one-time init,      * bottom up so that hcds can customize the root hubs before khubd      * starts talking to them.  (Note, bus id is assigned early too.)      */     if ((retval = hcd_buffer_create(hcd)) != 0) { //分配一個dma池:具體dma的分配可以檢視該函式         dev_dbg(hcd->self.controller, "pool alloc failed\n");         return retval;     }

    if ((retval = usb_register_bus(&hcd->self)) < 0)//註冊一個usb_bus         goto err_register_bus;

    if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { //分配一個dev裝置         dev_err(hcd->self.controller, "unable to allocate root hub\n");         retval = -ENOMEM;         goto err_allocate_root_hub;     }     hcd->self.root_hub = rhdev;

    switch (hcd->speed) {     case HCD_USB11:         rhdev->speed = USB_SPEED_FULL;         break;     case HCD_USB2:         rhdev->speed = USB_SPEED_HIGH;         break;     case HCD_USB3:         rhdev->speed = USB_SPEED_SUPER;         break;     default:         retval = -EINVAL;         goto err_set_rh_speed;     }

    /* wakeup flag init defaults to "everything works" for root hubs,      * but drivers can override it in reset() if needed, along with      * recording the overall controller's system wakeup capability.      */     device_set_wakeup_capable(&rhdev->dev, 1);

    /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is      * registered.  But since the controller can die at any time,      * let's initialize the flag before touching the hardware.      */     set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);

    /* "reset" is misnamed; its role is now one-time init. the controller      * should already have been reset (and boot firmware kicked off etc).      */     if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {         dev_err(hcd->self.controller, "can't setup\n");         goto err_hcd_driver_setup;     }     hcd->rh_pollable = 1;

    /* NOTE: root hub and controller capabilities may not be the same */     if (device_can_wakeup(hcd->self.controller)             && device_can_wakeup(&hcd->self.root_hub->dev))         dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");

    /* enable irqs just before we start the controller,      * if the BIOS provides legacy PCI irqs.      */     if (usb_hcd_is_primary_hcd(hcd) && irqnum) {         retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);         if (retval)             goto err_request_irq;     }

    hcd->state = HC_STATE_RUNNING;     retval = hcd->driver->start(hcd);     if (retval < 0) {         dev_err(hcd->self.controller, "startup error %d\n", retval);         goto err_hcd_driver_start;     }

    /* starting here, usbcore will pay attention to this root hub */     if ((retval = register_root_hub(hcd)) != 0)         goto err_register_root_hub;

    retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);     if (retval < 0) {         printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",                retval);         goto error_create_attr_group;     }     if (hcd->uses_new_polling && HCD_POLL_RH(hcd))         usb_hcd_poll_rh_status(hcd);

    /*      * Host controllers don't generate their own wakeup requests;      * they only forward requests from the root hub.  Therefore      * controllers should always be enabled for remote wakeup.      */     device_wakeup_enable(hcd->self.controller);     return retval;

error_create_attr_group:     clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);     if (HC_IS_RUNNING(hcd->state))         hcd->state = HC_STATE_QUIESCING;     spin_lock_irq(&hcd_root_hub_lock);     hcd->rh_registered = 0;     spin_unlock_irq(&hcd_root_hub_lock);

#ifdef CONFIG_USB_SUSPEND     cancel_work_sync(&hcd->wakeup_work); #endif     mutex_lock(&usb_bus_list_lock);     usb_disconnect(&rhdev);        /* Sets rhdev to NULL */     mutex_unlock(&usb_bus_list_lock); err_register_root_hub:     hcd->rh_pollable = 0;     clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);     del_timer_sync(&hcd->rh_timer);     hcd->driver->stop(hcd);     hcd->state = HC_STATE_HALT;     clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);     del_timer_sync(&hcd->rh_timer); err_hcd_driver_start:     if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)         free_irq(irqnum, hcd); err_request_irq: err_hcd_driver_setup: err_set_rh_speed:     usb_put_dev(hcd->self.root_hub); err_allocate_root_hub:     usb_deregister_bus(&hcd->self); err_register_bus:     hcd_buffer_destroy(hcd);     return retval; }

usb_alloc_dev()分配一個dev裝置:

struct usb_device *usb_alloc_dev(struct usb_device *parent,                  struct usb_bus *bus, unsigned port1)

parent表示hub裝置被連線,如果為空,則建立一個root的hub

建立dma池

int hcd_buffer_create(struct usb_hcd *hcd) {     char        name[16];     int        i, size;

    if (!hcd->self.controller->dma_mask &&         !(hcd->driver->flags & HCD_LOCAL_MEM))         return 0;

    for (i = 0; i < HCD_BUFFER_POOLS; i++) {         size = pool_max[i];         if (!size)             continue;         snprintf(name, sizeof name, "buffer-%d", size);         hcd->pool[i] = dma_pool_create(name, hcd->self.controller,                 size, size, 0);         if (!hcd->pool[i]) {             hcd_buffer_destroy(hcd);             return -ENOMEM;         }     }     return 0; }

usb_hcd_request_irqs():申請一個hcd中斷定時器

在usb_add_hcd建立usb_hcd_request_irqs:用來建立和呼叫中斷(中斷的建立)

static int usb_hcd_request_irqs(struct usb_hcd *hcd,         unsigned int irqnum, unsigned long irqflags) {     int retval;

    if (hcd->driver->irq) {

        /* IRQF_DISABLED doesn't work as advertised when used together          * with IRQF_SHARED. As usb_hcd_irq() will always disable          * interrupts we can remove it here.          */         if (irqflags & IRQF_SHARED)             irqflags &= ~IRQF_DISABLED;

        snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",                 hcd->driver->description, hcd->self.busnum);         retval = request_irq(irqnum, &usb_hcd_irq, irqflags,                 hcd->irq_descr, hcd);         if (retval != 0) {             dev_err(hcd->self.controller,                     "request interrupt %d failed\n",                     irqnum);             return retval;         }         hcd->irq = irqnum;         dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,                 (hcd->driver->flags & HCD_MEMORY) ?                     "io mem" : "io base",                     (unsigned long long)hcd->rsrc_start);     } else {         hcd->irq = 0;         if (hcd->rsrc_start)             dev_info(hcd->self.controller, "%s 0x%08llx\n",                     (hcd->driver->flags & HCD_MEMORY) ?                     "io mem" : "io base",                     (unsigned long long)hcd->rsrc_start);     }     return 0; }

註冊一個root的hub分配器register_root_hub():中的函式建立和流程

在register_root_hub()中建立usb_get_device_descriptor()

建立一個usb裝置:usb_new_device()

//在usb_new_device中呼叫usb_enumerate_device()函式中列舉設定

獲取usb的配置資訊usb_get_configuration,給usb裝置分配一個編號,獲取裝置描述符usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE),因為不知道裝置描述符的裡面有多少配置,配置有多少個介面,所以這裡直接讀取USB_DT_DEVICE_SIZE,而這個位元組長度恰好對應裝置描述符的基金額狗踢長度。

讀取配置引數usb_parse_configuration

解析介面資訊:usb_parse_interface

解析端點函式usb_parse_endpoint

端點的主要 工作:根據不同的usb,傳輸不同的位元組數,

在通過如下函式輸出控制資訊功能:announce_device

通過platform匯流排完成ehci裝置和驅動的匹配探測函式,完成主機控制器,和暫存器資源分配,分配一個hcd主機控制器(是否使用dma池),增加主句控制器到usb 總線上,註冊一個跟hub,期間包含最重要的部分,在獲取介面資訊時分兩次讀取,第一次讀取固定長度的介面資訊,第二次根據第一個的描述符裡面的資訊長度讀取整個介面資訊,根據藉口資訊解析端點,最後將該主機控制器的根hub註冊到usb總線上。