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);
- 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總線上。