一、linux 下的usb驱动框架
在linux系统中,usb驱动可以从两个角度去观察,一个是主机侧,一个是设备侧。linux usb 驱动的总体框架如下图所示:
从主机侧看usb驱动可分为四层:usb主机控制器硬件底层、usb主机控制器驱动、usb核心和usb设备驱动。
在主机侧要实现的驱动主要分为两类:usb主机控制器驱动和usb设备驱动。主机控制器驱动负责控制插入其中的usb设备,usb设备驱动主要负责usb设备和主机的通信。
usb核心向上为设备驱动提供编程接口,向下为usb控制器驱动提供编程口,维护整个usb设备信息,完成设备热插拔控制,总线数据传输控制。
可以看到这种设备驱动、核心层、主机控制器驱动这种三层结构的驱动框架,与之前分析过linux系统下i2c子系统的驱动架构有异曲同工之处。linux内核中将主机控制器的驱动和外设端的驱动分离,通过一个核心层将某种总线的协议进行抽象,外设端的驱动调用核心层API间接过渡到主机驱动传输函数的调用。
这里借助一张图来对比linux下,i2c、spi、usb三个子系统的相似之处。
这样一对比,就能比较清晰的分析usb主机控制器驱动与usb设备驱动。
二、usb总线驱动程序分析
主机控制器中重要的数据结构:
usb_hcd:描述了USB主机控制器驱动,包含主机控制器的信息
hc_driver:用于操作主机控制器的驱动,该结构体在usb_hcd 中
ohci_hcd: 是usb_hcd 结构体中的私有数据
在主机控制驱动中还是通过平台设备驱动来注册platform_device和platform_driver,然后用平台总线进行匹配,匹配成功之后调用probe函数,在probe函数中做进一步的操作。
probe函数:
static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, struct platform_device *dev) { struct usb_hcd *hcd = NULL; int retval; // dev->dev.platform_data == NULL,因此这里不会不用set_power s3c2410_usb_set_power(dev->dev.platform_data, 1, 1); s3c2410_usb_set_power(dev->dev.platform_data, 2, 1); // 创建一个hcd结构体,并做一些设置 hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx"); if (hcd == NULL) return -ENOMEM; // 设置内存和IO资源的开始位置 hcd->rsrc_start = dev->resource[0].start; // 设置内存和IO资源的长度 hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; // 申请一块内存资源 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_err(&dev->dev, "request_mem_region failed\n"); retval = -EBUSY; goto err_put; } // 在clock.c中找到"usb-host"对应的clk结构体 clk = clk_get(&dev->dev, "usb-host"); if (IS_ERR(clk)) { dev_err(&dev->dev, "cannot get usb-host clock\n"); retval = -ENOENT; goto err_mem; } // 在clock.c中找到"usb-bus-host"对应的clk结构体 usb_clk = clk_get(&dev->dev, "usb-bus-host"); if (IS_ERR(usb_clk)) { dev_err(&dev->dev, "cannot get usb-bus-host clock\n"); retval = -ENOENT; goto err_clk; } // 使能时钟 使能过流检查 s3c2410_start_hc(dev, hcd); // io端口重映射 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&dev->dev, "ioremap failed\n"); retval = -ENOMEM; goto err_ioremap; } // 初始化ohci_hcd 结构体ohci->next_statechange = jiffies ohci_hcd_init(hcd_to_ohci(hcd)); // 这个函数下边分析 一个usb主机控制器对应一个usb_hcd,对应一条usb总线,集成一个root_hub retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); if (retval != 0) goto err_ioremap; return 0; err_ioremap: s3c2410_stop_hc(dev); iounmap(hcd->regs); clk_put(usb_clk); err_clk: clk_put(clk); err_mem: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_put: usb_put_hcd(hcd); return retval; }
在probe函数中主要的任务如下:
(1)创建一个usb_hcd结构体===>和i2c控制器驱动中的中分配一个i2c_adapter一样
(2)设置这个这个usb_hcd结构体(设置操作主机控制器的hc_driver)===>设置i2c_adapter结构体(设置操作i2c_adapter的transfer函数)
(3)从platform_device中获取到硬件资源,进行内存映射
(4)使能时钟
(5)usb_add_hcd
usb_create_hcd函数:
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, const char *bus_name) { struct usb_hcd *hcd; hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); dev_set_drvdata(dev, hcd); ... // 初始化hcd下边的usb_bus,为后边将其加入到usb_bus 中做准备: //bus->devnum_next = 1; bus->root_hub = NULL; bus->busnum = -1; //bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; //bus->bandwidth_isoc_reqs = 0; usb_bus_init(&hcd->self); hcd->self.controller = dev; hcd->self.bus_name = bus_name; hcd->self.uses_dma = (dev->dma_mask != NULL); // 初始定时器用来轮询控制器的root_hub的状态改变 init_timer(&hcd->rh_timer); // 注册定时器中断服务函数 hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; #ifdef CONFIG_USB_SUSPEND INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif mutex_init(&hcd->bandwidth_mutex); // 给hcd添加主机控制器驱动函数 driver==ohci_s3c2410_hc_driver hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; return hcd; }
usb_add_hcd函数:一个usb主机控制器对应一条usb总线,集成一个root_hub,对应一个usb_hcd。
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { // 初始化缓存池 if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); return retval; } //设置hcd下usb_bus的busnum并将其挂到usb_bus_list这个链表中 hcd->self 在usb_create_hcd 中已经初始化了 (注册完之后hcd->self.busnum = 1) if ((retval = usb_register_bus(&hcd->self)) < 0) goto err_register_bus; // 创建一个root_hub if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { dev_err(hcd->self.controller, "unable to allocate root hub\n"); retval = -ENOMEM; goto err_allocate_root_hub; } // 将上边分配好的usb_device挂在主机控制的usb_bus下 hcd->self.root_hub = rhdev; // 根据ohci_s3c2410_hc_driver(HCD_USB11 | HCD_MEMORY,)下的flag 选择 root_hub 的speed switch (hcd->driver->flags & HCD_MASK) { 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: goto err_set_rh_speed; } device_init_wakeup(&rhdev->dev, 1); 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 */ // 接下来使能中断 // 接下来执行主机控制器的启动函数 /* starting here, usbcore will pay attention to this root hub */ rhdev->bus_mA = min(500u, hcd->power_budget); 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); return retval; ... }
在usb_add_hcd中,最主要干的一件事是创建一个root_hub,这个root_hub的数据类型是一个usb_device,并将这个root_hub注册到usb总线中。
这里大概解释一下什么是root_hub。在我们的电脑上通常有几个usb端口,这些端口可以用来连接一个普通的usb设备,或者一个hub,hub是一个usb设备,可以用来扩展连接usb设备的端口数量。通常情况下主机控制器的物理端口由一个虚拟的root_hub来管理。这个hub是主机控制器的设备驱动虚拟的,用来统一管理总线拓扑。用一张图说明usb系统的拓扑结构。
register_root_hub函数的调用太复杂了,这里先抽象出其函数调用过程如下:
为了分析清楚root_hub下的dev到底与总线上的哪一个device_driver匹配需要分析usb总线上的match函数
static int usb_device_match(struct device *dev, struct device_driver *drv) { // 需要匹配的是usb_device时的情况 if (is_usb_device(dev)) { /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) return 0; /* TODO: Add real matching code */ return 1; } // 需要匹配的是接口的情况 else if (is_usb_interface(dev)) { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; /* device drivers never match interfaces */ if (is_usb_device_driver(drv)) return 0; intf = to_usb_interface(dev); usb_drv = to_usb_driver(drv); id = usb_match_id(intf, usb_drv->id_table); if (id) return 1; id = usb_match_dynamic_id(intf, usb_drv); if (id) return 1; } return 0; }
对于传入的match函数的dev其dev->type是usb_device_type(在分配usb_alloc_dev中已经设置了)。
现在看usb总线上有哪些device_driver:在/core/usb.c中我们可以看到已经注册了两个usb_driver结构体 usbfs_driver和hub_driver
一个usb_device_driver结构体 usb_generic_driver
(这里跳转的比较突然,这三个结构体的注册是在usb_init函数中进行的)
所以match函数中传入的dev会和这三个已经注册到usb_bus上的device_driver进行匹配,这里看哪一个usb_driver下的device_driver会匹配成功,也就是看usbdrv_wrap->for_devices的值,这个值需要在usb_register函数中查看,这里给出结果。其实从driver的名字就可以看出只有usb_device_driver (usb设备驱动)是能够和device(设备)进行匹配的。
因此root_hub->dev会和usb_generic_driver->drvwrap.driver进行匹配,匹配完成之后会执行probe函数。那么这个probe函数有是哪一个呢?probe函数肯定是usb_generic_driver->drvwrap.driver.probe
这个函数在usb_register_device_driver(&usb_generic_driver, THIS_MODULE)中进行了设置usb_generic_driver->drvwrap.driver.probe = usb_probe_device
usb_probe_device函数的分析:
static int usb_probe_device(struct device *dev) { // 这里通过container_of 找到usb_device_driver 和 usb_device // 注意在really_probe 函数中已经将dev->driver 挂上了 device_driver ,所以这个地方才能找到usb_device_driver struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); struct usb_device *udev = to_usb_device(dev); int error = 0; dev_dbg(dev, "%s\n", __func__); /* TODO: Add real matching code */ /* The device should always appear to be in use * unless the driver suports autosuspend. */ if (!udriver->supports_autosuspend) error = usb_autoresume_device(udev); // 程序执行到这里可以看到饶了一大圈的probe函数就是usb_generic_driver->probe函数 也就是generic_probe 函数
if (!error) error = udriver->probe(udev); return error; }
因此接下分析generic_probe 函数:
static int generic_probe(struct usb_device *udev) { int err, c; if (usb_device_is_owned(udev)) ; /* Don't configure if the device is owned */ // 在创建root_hub 时分配usb_device时已经设置了dev->authorized = 1 else if (udev->authorized == 0) dev_err(&udev->dev, "Device is not authorized for usage\n"); else { // 因此会执行到这里来配置操作 c = usb_choose_configuration(udev); if (c >= 0) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d\n", c, err); } } } /* USB device state == configured ... usable */ usb_notify_add_device(udev); return 0; }
在这里我们可以看一下到底对着root->hub选择了什么配置,设置了什么配置
int usb_choose_configuration(struct usb_device *udev) { int i; int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; best = NULL; // root_hub下的一些与配置有关的东西在usb_new_device中都读取出来然后放到了root_hub下的config中 c = udev->config; // 有多少项配置 num_configs = udev->descriptor.bNumConfigurations; // 遍历所有的配置项 for (i = 0; i < num_configs; (i++, c++)) { struct usb_interface_descriptor *desc = NULL; /* It's possible that a config has no interfaces! */ // 有可能一个配置没有接口,所以要做判断 if (c->desc.bNumInterfaces > 0) // 取出配置下的第一个接口 desc = &c->intf_cache[0]->altsetting->desc; /* * HP's USB bus-powered keyboard has only one configuration * and it claims to be self-powered; other devices may have * similar errors in their descriptors. If the next test * were allowed to execute, such configurations would always * be rejected and the devices would not work as expected. * In the meantime, we run the risk of selecting a config * that requires external power at a time when that power * isn't available. It seems to be the lesser of two evils. * * Bugzilla #6448 reports a device that appears to crash * when it receives a GET_DEVICE_STATUS request! We don't * have any other way to tell whether a device is self-powered, * but since we don't use that information anywhere but here, * the call has been removed. * * Maybe the GET_DEVICE_STATUS call and the test below can * be reinstated when device firmwares become more reliable. * Don't hold your breath. */ #if 0 /* Rule out self-powered configs for a bus-powered device */ if (bus_powered && (c->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)) continue; #endif /* * The next test may not be as effective as it should be. * Some hubs have errors in their descriptor, claiming * to be self-powered when they are really bus-powered. * We will overestimate the amount of current such hubs * make available for each port. * * This is a fairly benign sort of failure. It won't * cause us to reject configurations that we should have * accepted. */ /* Rule out configs that draw too much bus current */ if (c->desc.bMaxPower * 2 > udev->bus_mA) { insufficient_power++; continue; } /* When the first config's first interface is one of Microsoft's * pet nonstandard Ethernet-over-USB protocols, ignore it unless * this kernel has enabled the necessary host side driver. * But: Don't ignore it if it's the only config. */ if (i == 0 && num_configs > 1 && desc && (is_rndis(desc) || is_activesync(desc))) { #if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) continue; #else best = c; #endif } /* From the remaining configs, choose the first one whose * first interface is for a non-vendor-specific class. * Reason: Linux is more likely to have a class driver * than a vendor-specific driver. */ else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && (desc && desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) { best = c; break; } /* If all the remaining configs are vendor-specific, * choose the first one. */ else if (!best) best = c; } if (insufficient_power > 0) dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); if (best) { i = best->desc.bConfigurationValue; dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { i = -1; dev_warn(&udev->dev, "no configuration chosen from %d choice%s\n", num_configs, plural(num_configs)); } return i; // 这里返回了一个系统觉得合适的配置项的编号 }
返回的这个最好的配置项的编号传入到usb_set_configuration,猜测要对此项配置进行设置。
int usb_set_configuration(struct usb_device *dev, int configuration) { int i, ret; struct usb_host_config *cp = NULL; struct usb_interface **new_interfaces = NULL; struct usb_hcd *hcd = bus_to_hcd(dev->bus); int n, nintf; // 首先根据选择的配置项的编号 找到相应的配置 if (dev->authorized == 0 || configuration == -1) configuration = 0; else { for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; break; } } } if ((!cp && configuration != 0)) return -EINVAL; /* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0, * we will accept it as a correctly configured state. * Use -1 if you really want to unconfigure the device. */ //当configuration==0 时发出警告,0是无效的配置,但仍然认为他是正确的 if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); /* Allocate memory for new interfaces before doing anything else, * so that if we run out then nothing will have changed. */ n = nintf = 0; // 得到接口总数,并分配内存 if (cp) { nintf = cp->desc.bNumInterfaces; new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_NOIO); if (!new_interfaces) { dev_err(&dev->dev, "Out of memory\n"); return -ENOMEM; } for (; n < nintf; ++n) { new_interfaces[n] = kzalloc( sizeof(struct usb_interface), GFP_NOIO); if (!new_interfaces[n]) { dev_err(&dev->dev, "Out of memory\n"); ret = -ENOMEM; free_interfaces: while (--n >= 0) kfree(new_interfaces[n]); kfree(new_interfaces); return ret; } } i = dev->bus_mA - cp->desc.bMaxPower * 2; if (i < 0) dev_warn(&dev->dev, "new config #%d exceeds power " "limit by %dmA\n", configuration, -i); } /* Wake up the device so we can send it the Set-Config request */ // 配置前唤醒设备 ret = usb_autoresume_device(dev); if (ret) goto free_interfaces; /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ if (dev->state != USB_STATE_ADDRESS) usb_disable_device(dev, 1); /* Skip ep0 */ /* Get rid of pending async Set-Config requests for this device */ cancel_async_set_config(dev); /* Make sure we have bandwidth (and available HCD resources) for this * configuration. Remove endpoints from the schedule if we're dropping * this configuration to set configuration 0. After this point, the * host controller will not allow submissions to dropped endpoints. If * this call fails, the device state is unchanged. */ mutex_lock(&hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { mutex_unlock(&hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } // 设置配置 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (ret < 0) { /* All the old state is gone, so what else can we do? * The device is probably useless now anyway. */ cp = NULL; } dev->actconfig = cp; if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); mutex_unlock(&hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } mutex_unlock(&hcd->bandwidth_mutex); usb_set_device_state(dev, USB_STATE_CONFIGURED); /* Initialize the new interface structures and the * hc/hcd/usbcore interface/endpoint state. */ // 接下来设置这个配置的接口并将接口注册到usb总线下 for (i = 0; i < nintf; ++i) { struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; intf->intf_assoc = find_iad(dev, cp, i); kref_get(&intfc->ref); alt = usb_altnum_to_altsetting(intf, 0); /* No altsetting 0? We'll assume the first altsetting. * We could use a GetInterface call, but if a device is * so non-compliant that it doesn't have altsetting 0 * then I wouldn't trust its reply anyway. */ if (!alt) alt = &intf->altsetting[0]; intf->cur_altsetting = alt; usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; intf->dev.dma_mask = dev->dev.dma_mask; INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); intf->minor = -1; device_initialize(&intf->dev); pm_runtime_no_callbacks(&intf->dev); dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); } kfree(new_interfaces); if (cp->string == NULL && !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) cp->string = usb_cache_string(dev, cp->desc.iConfiguration); /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() * routines may install different altsettings and may * claim() any interfaces not yet bound. Many class drivers * need that: CDC, audio, video, etc. */ for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; dev_dbg(&dev->dev, "adding %s (config #%d, interface %d)\n", dev_name(&intf->dev), configuration, intf->cur_altsetting->desc.bInterfaceNumber); device_enable_async_suspend(&intf->dev); ret = device_add(&intf->dev); if (ret != 0) { dev_err(&dev->dev, "device_add(%s) --> %d\n", dev_name(&intf->dev), ret); continue; } create_intf_ep_devs(intf); } usb_autosuspend_device(dev); return 0; }
简单看完usb_set_configuration之后,看一下此刻在/sys/bus/usb/device 路径存在的设备如下:
usb1这个设备是在之前一次register_root_hub中的device_add中添加的,而1-0:1.0是在sub_set_configuration中device_add添加的,对应表示 总线号-设备路径:配置号-接口号
1-0:1.0表示usb控制器1下的usb_hub下的1号配置的0号接口
注意到这里有出现了device_add,这又是一长串的函数调用,但是还是向总线注册设备、与总线上的device_driver进行匹配,匹配成功之后执行probe函数。所以在这里又得分析usb_device_match函数和probe,重点是这个probe函数执行的是哪一个函数?
之前分析过usb_device_match函数,这次传入到usb_device_match中的dev是一个接口类型的,所以应该执行usb_device_match中的第二条分支,通过id来进行匹配。这次与root_hub下的接口匹配的应该是hub_driver->drvwrap.driver,通过静态id_table的匹配成功之后应该执行usb_probe_interface这个函数。
static int usb_probe_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); struct usb_device *udev = interface_to_usbdev(intf); const struct usb_device_id *id; .... // 执行到这里又回到了hub_driver下的hub_probe函数 // 可以看到这里的probe函数的调用是一层套一层的,但最终都会执行到driver下的probe函数 error = driver->probe(intf, id); if (error) goto err;
.... }
hub_probe函数:
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_host_interface *desc; struct usb_endpoint_descriptor *endpoint; struct usb_device *hdev; struct usb_hub *hub; desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); /* Hubs have proper suspend/resume support */ usb_enable_autosuspend(hdev); if (hdev->level == MAX_TOPO_LEVEL) { dev_err(&intf->dev, "Unsupported bus topology: hub nested too deep\n"); return -E2BIG; } #ifdef CONFIG_USB_OTG_BLACKLIST_HUB if (hdev->parent) { dev_warn(&intf->dev, "ignoring external hub\n"); return -ENODEV; } #endif /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) { descriptor_error: dev_err (&intf->dev, "bad descriptor, ignoring hub\n"); return -EIO; } //hub interface的endpoint数目为1,这里的数目没有包括ep0 if (desc->desc.bNumEndpoints != 1) goto descriptor_error; //获取端点描述符 endpoint = &desc->endpoint[0].desc; //判断端点是不是in方向的端点, if (!usb_endpoint_is_int_in(endpoint)) goto descriptor_error; /* We found a hub */ dev_info (&intf->dev, "USB hub found\n"); // 分配一个usb_hub hub = kzalloc(sizeof(*hub), GFP_KERNEL); if (!hub) { dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n"); return -ENOMEM; } //初始化引用计数 kref_init(&hub->kref); INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->init_work, NULL); usb_get_intf(intf); usb_set_intfdata (intf, hub); intf->needs_remote_wakeup = 1; if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs++; // 配置hub if (hub_configure(hub, endpoint) >= 0) return 0; hub_disconnect (intf); return -ENODEV; }
程序接下来的执行过程以及框架的分析过程在下一篇博客中分析