一、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函数中做j进一步的操作。

      

      

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, , );
s3c2410_usb_set_power(dev->dev.platform_data, , );
// 创建一个hcd结构体,并做一些设置
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
// 设置内存和IO资源的开始位置
hcd->rsrc_start = dev->resource[].start;
// 设置内存和IO资源的长度
hcd->rsrc_len = dev->resource[].end - dev->resource[].start + ;
// 申请一块内存资源
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[].start, IRQF_DISABLED);
if (retval != )
goto err_ioremap; return ; 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)) != ) {
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)) < )
goto err_register_bus;
// 创建一个root_hub
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, )) == 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, );
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < ) {
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}
hcd->rh_pollable = ; /* 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)) != )
goto err_register_root_hub; retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
if (retval < ) {
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 ; /* TODO: Add real matching code */
return ;
}
// 需要匹配的是接口的情况
  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 ;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv); id = usb_match_id(intf, usb_drv->id_table);
if (id)
return ;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return ;
}
return ;
}

      

  对于传入的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 = ; 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 == )
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
// 因此会执行到这里来配置操作
c = usb_choose_configuration(udev);
if (c >= ) {
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 ;
}

在这里我们可以看一下到底对着root->hub选择了什么配置,设置了什么配置

int usb_choose_configuration(struct usb_device *udev)
{
int i;
int num_configs;
int insufficient_power = ;
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 = ; i < num_configs; (i++, c++)) {
struct usb_interface_descriptor *desc = NULL; /* It's possible that a config has no interfaces! */
// 有可能一个配置没有接口,所以要做判断
if (c->desc.bNumInterfaces > )
// 取出配置下的第一个接口
desc = &c->intf_cache[]->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 * > 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 == && num_configs > && 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 > )
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 = -;
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 == || configuration == -)
configuration = ;
else {
for (i = ; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue ==
configuration) {
cp = &dev->config[i];
break;
}
}
}
if ((!cp && configuration != ))
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 == )
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 = ;
// 得到接口总数,并分配内存
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 >= )
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
} i = dev->bus_mA - cp->desc.bMaxPower * ;
if (i < )
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, ); /* 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 < ) {
mutex_unlock(&hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
}
// 设置配置
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, ),
USB_REQ_SET_CONFIGURATION, , configuration, ,
NULL, , USB_CTRL_SET_TIMEOUT);
if (ret < ) {
/* 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 = ; 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, ); /* 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[]; 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 = -;
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 = ; 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 != ) {
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 ;
}

简单看完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);
// hub 只支持6层嵌套,在前边那张usb系统拓扑图中hub接hub最多接6层
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 != ) &&
(desc->desc.bInterfaceSubClass != )) {
descriptor_error:
dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
return -EIO;
} //hub interface的endpoint数目为1,这里的数目没有包括ep0
if (desc->desc.bNumEndpoints != )
goto descriptor_error;
//获取端点描述符
endpoint = &desc->endpoint[].desc;
//判断端点是不是中断in类型的端点,
if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
// 在上述情况都满足的情况下才说明有一个hub存在
/* 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; // hub实体
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 = ; if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
// 配置hub
if (hub_configure(hub, endpoint) >= )
return ; hub_disconnect (intf);
return -ENODEV;
}

在hub_probe函数中创建了一个hub实例后,在hub_configure中配置这个hub。

static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
...
// 为hub开辟缓冲区
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
// 获取hub的描述符, 之前说过hub从本质上讲也是一种usb设备,只是其设备描述符与普通的usb设备不同
ret = get_hub_descriptor(hdev, hub->descriptor,
sizeof(*hub->descriptor)); hdev->maxchild = hub->descriptor->bNbrPorts;
hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
// 获取描述hub特性的信息
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
// 判断hub是不是混合设备
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
// 如果是混合设备就需要存储其每一个下行端口是否可以被移除
// 判断hub的电源管理类型
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
}
// 判断hub的过流保护类型
switch (wHubCharacteristics & HUB_CHAR_OCPM) {
} spin_lock_init (&hub->tt.lock);
INIT_LIST_HEAD (&hub->tt.clear_list);
INIT_WORK(&hub->tt.clear_work, hub_tt_work);
// 根据设备描述符中bDeviceProtocol字段信息设置hub->tt
switch (hdev->descriptor.bDeviceProtocol) {
}
// 设置usb->tt.think_time
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
}
// 判断是否支持指示
if (wHubCharacteristics & HUB_CHAR_PORTIND) { }
// 获得hub的状态
ret = usb_get_status(hdev, USB_RECIP_DEVICE, , &hubstatus);
// 对hub的电源管理
...........
ret = hub_hub_status(hub, &hubstatus, &hubchange);
...........
// 分配一个urb
hub->urb = usb_alloc_urb(, GFP_KERNEL);
// 填充这个urb
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
// 激活hub
hub_activate(hub, HUB_INIT);
}

  在hub_configure中填充了urb后,检测hub端口,如果状态发生变化,那么会调用hub_irq函数(这其中的过程需要后续的发现)

  

linux usb驱动记录(一)的更多相关文章

  1. linux usb驱动记录(二)

    三.usb设备的识别过程 在这里梳理一下上一篇博客中的内容:(这张图来自https://blog.csdn.net/lizuobin2/article/details/51931161) 上一篇博客刚 ...

  2. linux usb 驱动详解

    linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...

  3. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  4. Linux USB驱动框架分析【转】

    转自:http://blog.csdn.net/jeffade/article/details/7701431 Linux USB驱动框架分析(一) 初次接触和OS相关的设备驱动编写,感觉还挺有意思的 ...

  5. Linux USB驱动

    linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...

  6. Linux USB驱动框架分析(2)【转】

    转自:http://blog.chinaunix.net/uid-23046336-id-3243543.html   看了http://blog.chinaunix.net/uid-11848011 ...

  7. Linux USB驱动学习总结(二)---- USB设备驱动

    USB 设备驱动: 一.USB 描述符:(存在于USB 的E2PROM里面) 1.  设备描述符:struct usb_device_descriptor 2.  配置描述符:struct usb_c ...

  8. Linux USB驱动学习总结(一)---- USB基本概念及驱动架构

    USB,Universal Serial Bus(通用串行总线),是一个外部总线标准,用于规范电脑与外部设备的连接和通讯.是应用在PC领域的接口技术.USB接口支持设备的即插即用和热插拔功能.USB是 ...

  9. Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程

    1.usbmouse的定义:usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性 struct usb_mouse { ];///USB鼠标设备 ...

随机推荐

  1. iOS-Http断点续传

    下载LOFTER客户端IOS Http断点续传浅析 http实现断点续传的关键地方就是在httprequest中加入“Range”头. //设置Range头,值:bytes=x-y;x:开始字节,y: ...

  2. shell之判断文件是否存在

    #!/bin/sh myPath="/var/log/httpd/" myFile="/var /log/httpd/access.log" #这里的-x 参数 ...

  3. 单例Bean注册表接口SingletonBeanRegistry

    Github: SingletonBeanRegistry.java SingletonBeanRegistry package org.springframework.beans.factory.c ...

  4. Spring A 标签链接使用

    1.示例 <a th:href="@{/edit/{id}(id=${user.id})}">修改</a> 以@开头前面的{id}是占位符,后面的(id=$ ...

  5. 洛谷 题解 P1133 【教主的花园】

    $n<=10^5 $ O(n)算法 状态 dp[i][j][k]表示在第i个位置,种j*10的高度的树,且这棵树是否比相邻两棵树高 转移 dp[i][1][0]=max(dp[i-1][2][1 ...

  6. 包银消费CTO汤向军:消费金融大数据风控架构与实践

    1 业务架构 风控平台是相对独立的系统,信审的案件可以从借款端平台推过来,也可以从第三方平台推过来.信审案件到达风控平台后,自动创建工作流,根据风控流程处理各流程环节任务. •自动决策 风控流程自动处 ...

  7. win10配置Keras及GPU环境

    今天搭建了Keras深度学习的环境 详细记录一下 安装Anaconda3 Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项. Anac ...

  8. springboot问题

    1.导入数据库jar包后,配置好,发现报错 数据库连接不成功  加上@SpringBootApplication(exclude = DataSourceAutoConfiguration.class ...

  9. 三种SpringSecurity方法级别权限控制

    一 JSR-250注解 1.在pom.xml添加 <dependency> <groupId>javax.annotation</groupId> <arti ...

  10. Python【列表 字典 元组】

    列表列表用中括号[ ]把各种数据框起来,每一个数据叫作“元素”.每个元素之间都要用英文逗号隔开各种类型的数据(整数/浮点数/字符串)————————————————————————————从列表提取单 ...