1. 主机控制器流程图

device_add()上图和下图的连接!

关于hub_probe()内部的具体实现,详见:点击打开链接

2. echi设备注册

static struct platform_device *nuc970_public_dev[] __initdata = {&&nuc970_device_ehci,
};
static u64 nuc970_device_usb_ehci_dmamask = 0xffffffffUL;static struct platform_device nuc970_device_ehci = {.name       = "nuc970-ehci", //平台设备名,用来与设备驱动匹配的.id        = -1,.num_resources    = ARRAY_SIZE(nuc970_ehci_resource),.resource   = nuc970_ehci_resource,.dev              = {.dma_mask = &nuc970_device_usb_ehci_dmamask,.coherent_dma_mask = 0xffffffffUL}
};
static struct resource nuc970_ehci_resource[] = {[0] = { //寄存器地址.start = NUC970_PA_EHCI,.end   = NUC970_PA_EHCI + NUC970_SZ_EHCI - 1,.flags = IORESOURCE_MEM,},[1] = { //中断.start = IRQ_EHCI,.end   = IRQ_EHCI,.flags = IORESOURCE_IRQ,}
};

ehci设备注册:

platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev));

3. echi平台驱动注册

static int __init ehci_nuc970_init(void)
{return platform_driver_register(&ehci_hcd_nuc970_driver); //echi平台设备驱动注册
}static void __exit ehci_nuc970_cleanup(void)
{platform_driver_unregister(&ehci_hcd_nuc970_driver); //echi平台设备驱动注销}
static struct platform_driver ehci_hcd_nuc970_driver = { //平台设备驱动.probe = ehci_nuc970_probe,.remove = ehci_nuc970_remove,.driver = {.name = "nuc970-ehci", //平台设备驱动名称,用来与2.中的平台设备匹配.owner= THIS_MODULE,},
};

当platform_device平台设备和platform_driver平台驱动注册到platform总线上时,通过设备名和设备驱动名“nuc970-echi”相匹配,最终调用ehci_nuc970_probe()探测函数,接下来我们将重点分析该函数。

4. ehci_nuc970_probe()

static int ehci_nuc970_probe(struct platform_device *pdev)
{//printk("ehci_nuc970_probe()\n");if (usb_disabled())return -ENODEV;return usb_nuc970_probe(&ehci_nuc970_hc_driver, pdev); //见下
}

usb_nuc970_probe()的函数参数为平台设备和ehci主机控制器驱动ehci_nuc970_hc_driver,对应结构体如下:

static const struct hc_driver ehci_nuc970_hc_driver = {.description = hcd_name, //hcd主机控制器名称“ehci-platform”.product_desc = "Nuvoton NUC970 EHCI Host Controller", //产品描述.hcd_priv_size = sizeof(struct ehci_hcd), //echi主机控制器大小/** generic hardware linkage*/.irq = ehci_irq, //echi硬件中断.flags = HCD_USB2|HCD_MEMORY, //usb2.0 | hcd使用内存(其它就是I/O)/** basic lifecycle operations*/.reset = ehci_init, //复位主机控制器.start = ehci_run, //启动主机控制器.stop = ehci_stop, //停止主机控制器/** managing i/o requests and associated device resources*/.urb_enqueue = ehci_urb_enqueue, //urq请求队列.urb_dequeue = ehci_urb_dequeue, //urb释放队列.endpoint_disable = ehci_endpoint_disable, //端点禁止/** scheduling support*/.get_frame_number = ehci_get_frame,/** root hub support*/.hub_status_data = ehci_hub_status_data, //获取hub状态.hub_control = ehci_hub_control, //hub控制操作
#ifdef  CONFIG_PM.bus_suspend = ehci_bus_suspend,.bus_resume = ehci_bus_resume,
#endif
};

usb_nuc970_probe()函数:

return usb_nuc970_probe(&ehci_nuc970_hc_driver, pdev);
static int usb_nuc970_probe(const struct hc_driver *driver,struct platform_device *pdev)
{struct usb_hcd *hcd;struct ehci_hcd *ehci;u32  physical_map_ehci;struct pinctrl *p;int retval;if (IS_ERR(clk_get(NULL, "usbh_hclk"))) {printk("clk_get error!!\n");return -1;}/* multi-function pin select */
#if defined (CONFIG_NUC970_USBH_PWR_PE)/* set over-current active low */__raw_writel(__raw_readl(NUC970_VA_OHCI+0x204) | 0x8, NUC970_VA_OHCI+0x204);/* initial USBH_PPWR0 & USBH_PPWR1 pin -> PE.14 & PE.15 */p = devm_pinctrl_get_select(&pdev->dev, "usbh-ppwr-pe");if (IS_ERR(p)){dev_err(&pdev->dev, "unable to reserve pin\n");retval = PTR_ERR(p);}
#elif defined (CONFIG_NUC970_USBH_PWR_PF)/* set over-current active low */__raw_writel(__raw_readl(NUC970_VA_OHCI+0x204) | 0x8, NUC970_VA_OHCI+0x204);/* initial USBH_PPWR pin -> PF.10 */p = devm_pinctrl_get_select(&pdev->dev, "usbh-ppwr-pf");if (IS_ERR(p)){dev_err(&pdev->dev, "unable to reserve pin\n");retval = PTR_ERR(p);}
#elif defined (CONFIG_NUC970_USBH_OC_ONLY)/* set over-current active low */__raw_writel(__raw_readl(NUC970_VA_OHCI+0x204) | 0x8, NUC970_VA_OHCI+0x204);p = devm_pinctrl_get_select(&pdev->dev, "usbh-ppwr-oc");if (IS_ERR(p)){dev_err(&pdev->dev, "unable to reserve pin\n");retval = PTR_ERR(p);}
#else  //  CONFIG_NUC970_USBH_NONE/* set over-current active high */__raw_writel(__raw_readl(NUC970_VA_OHCI+0x204) &~0x8, NUC970_VA_OHCI+0x204);
#endif/* Enable USB Host clock */clk_prepare(clk_get(NULL, "usb_eclk"));  clk_enable(clk_get(NULL, "usb_eclk"));clk_prepare(clk_get(NULL, "usbh_hclk"));  clk_enable(clk_get(NULL, "usbh_hclk"));if (pdev->resource[1].flags != IORESOURCE_IRQ) { //判定是否是中断pr_debug("resource[1] is not IORESOURCE_IRQ");retval = -ENOMEM;}hcd = usb_create_hcd(driver, &pdev->dev, "nuc970-ehci"); //创建一个usb hcd主机控制器(struct usb_hcd)if (!hcd) {retval = -ENOMEM;goto err1;}//赋值ehci寄存器地址hcd->rsrc_start = pdev->resource[0].start;hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;//申请内存if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {pr_debug("ehci probe request_mem_region failed");retval = -EBUSY;goto err2;}//映射内存hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);if (hcd->regs == NULL) {pr_debug("ehci error mapping memory\n");retval = -EFAULT;goto err3;}ehci = hcd_to_ehci(hcd);ehci->caps = hcd->regs;ehci->regs = hcd->regs + 0x20;/* enable PHY 0/1 */physical_map_ehci = (u32)ehci->caps;__raw_writel(0x160, physical_map_ehci+0xC4);__raw_writel(0x520, physical_map_ehci+0xC8);/* cache this readonly data; minimize chip reads */ehci->hcs_params = readl(&ehci->caps->hcs_params); //echi结构体参数寄存器ehci->sbrn = 0x20;retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); //增加主机控制器if (retval != 0)goto err4;return retval;err4:iounmap(hcd->regs);
err3:release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err2:usb_put_hcd(hcd);
err1:return retval;
}

这个函数内部主要时完成一些资源的分配和初始化,我们只关注usb hcd主机控制器的创建和增加,对应的接口函数如下:

hcd = usb_create_hcd(driver, &pdev->dev, "nuc970-ehci"); //创建一个usb hcd主机控制器(struct usb_hcd)retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); //增加主机控制器

这里我们将逐个分析。

5. usb主机控制器创建

struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,struct device *dev, const char *bus_name)
{return usb_create_shared_hcd(driver, dev, bus_name, NULL); //创建一个共享的USB hcd控制器
}
EXPORT_SYMBOL_GPL(usb_create_hcd);
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); //分配一个usb hcd主机控制器if (!hcd) {dev_dbg (dev, "hcd alloc failed\n");return NULL;}//分配"带宽"互斥锁,干啥用的,忘了!!!if (primary_hcd == NULL) { //=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); //将usb 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; //绑定设备,对应platform_devicehcd->self.bus_name = bus_name; //usb-hcd总线名称“nuc970-ehci”hcd->self.uses_dma = (dev->dma_mask != NULL); //主机控制器是否使用DMA,通过platfrom_device知道dev->dma_mask=0xffffinit_timer(&hcd->rh_timer); //初始化一个root-hub定时器hcd->rh_timer.function = rh_timer_func; //绑定定时器超时的回调函数hcd->rh_timer.data = (unsigned long) hcd; //回调函数的参数为usb-hcd主机控制器
#ifdef CONFIG_PM_RUNTIMEINIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endifhcd->driver = driver; //usb-hcd主机控制器绑定主机控制器驱动driverhcd->speed = driver->flags & HCD_MASK; //获取主机控制器速率hcd->product_desc = (driver->product_desc) ? driver->product_desc : //初始化主机控制器的产品描述符"USB Host Controller";return hcd;
}

该函数内部主要分配了一个usb-hcd主机控制器,初始化主机控制器root-hub的一个定时器回调函数,最后将主机控制器驱动hcd_driver绑定到usb-hcd主机控制器上,这里要区分他们的关系:

a> struct usb_hcd usb主机控制器

b> struct hc_driver 主机控制器驱动

c> struct usb_bus self 总线

d> struct usb_device *rhdev 设备

6. usb主机控制器添加

retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); //增加主机控制器
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]. *//*authorized_default的三种设备状态-1 表示认证无线之外的所有设备0 表示不认证任何设备1 表示认证所有设备*/if (authorized_default < 0 || authorized_default > 1)hcd->authorized_default = hcd->wireless? 0 : 1;elsehcd->authorized_default = authorized_default;set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); //设置usb-hcd的状态标识为“上电”/* 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缓冲池dev_dbg(hcd->self.controller, "pool alloc failed\n");return retval;}if ((retval = usb_register_bus(&hcd->self)) < 0) //注册一个usb-busgoto err_register_bus;if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { //分配一个usb设备,作为root_hubdev_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); //.start = ehci_runif (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_PM_RUNTIMEcancel_work_sync(&hcd->wakeup_work);
#endifmutex_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;
}
EXPORT_SYMBOL_GPL(usb_add_hcd);

usb-hcd主机控制器添加,主要完成以下几点:

1> hcd_buffer_create(hcd) 创建一个hcd DMA池;

2> usb_register_bus(&hcd->self) 注册一个usb总线;

3> usb_alloc_dev(NULL, &hcd->self, 0) 分配一个usb-device设备;

4> usb_hcd_request_irqs(hcd, irqnum, irqflags)申请一个hcd中断定时器;

5> register_root_hub(hcd)注册一个root-hub。

这里将逐个分析:

5.1 hcd_buffer_create()

创建DMA池:

hcd_buffer_create()--> dma_pool_create()-->device_create_file(dev, &dev_attr_pools)

static DEVICE_ATTR(pools, S_IRUGO, show_pools, NULL);
static ssize_t
show_pools(struct device *dev, struct device_attribute *attr, char *buf)
{unsigned temp;unsigned size;char *next;struct dma_page *page;struct dma_pool *pool;next = buf;size = PAGE_SIZE;temp = scnprintf(next, size, "poolinfo - 0.1\n");size -= temp;next += temp;mutex_lock(&pools_lock);list_for_each_entry(pool, &dev->dma_pools, pools) {unsigned pages = 0;unsigned blocks = 0;spin_lock_irq(&pool->lock);list_for_each_entry(page, &pool->page_list, page_list) {pages++;blocks += page->in_use;}spin_unlock_irq(&pool->lock);/* per-pool info, no real statistics yet */temp = scnprintf(next, size, "%-16s %4u %4Zu %4Zu %2u\n",pool->name, blocks,pages * (pool->allocation / pool->size),pool->size, pages);size -= temp;next += temp;}mutex_unlock(&pools_lock);return PAGE_SIZE - size;
}

在终端上执行: cat pools命令调用上面的show_pools(),打印如下信息:

[root@szclou /sys/devices/platform/nuc970-ehci]#cat pools
poolinfo - 0.1
ehci_sitd           0    0   96  0
ehci_itd            0    0  160  0
ehci_qh             1   42   96  1
ehci_qtd            1   42   96  1
buffer-2048         0    0 2048  0
buffer-512          0    0  512  0
buffer-128          0    0  128  0
buffer-32           0    0   32  0

至此,属性文件的操作就是用来显示这些信息的,再看一个usb-root信息

dev->dev.groups = usb_device_groups;

其中dev->dev.groups类型为

const struct attribute_group **groups
const struct attribute_group *usb_device_groups[] = {&dev_attr_grp, //属性组信息&dev_string_attr_grp, //属性字符串组NULL
};
static struct attribute_group dev_attr_grp = {.attrs = dev_attrs,
};
static struct attribute *dev_attrs[] = {/* current configuration's attributes */&dev_attr_configuration.attr,&dev_attr_bNumInterfaces.attr,&dev_attr_bConfigurationValue.attr,&dev_attr_bmAttributes.attr,&dev_attr_bMaxPower.attr,/* device attributes */&dev_attr_urbnum.attr,&dev_attr_idVendor.attr,&dev_attr_idProduct.attr,&dev_attr_bcdDevice.attr,&dev_attr_bDeviceClass.attr,&dev_attr_bDeviceSubClass.attr,&dev_attr_bDeviceProtocol.attr,&dev_attr_bNumConfigurations.attr,&dev_attr_bMaxPacketSize0.attr,&dev_attr_speed.attr,&dev_attr_busnum.attr,&dev_attr_devnum.attr,&dev_attr_devpath.attr,&dev_attr_version.attr,&dev_attr_maxchild.attr,&dev_attr_quirks.attr,&dev_attr_avoid_reset_quirk.attr,&dev_attr_authorized.attr,&dev_attr_remove.attr,&dev_attr_removable.attr,&dev_attr_ltm_capable.attr,NULL,
};
static struct attribute_group dev_string_attr_grp = {.attrs =  dev_string_attrs,.is_visible = dev_string_attrs_are_visible,
};
static struct attribute *dev_string_attrs[] = {&dev_attr_manufacturer.attr,&dev_attr_product.attr,&dev_attr_serial.attr,NULL
};

*dev_string_attrs[] 和 *dev_attrs[]对应系统目录结构信息为:

[root@szclou /sys/devices/platform/nuc970-ehci/usb1]#ls
1-0:1.0              bmAttributes         maxchild
authorized           busnum               product
authorized_default   configuration        quirks
avoid_reset_quirk    descriptors          removable
bConfigurationValue  dev                  remove
bDeviceClass         devnum               serial
bDeviceProtocol      devpath              speed
bDeviceSubClass      driver               subsystem
bMaxPacketSize0      ep_00                uevent
bMaxPower            idProduct            urbnum
bNumConfigurations   idVendor             version
bNumInterfaces       ltm_capable
bcdDevice            manufacturer

5.2 usb_register_bus()

static int usb_register_bus(struct usb_bus *bus)
{int result = -E2BIG;int busnum;mutex_lock(&usb_bus_list_lock);busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);if (busnum >= USB_MAXBUS) {printk (KERN_ERR "%s: too many buses\n", usbcore_name);goto error_find_busnum;}set_bit (busnum, busmap.busmap);bus->busnum = busnum;/* Add it to the local list of buses */list_add (&bus->bus_list, &usb_bus_list); //将当前usb总线添加到usb_bus_list链表中mutex_unlock(&usb_bus_list_lock);usb_notify_add_bus(bus);dev_info (bus->controller, "new USB bus registered, assigned bus ""number %d\n", bus->busnum);return 0;error_find_busnum:mutex_unlock(&usb_bus_list_lock);return result;
}

5.3 usb_alloc_dev()

 if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { //分配一个usb设备,作为root_hubdev_err(hcd->self.controller, "unable to allocate root hub\n");retval = -ENOMEM;goto err_allocate_root_hub;}hcd->self.root_hub = rhdev; //如果上面usb设备分配成功,将作为hcd主机控制器的根设备
struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1)
{struct usb_device *dev;struct usb_hcd *usb_hcd = bus_to_hcd(bus);unsigned root_hub = 0;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return NULL;if (!usb_get_hcd(usb_hcd)) {kfree(dev);return NULL;}/* Root hubs aren't true devices, so don't allocate HCD resources */if (usb_hcd->driver->alloc_dev && parent &&!usb_hcd->driver->alloc_dev(usb_hcd, dev)) {usb_put_hcd(bus_to_hcd(bus));kfree(dev);return NULL;}device_initialize(&dev->dev);dev->dev.bus = &usb_bus_type; //绑定设备的总线为“usb”dev->dev.type = &usb_device_type; //设备的类型dev->dev.groups = usb_device_groups; //对sysfs文件系统的属性文件操作,上面有对usb_device_groups分析dev->dev.dma_mask = bus->controller->dma_mask;set_dev_node(&dev->dev, dev_to_node(bus->controller));dev->state = USB_STATE_ATTACHED; //设置usb状态为绑定dev->lpm_disable_count = 1;atomic_set(&dev->urbnum, 0);INIT_LIST_HEAD(&dev->ep0.urb_list);dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; //设置断点长度dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; //设置端点描述符/* ep0 maxpacket comes later, from device descriptor */usb_enable_endpoint(dev, &dev->ep0, false);dev->can_submit = 1;/* Save readable and stable topology id, distinguishing devices* by location for diagnostics, tools, driver model, etc.  The* string is a path along hub ports, from the root.  Each device's* dev->devpath will be stable until USB is re-cabled, and hubs* are often labeled with these port numbers.  The name isn't* as stable:  bus->busnum changes easily from modprobe order,* cardbus or pci hotplugging, and so on.*/if (unlikely(!parent)) {dev->devpath[0] = '0';dev->route = 0;dev->dev.parent = bus->controller;dev_set_name(&dev->dev, "usb%d", bus->busnum);root_hub = 1;} else {/* match any labeling on the hubs; it's one-based */if (parent->devpath[0] == '0') {snprintf(dev->devpath, sizeof dev->devpath,"%d", port1);/* Root ports are not counted in route string */dev->route = 0;} else {snprintf(dev->devpath, sizeof dev->devpath,"%s.%d", parent->devpath, port1);/* Route string assumes hubs have less than 16 ports */if (port1 < 15)dev->route = parent->route +(port1 << ((parent->level - 1)*4));elsedev->route = parent->route +(15 << ((parent->level - 1)*4));}dev->dev.parent = &parent->dev;dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);/* hub driver sets up TT records */}dev->portnum = port1;dev->bus = bus;dev->parent = parent;INIT_LIST_HEAD(&dev->filelist);#ifdef  CONFIG_PMpm_runtime_set_autosuspend_delay(&dev->dev,usb_autosuspend_delay * 1000);dev->connect_time = jiffies;dev->active_duration = -jiffies;
#endifif (root_hub) /* Root hub always ok [and always wired] */dev->authorized = 1;else {dev->authorized = usb_hcd->authorized_default;dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;}return dev;
}

5.4 usb_hcd_request_irqs()

在分析中断之前,先看下中断的处理流程图

 if (usb_hcd_is_primary_hcd(hcd) && irqnum) {retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); //申请中断if (retval)goto err_request_irq;}
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, //usb_hcd_irq()函数内部将调用hcd->driver->irq(hcd)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;
}

5.5 register_root_hub()

 if ((retval = register_root_hub(hcd)) != 0)goto err_register_root_hub;
static int register_root_hub(struct usb_hcd *hcd)
{struct device *parent_dev = hcd->self.controller;struct usb_device *usb_dev = hcd->self.root_hub;const int devnum = 1;int retval;usb_dev->devnum = devnum; //设备编号usb_dev->bus->devnum_next = devnum + 1; //下一个要打开的设备编号memset (&usb_dev->bus->devmap.devicemap, 0,sizeof usb_dev->bus->devmap.devicemap);set_bit (devnum, usb_dev->bus->devmap.devicemap); //设备编号屏蔽字usb_set_device_state(usb_dev, USB_STATE_ADDRESS); //设置usb设备状态,当前为USB_STATE_ADDRESSmutex_lock(&usb_bus_list_lock);usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); //给端点0描述符分配64个字节长度空间retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); //获取设备描述符,包括配置,接口,端点if (retval != sizeof usb_dev->descriptor) {mutex_unlock(&usb_bus_list_lock);dev_dbg (parent_dev, "can't read %s device descriptor %d\n",dev_name(&usb_dev->dev), retval);return (retval < 0) ? retval : -EMSGSIZE;}if (usb_dev->speed == USB_SPEED_SUPER) { //usb3.0,暂时不分析retval = usb_get_bos_descriptor(usb_dev);if (retval < 0) {mutex_unlock(&usb_bus_list_lock);dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",dev_name(&usb_dev->dev), retval);return retval;}}retval = usb_new_device (usb_dev); //新建一个usb设备if (retval) {dev_err (parent_dev, "can't register root hub for %s, %d\n",dev_name(&usb_dev->dev), retval);} else {spin_lock_irq (&hcd_root_hub_lock);hcd->rh_registered = 1;spin_unlock_irq (&hcd_root_hub_lock);/* Did the HC die before the root hub was registered? */if (HCD_DEAD(hcd))usb_hc_died (hcd);  /* This time clean up */}mutex_unlock(&usb_bus_list_lock);return retval;
}

该函数主要完成的工作时给usb设备分配一个编号(范围1~127),获取设备描述符usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE),这里使用了一个技巧,因为不知道设备描述符里面有多少个配置,配置里有多少个接口,所以这里直接读取USB_DT_DEVICE_SIZE个字节,而这个字节的长度恰好对应设备描述符的结构体成员长度,如下:

/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {  __u8  bLength;//设备描述符的字节数大小,为0x12   __u8  bDescriptorType;//描述符类型编号,为0x01   __le16 bcdUSB;//USB版本号   __u8  bDeviceClass;//USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型   //0x00不是在设备描述符中定义的,如HID   __u8  bDeviceSubClass;//usb分配的子类代码,同上,值由USB规定和分配的  __u8  bDeviceProtocol;//USB分配的设备协议代码,同上   __u8  bMaxPacketSize0;//端点0的最大包的大小   __le16 idVendor;//厂商编号   __le16 idProduct;//产品编号   __le16 bcdDevice;//设备出厂编号  __u8  iManufacturer;//描述厂商字符串的索引   __u8  iProduct;//描述产品字符串的索引  __u8  iSerialNumber;//描述设备序列号字符串的索引   __u8  bNumConfigurations;//可能的配置数量
} __attribute__ ((packed)); 

再后续时创建了一个新的usb设备,下面将详细分析。

5.6 usb_new_device

先贴出流程图

retval = usb_new_device (usb_dev); //新建一个usb设备
int usb_new_device(struct usb_device *udev)
{int err;if (udev->parent) {/* Initialize non-root-hub device wakeup to disabled;* device (un)configuration controls wakeup capable* sysfs power/wakeup controls wakeup enabled/disabled*/device_init_wakeup(&udev->dev, 0);}/* Tell the runtime-PM framework the device is active */pm_runtime_set_active(&udev->dev);pm_runtime_get_noresume(&udev->dev);pm_runtime_use_autosuspend(&udev->dev);pm_runtime_enable(&udev->dev);/* By default, forbid autosuspend for all devices.  It will be* allowed for hubs during binding.*/usb_disable_autosuspend(udev);//枚举设备err = usb_enumerate_device(udev);  /* Read descriptors */if (err < 0)goto fail;dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",udev->devnum, udev->bus->busnum,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));/* export the usbdev device-node for libusb */udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));/* Tell the world! */announce_device(udev);if (udev->serial)add_device_randomness(udev->serial, strlen(udev->serial));if (udev->product)add_device_randomness(udev->product, strlen(udev->product));if (udev->manufacturer)add_device_randomness(udev->manufacturer,strlen(udev->manufacturer));device_enable_async_suspend(&udev->dev);/** check whether the hub marks this port as non-removable. Do it* now so that platform-specific data can override it in* device_add()*/if (udev->parent)set_usb_port_removable(udev);/* Register the device.  The device driver is responsible* for configuring the device and invoking the add-device* notifier chain (used by usbfs and possibly others).*/err = device_add(&udev->dev);if (err) {dev_err(&udev->dev, "can't device_add, error %d\n", err);goto fail;}/* Create link files between child device and usb port device. */if (udev->parent) {struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);struct usb_port *port_dev = hub->ports[udev->portnum - 1];err = sysfs_create_link(&udev->dev.kobj,&port_dev->dev.kobj, "port");if (err)goto fail;err = sysfs_create_link(&port_dev->dev.kobj,&udev->dev.kobj, "device");if (err) {sysfs_remove_link(&udev->dev.kobj, "port");goto fail;}pm_runtime_get_sync(&port_dev->dev);}(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);usb_mark_last_busy(udev);pm_runtime_put_sync_autosuspend(&udev->dev);return err;fail:usb_set_device_state(udev, USB_STATE_NOTATTACHED);pm_runtime_disable(&udev->dev);pm_runtime_set_suspended(&udev->dev);return err;
}

这里主要分析设备枚举,是这里最核心的一个地方!!!

 //枚举设备err = usb_enumerate_device(udev);    /* Read descriptors */
static int usb_enumerate_device(struct usb_device *udev)
{int err;if (udev->config == NULL) {err = usb_get_configuration(udev); //获取配置信息if (err < 0) {if (err != -ENODEV)dev_err(&udev->dev, "can't read configurations, error %d\n",err);return err;}}/* read the standard strings and cache them if present */udev->product = usb_cache_string(udev, udev->descriptor.iProduct);udev->manufacturer = usb_cache_string(udev,udev->descriptor.iManufacturer);udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);err = usb_enumerate_device_otg(udev);if (err < 0)return err;usb_detect_interface_quirks(udev);return 0;
}

5.6.1 获取配置信息

 if (udev->config == NULL) {err = usb_get_configuration(udev);if (err < 0) {if (err != -ENODEV)dev_err(&udev->dev, "can't read configurations, error %d\n",err);return err;}}
int usb_get_configuration(struct usb_device *dev)
{struct device *ddev = &dev->dev;int ncfg = dev->descriptor.bNumConfigurations; //usb配置个数int result = 0;unsigned int cfgno, length;unsigned char *bigbuffer;struct usb_config_descriptor *desc;cfgno = 0;result = -ENOMEM;if (ncfg > USB_MAXCONFIG) {dev_warn(ddev, "too many configurations: %d, ""using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;}if (ncfg < 1) {dev_err(ddev, "no configurations\n");return -EINVAL;}length = ncfg * sizeof(struct usb_host_config);dev->config = kzalloc(length, GFP_KERNEL); //分配一个设备下的ncfg个config配置结构体if (!dev->config)goto err2;length = ncfg * sizeof(char *);dev->rawdescriptors = kzalloc(length, GFP_KERNEL); //分配ncfg个char指针if (!dev->rawdescriptors)goto err2;desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);if (!desc)goto err2;result = 0;for (; cfgno < ncfg; cfgno++) {/* We grab just the first descriptor so we know how long* the whole configuration is */result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,desc, USB_DT_CONFIG_SIZE); //由于无法知道配置的长度,这里使用一个小技巧,先读取配置USB_DT_CONFIG_SIZE个字节,从该长度内部可以获取配置下的接口长度,所以这里会读取两次,这是第一次!!!if (result < 0) {dev_err(ddev, "unable to read config index %d ""descriptor/%s: %d\n", cfgno, "start", result);if (result != -EPIPE)goto err;dev_err(ddev, "chopping to %d config(s)\n", cfgno);dev->descriptor.bNumConfigurations = cfgno;break;} else if (result < 4) {dev_err(ddev, "config index %d descriptor too short ""(expected %i, got %i)\n", cfgno,USB_DT_CONFIG_SIZE, result);result = -EINVAL;goto err;}length = max((int) le16_to_cpu(desc->wTotalLength), //获取当前配置的的总长度USB_DT_CONFIG_SIZE);/* Now that we know the length, get the whole thing */bigbuffer = kmalloc(length, GFP_KERNEL); if (!bigbuffer) {result = -ENOMEM;goto err;}//获取配置的所有长度信息,这里是第二次读取!!!result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);if (result < 0) {dev_err(ddev, "unable to read config index %d ""descriptor/%s\n", cfgno, "all");kfree(bigbuffer);goto err;}if (result < length) {dev_warn(ddev, "config index %d descriptor too short ""(expected %i, got %i)\n", cfgno, length, result);length = result;}dev->rawdescriptors[cfgno] = bigbuffer; //每一个指针指向一个配置//解析配置信息result = usb_parse_configuration(dev, cfgno,&dev->config[cfgno], bigbuffer, length);if (result < 0) {++cfgno;goto err;}}result = 0;err:kfree(desc);dev->descriptor.bNumConfigurations = cfgno;
err2:if (result == -ENOMEM)dev_err(ddev, "out of memory\n");return result;
}

5.6.2 解析配置信息

     //解析配置信息result = usb_parse_configuration(dev, cfgno,&dev->config[cfgno], bigbuffer, length);
static int usb_parse_configuration(struct usb_device *dev, int cfgidx,struct usb_host_config *config, unsigned char *buffer, int size)
{struct device *ddev = &dev->dev;unsigned char *buffer0 = buffer;int cfgno;int nintf, nintf_orig;int i, j, n;struct usb_interface_cache *intfc;unsigned char *buffer2;int size2;struct usb_descriptor_header *header;int len, retval;u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];unsigned iad_num = 0;memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); //拷贝当前配置信息到config->descif (config->desc.bDescriptorType != USB_DT_CONFIG ||config->desc.bLength < USB_DT_CONFIG_SIZE ||config->desc.bLength > size) {dev_err(ddev, "invalid descriptor for config index %d: ""type = 0x%X, length = %d\n", cfgidx,config->desc.bDescriptorType, config->desc.bLength);return -EINVAL;}cfgno = config->desc.bConfigurationValue; //当前配置编号值//这里偏移buffer的目的是啥???不过下面又开始计算配置下的接口了!!!//上面是以前的疑问,现在明白了,buffer += config->desc.bLength 表示跳过配置的内容,直接跳转到接口buffer += config->desc.bLength;  //偏移到接口size -= config->desc.bLength; //减去配置的长度nintf = nintf_orig = config->desc.bNumInterfaces; //接口个数if (nintf > USB_MAXINTERFACES) {dev_warn(ddev, "config %d has too many interfaces: %d, ""using maximum allowed: %d\n",cfgno, nintf, USB_MAXINTERFACES);nintf = USB_MAXINTERFACES;}/* Go through the descriptors, checking their length and counting the* number of altsettings for each interface */n = 0;for ((buffer2 = buffer, size2 = size); //遍历接口size2 > 0;(buffer2 += header->bLength, size2 -= header->bLength)) {if (size2 < sizeof(struct usb_descriptor_header)) {dev_warn(ddev, "config %d descriptor has %d excess ""byte%s, ignoring\n",cfgno, size2, plural(size2));break;}header = (struct usb_descriptor_header *) buffer2; //这里先获取描述符接口的长度和描述符的类型(这里类型为接口)两个字段if ((header->bLength > size2) || (header->bLength < 2)) {dev_warn(ddev, "config %d has an invalid descriptor ""of length %d, skipping remainder of the config\n",cfgno, header->bLength);break;}if (header->bDescriptorType == USB_DT_INTERFACE) { //是接口struct usb_interface_descriptor *d;int inum;d = (struct usb_interface_descriptor *) header; //上面通过两个字段确定了为接口,这里直接转换为接口描述符 struct usb_interface_descriptorif (d->bLength < USB_DT_INTERFACE_SIZE) {dev_warn(ddev, "config %d has an invalid ""interface descriptor of length %d, ""skipping\n", cfgno, d->bLength);continue;}inum = d->bInterfaceNumber; //获取接口的编号if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&n >= nintf_orig) {dev_warn(ddev, "config %d has more interface ""descriptors, than it declares in ""bNumInterfaces, ignoring interface ""number: %d\n", cfgno, inum);continue;}if (inum >= nintf_orig) //当前接口编号是否大于总得接口个数dev_warn(ddev, "config %d has an invalid ""interface number: %d but max is %d\n",cfgno, inum, nintf_orig - 1);/* Have we already encountered this interface?* Count its altsettings */for (i = 0; i < n; ++i) {if (inums[i] == inum) //确定之前是否已经统计了该接口break;}if (i < n) { //if (nalts[i] < 255)++nalts[i]; //对当前接口编号相同的进行统计(初始化为1,在else if语句中)} else if (n < USB_MAXINTERFACES) {inums[n] = inum; //记录当前接口的编号到inums数组中nalts[n] = 1; //初始化当前接口值为1++n;}} else if (header->bDescriptorType ==USB_DT_INTERFACE_ASSOCIATION) {if (iad_num == USB_MAXIADS) {dev_warn(ddev, "found more Interface ""Association Descriptors ""than allocated for in ""configuration %d\n", cfgno);} else {config->intf_assoc[iad_num] =(struct usb_interface_assoc_descriptor*)header;iad_num++;}} else if (header->bDescriptorType == USB_DT_DEVICE ||header->bDescriptorType == USB_DT_CONFIG)dev_warn(ddev, "config %d contains an unexpected ""descriptor of type 0x%X, skipping\n",cfgno, header->bDescriptorType);}  /* for ((buffer2 = buffer, size2 = size); ...) */size = buffer2 - buffer; //size=一个配置中所有接口描述符的长度config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0); //config->desc.wTotalLength = 一个配置和接口描述符的总长度if (n != nintf)dev_warn(ddev, "config %d has %d interface%s, different from ""the descriptor's value: %d\n",cfgno, n, plural(n), nintf_orig);else if (n == 0)dev_warn(ddev, "config %d has no interfaces?\n", cfgno);config->desc.bNumInterfaces = nintf = n;/* Check for missing interface numbers */for (i = 0; i < nintf; ++i) { //由于inums数组中存储的是随机接口编号,所以这里通过从小到大的顺序检测是否正常,否则输出调试警告for (j = 0; j < nintf; ++j) {if (inums[j] == i)break;}if (j >= nintf)dev_warn(ddev, "config %d has no interface number ""%d\n", cfgno, i);}/* Allocate the usb_interface_caches and altsetting arrays */for (i = 0; i < nintf; ++i) { //遍历接口j = nalts[i];if (j > USB_MAXALTSETTING) {dev_warn(ddev, "too many alternate settings for ""config %d interface %d: %d, ""using maximum allowed: %d\n",cfgno, inums[i], j, USB_MAXALTSETTING);nalts[i] = j = USB_MAXALTSETTING;}//sizeof(*intfc): 接口缓存的长度//sizeof(struct usb_host_interface) * j: 表示一个主机控制的长度*接口个数len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;//表示一个接口的缓存(包括接口轮流, 因为存在多个相同的接口编号)config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL); if (!intfc)return -ENOMEM;kref_init(&intfc->ref);}/* FIXME: parse the BOS descriptor *//* Skip over any Class Specific or Vendor Specific descriptors;* find the first interface descriptor */config->extra = buffer; //处理设备中要求的接口个数,之外的接口数据i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,USB_DT_INTERFACE, &n);config->extralen = i;if (n > 0)dev_dbg(ddev, "skipped %d descriptor%s after %s\n",n, plural(n), "configuration");buffer += i;size -= i;/* Parse all the interface/altsetting descriptors */while (size > 0) {//解析接口数据retval = usb_parse_interface(ddev, cfgno, config,buffer, size, inums, nalts);if (retval < 0)return retval;buffer += retval;size -= retval;}/* Check for missing altsettings */for (i = 0; i < nintf; ++i) {intfc = config->intf_cache[i];for (j = 0; j < intfc->num_altsetting; ++j) {for (n = 0; n < intfc->num_altsetting; ++n) {if (intfc->altsetting[n].desc.bAlternateSetting == j)break;}if (n >= intfc->num_altsetting)dev_warn(ddev, "config %d interface %d has no ""altsetting %d\n", cfgno, inums[i], j);}}return 0;
}

5.6.3 解析接口信息

 while (size > 0) {//解析接口数据retval = usb_parse_interface(ddev, cfgno, config,buffer, size, inums, nalts);if (retval < 0)return retval;buffer += retval;size -= retval;}
static int usb_parse_interface(struct device *ddev, int cfgno,struct usb_host_config *config, unsigned char *buffer, int size,u8 inums[], u8 nalts[])
{unsigned char *buffer0 = buffer;struct usb_interface_descriptor   *d;int inum, asnum;struct usb_interface_cache *intfc;struct usb_host_interface *alt;int i, n;int len, retval;int num_ep, num_ep_orig;d = (struct usb_interface_descriptor *) buffer; //获取接口描述符信息buffer += d->bLength;size -= d->bLength;if (d->bLength < USB_DT_INTERFACE_SIZE)goto skip_to_next_interface_descriptor;/* Which interface entry is this? */intfc = NULL;inum = d->bInterfaceNumber; //接口编号for (i = 0; i < config->desc.bNumInterfaces; ++i) { //根据设备中获取的接口总数,遍历接口,匹配与当前inum接口编号相等的if (inums[i] == inum) {intfc = config->intf_cache[i]; //获取接口缓存信息break;}}if (!intfc || intfc->num_altsetting >= nalts[i])goto skip_to_next_interface_descriptor;/* Check for duplicate altsetting entries */asnum = d->bAlternateSetting;for ((i = 0, alt = &intfc->altsetting[0]);i < intfc->num_altsetting;(++i, ++alt)) {if (alt->desc.bAlternateSetting == asnum) {dev_warn(ddev, "Duplicate descriptor for config %d ""interface %d altsetting %d, skipping\n",cfgno, inum, asnum);goto skip_to_next_interface_descriptor;}}++intfc->num_altsetting;memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); //拷贝一个接口描述符/* Skip over any Class Specific or Vendor Specific descriptors;* find the first endpoint or interface descriptor */alt->extra = buffer;i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,USB_DT_INTERFACE, &n);alt->extralen = i;if (n > 0)dev_dbg(ddev, "skipped %d descriptor%s after %s\n",n, plural(n), "interface");buffer += i;size -= i;/* Allocate space for the right(?) number of endpoints */num_ep = num_ep_orig = alt->desc.bNumEndpoints; //获取接口的端点个数alt->desc.bNumEndpoints = 0;     /* Use as a counter */if (num_ep > USB_MAXENDPOINTS) {dev_warn(ddev, "too many endpoints for config %d interface %d ""altsetting %d: %d, using maximum allowed: %d\n",cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);num_ep = USB_MAXENDPOINTS;}if (num_ep > 0) {/* Can't allocate 0 bytes */len = sizeof(struct usb_host_endpoint) * num_ep; //分配端点个数的空间alt->endpoint = kzalloc(len, GFP_KERNEL);if (!alt->endpoint)return -ENOMEM;}/* Parse all the endpoint descriptors */n = 0;while (size > 0) {if (((struct usb_descriptor_header *) buffer)->bDescriptorType== USB_DT_INTERFACE)break;//解析端点 retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,num_ep, buffer, size);if (retval < 0)return retval;++n;buffer += retval;size -= retval;}if (n != num_ep_orig)dev_warn(ddev, "config %d interface %d altsetting %d has %d ""endpoint descriptor%s, different from the interface ""descriptor's value: %d\n",cfgno, inum, asnum, n, plural(n), num_ep_orig);return buffer - buffer0;skip_to_next_interface_descriptor:i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,USB_DT_INTERFACE, NULL);return buffer - buffer0 + i;
}

5.6.4 解析端点

     //解析端点  retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,num_ep, buffer, size);
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,int asnum, struct usb_host_interface *ifp, int num_ep,unsigned char *buffer, int size)
{unsigned char *buffer0 = buffer;struct usb_endpoint_descriptor *d;struct usb_host_endpoint *endpoint;int n, i, j, retval;d = (struct usb_endpoint_descriptor *) buffer; //获取端点描述符buffer += d->bLength;size -= d->bLength;if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)n = USB_DT_ENDPOINT_AUDIO_SIZE;else if (d->bLength >= USB_DT_ENDPOINT_SIZE)n = USB_DT_ENDPOINT_SIZE;else {dev_warn(ddev, "config %d interface %d altsetting %d has an ""invalid endpoint descriptor of length %d, skipping\n",cfgno, inum, asnum, d->bLength);goto skip_to_next_endpoint_or_interface_descriptor;}i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;if (i >= 16 || i == 0) {dev_warn(ddev, "config %d interface %d altsetting %d has an ""invalid endpoint with address 0x%X, skipping\n",cfgno, inum, asnum, d->bEndpointAddress);goto skip_to_next_endpoint_or_interface_descriptor;}/* Only store as many endpoints as we have room for */if (ifp->desc.bNumEndpoints >= num_ep)goto skip_to_next_endpoint_or_interface_descriptor;endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];++ifp->desc.bNumEndpoints;memcpy(&endpoint->desc, d, n);INIT_LIST_HEAD(&endpoint->urb_list);/* Fix up bInterval values outside the legal range. Use 32 ms if no* proper value can be guessed. *///根据不同速率的usb,计算时间i = 0;      /* i = min, j = max, n = default */j = 255;if (usb_endpoint_xfer_int(d)) {i = 1;switch (to_usb_device(ddev)->speed) {case USB_SPEED_SUPER:case USB_SPEED_HIGH:/* Many device manufacturers are using full-speed* bInterval values in high-speed interrupt endpoint* descriptors. Try to fix those and fall back to a* 32 ms default value otherwise. */n = fls(d->bInterval*8);if (n == 0)n = 9; /* 32 ms = 2^(9-1) uframes */j = 16;break;default:        /* USB_SPEED_FULL or _LOW *//* For low-speed, 10 ms is the official minimum.* But some "overclocked" devices might want faster* polling so we'll allow it. */n = 32;break;}} else if (usb_endpoint_xfer_isoc(d)) {i = 1;j = 16;switch (to_usb_device(ddev)->speed) {case USB_SPEED_HIGH:n = 9;        /* 32 ms = 2^(9-1) uframes */break;default:        /* USB_SPEED_FULL */n = 6;     /* 32 ms = 2^(6-1) frames */break;}}if (d->bInterval < i || d->bInterval > j) {dev_warn(ddev, "config %d interface %d altsetting %d ""endpoint 0x%X has an invalid bInterval %d, ""changing to %d\n",cfgno, inum, asnum,d->bEndpointAddress, d->bInterval, n);endpoint->desc.bInterval = n;}/* Some buggy low-speed devices have Bulk endpoints, which is* explicitly forbidden by the USB spec.  In an attempt to make* them usable, we will try treating them as Interrupt endpoints.*/if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&usb_endpoint_xfer_bulk(d)) {dev_warn(ddev, "config %d interface %d altsetting %d ""endpoint 0x%X is Bulk; changing to Interrupt\n",cfgno, inum, asnum, d->bEndpointAddress);endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;endpoint->desc.bInterval = 1;if (usb_endpoint_maxp(&endpoint->desc) > 8)endpoint->desc.wMaxPacketSize = cpu_to_le16(8); //低速设备端点字节为8个}/** Some buggy high speed devices have bulk endpoints using* maxpacket sizes larger than 64 under full-speed mode.  * Full speed HCDs may not* be able to handle that particular bug, so let's modify * the maxpacket size to make it work.*/if (to_usb_device(ddev)->speed == USB_SPEED_FULL&& usb_endpoint_xfer_bulk(d)) {if (usb_endpoint_maxp(&endpoint->desc) > 64)endpoint->desc.wMaxPacketSize = cpu_to_le16(64); //全速设备端点字节为64个}/** Some buggy high speed devices have bulk endpoints using* maxpacket sizes other than 512.  High speed HCDs may not* be able to handle that particular bug, so let's warn...*/if (to_usb_device(ddev)->speed == USB_SPEED_HIGH&& usb_endpoint_xfer_bulk(d)) {unsigned maxp;maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;  //高速设备端点字节数。。。if (maxp != 512)dev_warn(ddev, "config %d interface %d altsetting %d ""bulk endpoint 0x%X has invalid maxpacket %d\n",cfgno, inum, asnum, d->bEndpointAddress,maxp);}/* Parse a possible SuperSpeed endpoint companion descriptor */if (to_usb_device(ddev)->speed == USB_SPEED_SUPER)usb_parse_ss_endpoint_companion(ddev, cfgno,inum, asnum, endpoint, buffer, size);/* Skip over any Class Specific or Vendor Specific descriptors;* find the next endpoint or interface descriptor */endpoint->extra = buffer;i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,USB_DT_INTERFACE, &n);endpoint->extralen = i;retval = buffer - buffer0 + i;if (n > 0)dev_dbg(ddev, "skipped %d descriptor%s after %s\n",n, plural(n), "endpoint");return retval;skip_to_next_endpoint_or_interface_descriptor:i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,USB_DT_INTERFACE, NULL);return buffer - buffer0 + i;
}

端点部分主要工作是,根据不同速率的usb,传输不同的字节数,以及主机查询端点的间隔时间。至此分析完了枚举一个usb主机控制器的过程:设备-->N个配置-->N个接口-->N个端点,最后通过如下函数输出控制器的功能信息

6. 输出主机控制器信息

 announce_device(udev);
static void announce_device(struct usb_device *udev)
{dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",le16_to_cpu(udev->descriptor.idVendor),le16_to_cpu(udev->descriptor.idProduct));dev_info(&udev->dev,"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",udev->descriptor.iManufacturer,udev->descriptor.iProduct,udev->descriptor.iSerialNumber);show_string(udev, "Product", udev->product);show_string(udev, "Manufacturer", udev->manufacturer);show_string(udev, "SerialNumber", udev->serial);
}

系统启动时输出上述信息:

usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb1: Product: Nuvoton NUC970 EHCI Host Controller
usb usb1: Manufacturer: Linux 3.10.32 ehci_hcd
usb usb1: SerialNumber: nuc970-ehci

7. 总结

本文开始通过platform总线完成ehci设备和驱动的匹配调用探测函数ehci_nuc970_probe(),在该函数内部完成主机控制器的寄存器资源分配,然后注册一个hcd主机控制器(包括是否使用DMA池),然后增加主机控制器到usb总线上,然后注册一个根hub,期间包括最重要的部分,即设备枚举,在枚举的过程,先获取设备,通过设备获取接口,因接口长度未定,所以分两次读取接口信息,即第一次读取固定长度的接口信息,第二次根据第一次的描述符信息里的长度再读取整个接口信息,最后根据接口信息解析端点,最后将该主机控制器的根hub注册到usb总线上。

usb主机控制器ehci相关推荐

  1. linux usb xhci ehci,ehci和xhci有什么区别

    ehci和xhci有什么区别 EHCI(Enhanced Host Controller Interface)增强型主机控制器接口规范描述了一个通用串行总线(USB)2.0版的主机控制器的寄存器级接口 ...

  2. linux usb xhci ehci,ehci是什么意思?ehci和xhci的区别有哪些?

    很多新装机的朋友可能都有遇到过,Intel六代Skylake装机平台在安装Win7系统后,容易出现USB鼠标键盘不能用的情况,而导致这种现象的原因主要是由于ehci和xhci驱动的问题,因此今天小编要 ...

  3. usb主机控制器驱动

    1. tcc803x的usb2.0由两个模块组成:主从一体模块(dwc2)和主模块,主从一体模块里主部分集           成了echi.ohci两个主控制器,从部分集成了一个设备控制器.主模块里 ...

  4. s3c2440的USB主机控制器

    s3c2440提供了USB主机接口,它与OHCI v1.0完全兼容.要使用该功能,就必须熟悉OHCI v1.0规范:而要熟悉OHCI v1.0规范,那么还必须先熟悉USB v1.1协议.因此涉及到该部 ...

  5. USB接口(2)——USB控制器EHCI、OHCI、UHCI与usb hub芯片

    EHCI和OHCI,UHCI概念 USB控制器类型: OHCI.UHCI都是USB1.1的接口标准,而EHCI是对应USB2.0的接口标准,最新的xHCI是USB3.0的接口标准. OHCI( Ope ...

  6. linux设备驱动之USB主机控制器驱动分析

    http://www.cnblogs.com/sdphome/archive/2011/09/29/2195791.html 一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个 ...

  7. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html ------------------------------------------ ...

  8. usb控制linux关机,linux设备驱动之USB主机控制器驱动分析

    一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个分层模型.linux中的代码也是按照这个分层模型来设计的.具体的分为 usb设备,hub和主机控制器三部份.在阅读代码的时候, ...

  9. usb通用设备驱动注册

    1. usb设备驱动注册流程图 ...... 2. usb设备驱动注册 usb_register_device_driver(&usb_generic_driver, THIS_MODULE) ...

最新文章

  1. 学完这份「计算机基础知识」,你也可以来字节跳动(附下载)
  2. 温度对结构光深度的影响
  3. 补第四周作业总结——8 puzzle
  4. hdu-5781 ATM Mechine(dp+概率期望)
  5. 【干货分享】前端面试知识点锦集03(JavaScript篇)——附答案
  6. Git 2.19 对Diff、Branch和Grep等做了改进
  7. Struts2学习笔记(十九) 验证码
  8. PHP面试题 - MySQL数据库
  9. java rce漏洞原理_JXBrowser JavaScript-Java bridge 中的RCE漏洞
  10. python暴力破解六位密码(数字和大小写字母)
  11. android防丢器设计,基于单片机的防丢器设计
  12. 年审是当月还是当天_车辆年审到期是指当月吗?
  13. 印章识别--笛卡尔坐标系转换成极坐标系
  14. logrotate实现日志切割和清理(清晰易懂)
  15. TextView(文本框)
  16. Android系统的编舞者Choreographer
  17. 基于JAVA酒店信息管理计算机毕业设计源码+数据库+lw文档+系统+部署
  18. STM8S自学笔记-003 GPIO输出:点亮LED灯 and 跑马灯特效
  19. Python strip()方法:移除头尾字符
  20. 2023年四月份图形化一级打卡试题

热门文章

  1. 仿百度糯米团顶部搜索栏动画效果
  2. 数组扩容, 两个bytes[]数组合并
  3. 华润MMX链上云详解
  4. 一个批处理远离百度广告的骚扰
  5. 2020 网络流行语大全!你知道几个?
  6. 《TCP/IP协议族》:BOOTP协议
  7. 路上有你:《架构师成长之路》终结篇 (附新年礼物)
  8. Python - SQLAlchemy 子查询
  9. proxychains全局代理设置
  10. 关于Java中的WeakReference