linux usb驅動記錄(一)


一、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, 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);
    // 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 != 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;
        // 在上述情況都滿足的情況下才說明有一個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 = 1;

    if (hdev->speed == USB_SPEED_HIGH)
        highspeed_hubs++;
        // 配置hub
    if (hub_configure(hub, endpoint) >= 0)
        return 0;

    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, 0, &hubstatus);
        //  對hub的電源管理
        ...........
    ret = hub_hub_status(hub, &hubstatus, &hubchange);
        ...........
        //  分配一個urb
    hub->urb = usb_alloc_urb(0, 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函數(這其中的過程需要后續的發現)

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM