1. 主机控制器流程图

usb主机控制器ehci_回调函数

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

usb主机控制器ehci_设备驱动_02


关于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()

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

usb主机控制器ehci_初始化_03


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

先贴出流程图

usb主机控制器ehci_回调函数_04

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总线上。