usb主机控制器ehci

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_device
	hcd->self.bus_name = bus_name; //usb-hcd总线名称“nuc970-ehci”
	hcd->self.uses_dma = (dev->dma_mask != NULL); //主机控制器是否使用DMA,通过platfrom_device知道dev->dma_mask=0xffff

	init_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_RUNTIME
	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;
}

该函数内部主要分配了一个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;
	else
		hcd->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-bus
		goto err_register_bus;

	if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { //分配一个usb设备,作为root_hub
		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); //.start = ehci_run
	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_PM_RUNTIME
	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;
} 
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_hub
		dev_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));
			else
				dev->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_PM
	pm_runtime_set_autosuspend_delay(&dev->dev,
			usb_autosuspend_delay * 1000);
	dev->connect_time = jiffies;
	dev->active_duration = -jiffies;
#endif
	if (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_ADDRESS

	mutex_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->desc
	if (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_descriptor
			if (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总线上。





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值