Linux USB 3.0驱动分析(五)——USB Hub代码分析

本文分析的是linux-5.4.3


一、Linux 下USB Hub热插拔处理

1、 Linux下USB HUB的驱动的实现和分析:

       在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化

       代码路径:drivers\usb\core\hub.c

       在usb_hub_init函数中完成了注册hub驱动,并且利用函数alloc_workqueue创建一个工作队列

       USB设备是热插拔,这就和PCI设备不同,PCI设备是在系统启动的时候都固定了,因此PCI设备只需要初始化进行枚举就可以了,采用递归算法即可。而USB设备需要热插拔,因此在hub_probe函数中调用hub_configure函数来配置hub,在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb,利用usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。

       每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq, 在该函数中置位event_bits,运行工作队列。进入hub_event函数,该函数用来处理端口变化的事件。然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。


2、软件层次分析-初始化

这里我们先讲讲USB热插拔事件的处理工作。hub_event工作,运行于工作队列

hub_event来检查usb port的事件通知HCD和usb core,然后做相应的处理。

驱动目录drivers/usb/*
usb/serial  usb 串行设备驱动 (例如usb 3G卡、蓝牙等)
usb/storage  usb 大储量磁盘驱动(u盘)  
usb/host usb host usb主机控制器驱动(嵌入式otg:dwc_otg)
usb/core   usb 核心一些处理代码,所有的驱动相关处理都在这里,也都注册到它里面。
usb/usb-skeleton.c 经典的usb客户驱动框架,可以参考

当然还有其他这里不再说明。

这里我们主要分析khub的工作原理: 硬件层次是hub的工作,如何和host及其设备间通信及相应事件

[usb/core/hub.c ]

int usb_hub_init(void)
{
    if (usb_register(&hub_driver) < 0) { //注册usb HUB设备
        printk(KERN_ERR "%s: can't register hub driver\n",
            usbcore_name);
        return -1;
    }

    /*
     * The workqueue needs to be freezable to avoid interfering with
     * USB-PERSIST port handover. Otherwise it might see that a full-speed
     * device was gone before the EHCI controller had handed its port
     * over to the companion full-speed controller.
     */
    /* 工作队列需要是可冻结的,以避免干扰usb持续的端口切换。
    否则它可能会看到全速设备在EHCI控制器把端口交给全速控制器之前就消失了 */
    hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);  //创建工作队列
    if (hub_wq)
        return 0;
}


然后进入hub的probe函数,主要是一些工作的初始化和hub的配置,我这里化简了

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    desc = intf->cur_altsetting;
    hdev = interface_to_usbdev(intf);

    /*
     * Hubs have proper suspend/resume support, except for root hubs
     * where the controller driver doesn't have bus_suspend and
     * bus_resume methods.
     */
    if (hdev->parent) {        /* normal device */
        usb_enable_autosuspend(hdev);
    } else {            /* root hub */ //根节点没有bus_suspend和bus_resume方法
        const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
        if (drv->bus_suspend && drv->bus_resume)
            usb_enable_autosuspend(hdev);
    }
 
    hub = kzalloc(sizeof(*hub), GFP_KERNEL); //分配usb_hub结构体
    if (!hub)
        return -ENOMEM;

    INIT_DELAYED_WORK(&hub->leds, led_work); //用于hub led闪烁的指示灯
    INIT_DELAYED_WORK(&hub->init_work, NULL);
    INIT_WORK(&hub->events, hub_event); //用于处理hub的事件
  
    if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) //设置hub的端点0
        return 0;
}

hub_configure配置hub,包括不同的hub的判断和配置,但Linux认为最多只能31个接口,里面比较复杂,这里也化简了

static int hub_configure(struct usb_hub *hub,
    struct usb_endpoint_descriptor *endpoint)
{
    ret = hub_hub_status(hub, &hubstatus, &hubchange); //获取hub的状态,主要是通过usb_control_msg进行通信
    if (ret < 0) {
        message = "can't get hub status";
        goto fail;
    }

    hub->urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb
    if (!hub->urb) {
        ret = -ENOMEM;
        goto fail;
    }
	/* UHCI必须要知道HUB的端口的一些连接状态,因此,需要HUB周期性的上报它的端口连接状态.这个URB就是用来做这个用途的.
    UHCI周期性的发送IN方向中断传输传输给HUB.HUB就会通过这个URB将端口信息发送给UHCI.那这个轮询周期是多长呢?
    根据我们之前分析的UHCI的知识,它的调度周期是由endpoint的bInterval 字段所决定的.*/
    usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
        hub, endpoint->bInterval); //填充urb,完成之后调用hub_irq函数

    for (i = 0; i < maxchild; i++) {
        ret = usb_hub_create_port_device(hub, i + 1); //创建hub的端点设备,比如/sys/devices/platform/soc@0/38100000.usb/xhci-hcd.0.auto/usb1/1-0:1.0/usb1-port1
        if (ret < 0) {
            dev_err(hub->intfdev,
                "couldn't create port%d device.\n", i + 1);
            break;
        }
    }
    /* Update the HCD's internal representation of this hub before hub_wq
     * starts getting port status changes for devices under the hub.
     */
	//在hub_wq之前,更新这个集线器的HCD内部数据,开始为集线器下的设备获取端口状态变化。
    if (hcd->driver->update_hub_device) {
        ret = hcd->driver->update_hub_device(hcd, hdev,
                &hub->tt, GFP_KERNEL);
        if (ret < 0) {
            message = "can't update HCD hub info";
            goto fail;
        }
    }

    hub_activate(hub, HUB_INIT);
    return 0;
}

继续分析hub_activate,主要是启动hub,我们这里传入的参数是HUB_INIT

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
    /* Continue a partial initialization */
    if (type == HUB_INIT2 || type == HUB_INIT3) {
        device_lock(&hdev->dev);
        /* Was the hub disconnected while we were waiting? */
        if (hub->disconnected)
            goto disconnected;
        if (type == HUB_INIT2)
            goto init2;
        goto init3;
    }

        if (type == HUB_INIT) {
            delay = hub_power_on_good_delay(hub); //上电后延时,使hub稳定
            hub_power_on(hub, false); //对所有的端点上电,usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, NULL, 0, 1000);
            INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
            queue_delayed_work(system_power_efficient_wq,
                    &hub->init_work,
                    msecs_to_jiffies(delay)); //进入初始化的第二阶段hub_init_func2,也就是下面的 init2:

 init2:
    /*
     * Check each port and set hub->change_bits to let hub_wq know
     * which ports need attention.
     */
    for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
        struct usb_port *port_dev = hub->ports[port1 - 1];
        struct usb_device *udev = port_dev->child;
        u16 portstatus, portchange;
        portstatus = portchange = 0;
        status = hub_port_status(hub, port1, &portstatus, &portchange); //获取端口的状态,
		..........//后面进行一系列判断,包括usb2.0,3.0等。并且set_bit(port1, hub->change_bits);

    /* If no port-status-change flags were set, we don't need any
     * debouncing.  If flags were set we can try to debounce the
     * ports all at once right now, instead of letting hub_wq do them
     * one at a time later on.
     *
     * If any port-status changes do occur during this delay, hub_wq
     * will see them later and handle them normally.
     */
    if (need_debounce_delay) { //用于消抖
        delay = HUB_DEBOUNCE_STABLE;
        /* Don't do a long sleep inside a workqueue routine */
        if (type == HUB_INIT2) {
            INIT_DELAYED_WORK(&hub->init_work, hub_init_func3); //进行第三个阶段init3:
            queue_delayed_work(system_power_efficient_wq,
                    &hub->init_work,
                    msecs_to_jiffies(delay));
            device_unlock(&hdev->dev);
            return;        /* Continues at init3: below */
    }

 init3:
    status = usb_submit_urb(hub->urb, GFP_NOIO); //提交urb,等执行完成就会回调hub_irq
    if (status < 0)
        dev_err(hub->intfdev, "activate --> %d\n", status);
    if (hub->has_indicators && blinkenlights) //如果有指示灯,点亮
        queue_delayed_work(system_power_efficient_wq,
                &hub->leds, LED_CYCLE_PERIOD);

    /* Scan all ports that need attention */
    kick_hub_wq(hub); //主要是queue_work(hub_wq, &hub->events),也就是把hub_event加入工作队列,开始运行
}

我们来看看hub_event,前面int2的时候有设置hub->change_bits,这里会进行处理

static void hub_event(struct work_struct *work)
{
    if (hub->error) {
        dev_dbg(hub_dev, "resetting for error %d\n", hub->error);
        ret = usb_reset_device(hdev); //警告接口驱动程序并执行USB端口重置
    }

    /* deal with port status changes */
    for (i = 1; i <= hdev->maxchild; i++) {
        struct usb_port *port_dev = hub->ports[i - 1];
        if (test_bit(i, hub->event_bits)
                || test_bit(i, hub->change_bits)
                || test_bit(i, hub->wakeup_bits)) { //如果这几个条件都满足,就port_event
            /*
             * The get_noresume and barrier ensure that if
             * the port was in the process of resuming, we
             * flush that work and keep the port active for
             * the duration of the port_event().  However,
             * if the port is runtime pm suspended
             * (powered-off), we leave it in that state, run
             * an abbreviated port_event(), and move on.
             */
            pm_runtime_get_noresume(&port_dev->dev);
            pm_runtime_barrier(&port_dev->dev);
            usb_lock_port(port_dev);
            port_event(hub, i); //处理事件
            usb_unlock_port(port_dev);
            pm_runtime_put_sync(&port_dev->dev);
        }
    }
}

我们再看看 port_event做了什么。

来看一下,什么情况下, hub_port_connect_change才会被设为1.
1:端口在hub->change_bits中被置位.搜索整个代码树,发生在设置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手动将端口禁用,会将对应位置1.
2:hub上没有这个设备树上没有这个端口上的设备.但显示端口已经连上了设备
3:hub这个端口上的连接发生了改变,从端口有设备连接变为无设备连接,或者从无设备连接变为有设备连接.
4:hub的端口变为了disable,此时这个端口上连接了设备,但被显示该端口已经变禁用,需要将connect_change设为1.
5:端口状态从SUSPEND变成了RESUME,远程唤醒端口上的设备失败,就需要将connect_change设为1.

static void port_event(struct usb_hub *hub, int port1)
        __must_hold(&port_dev->status_lock)
{
    if (hub_port_status(hub, port1, &portstatus, &portchange) < 0) //确认端口改变了
        return;

    if (connect_change)
        hub_port_connect_change(hub, port1, portstatus, portchange); //处理端口改变的情况
}

调用hub_port_connect_change再调用hub_port_connect

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
        u16 portchange)
{
    /* Disconnect any existing devices under this port */
    if (udev) {
        if (hcd->usb_phy && !hdev->parent) //断开该端口设备的连接:如果是root hub,挂接在控制器上的,则断开该端口下的设备。
            usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
        usb_disconnect(&port_dev->child);
    }

    for (i = 0; i < SET_CONFIG_TRIES; i++) {
        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         */
        // 分配usb设备内存并初始化bus、type、group、设备在系统中的路径(dev->path)、ep0的属性并设置设备状态为attached。
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        if (!udev) {
            dev_err(&port_dev->dev,
                    "couldn't allocate usb_device\n");
            goto done;
        }
        usb_set_device_state(udev, USB_STATE_POWERED); //设置为 power状态,并设置电源等。
        udev->bus_mA = hub->mA_per_port;
        udev->level = hdev->level + 1;
        udev->wusb = hub_is_wusb(hub);

        /* Devices connected to SuperSpeed hubs are USB 3.0 or later */
        if (hub_is_superspeed(hub->hdev)) //判断速度是否为高速
            udev->speed = USB_SPEED_SUPER;
        else
            udev->speed = USB_SPEED_UNKNOWN;

        choose_devnum(udev); //获取设备号,在usbfs中,设备号被用作文件名.
        if (udev->devnum <= 0) {
            status = -ENOTCONN;    /* Don't retry */
            goto loop;
        }

        /* reset (non-USB 3.0 devices) and get descriptor */
        usb_lock_port(port_dev);
        status = hub_port_init(hub, udev, port1, i); //初始化设备,设置地址,读取设备描述符
        usb_unlock_port(port_dev);

        /* Run it through the hoops (find a driver, etc) */
        if (!status) {
            status = usb_new_device(udev); //执行初始设备设置
            if (status) {
                mutex_lock(&usb_port_peer_mutex);
                spin_lock_irq(&device_state_lock);
                port_dev->child = NULL;
                spin_unlock_irq(&device_state_lock);
                mutex_unlock(&usb_port_peer_mutex);
            } else {
                if (hcd->usb_phy && !hdev->parent)
                    usb_phy_notify_connect(hcd->usb_phy,
                            udev->speed);
            }
        }
    }
}

我们重点分析一下usb_new_device,初始化设备

int usb_new_device(struct usb_device *udev)
{
    /* 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); //打印出来,需要内核打开CONFIG_USB_ANNOUNCE_NEW_DEVICES宏

    /* 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);
        int port1 = udev->portnum;
        struct usb_port    *port_dev = hub->ports[port1 - 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;
        }
        if (!test_and_set_bit(port1, hub->child_usage_bits))
            pm_runtime_get_sync(&port_dev->dev);
    }

    (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); //创建端点设备节点,/sys/devices/platform/soc@0/38100000.usb/xhci-hcd.0.auto/usb1/ep_00
}



3.软件层次分析-Hub部分热拔插

前面主要是初始化的操作,也会去识别开机的时候已经插入的设备,如果是开机之后插入的设备,又是什么流程呢

前面是每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq

static void hub_irq(struct urb *urb)
{
    switch (status) {
    case -ENOENT:        /* synchronous unlink */
    case -ECONNRESET:    /* async unlink */
    case -ESHUTDOWN:    /* hardware going away */
        return;
    default:        /* presumably an error */
        /* Cause a hub reset after 10 consecutive errors */
        dev_dbg(hub->intfdev, "transfer --> %d\n", status);
        if ((++hub->nerrors < 10) || hub->error)
            goto resubmit;
        hub->error = status;
        /* FALL THROUGH */
    /* let hub_wq handle things */
    case 0:            /* we got data:  port status changed */
        bits = 0;
        for (i = 0; i < urb->actual_length; ++i)
            bits |= ((unsigned long) ((*hub->buffer)[i]))
                    << (i*8);
        hub->event_bits[0] = bits; //置位event_bits /* status change bitmask */
        break;
    }
    hub->nerrors = 0;

    /* Something happened, let hub_wq figure it out */
    kick_hub_wq(hub); //

resubmit:
    hub_resubmit_irq_urb(hub);  //主要是queue_work(hub_wq, &hub->events),也就是把hub_event加入工作队列,开始运行,前面有分析了
}


这里我们同样贴出它的函数调用流程图,虽然是旧版本的,但是大致差不多(这里懒得自己画了,就剪切了个^^)



通过流程图我们可以清晰的明白,当usb设备插入usb接口后,hub_irq执行,启动工作队列执行hub_event工作,它检测到port状态的变化,调用hub_port_connect_change(),如果是新设备那么usb_allco_dev,然后调用usb_new_device来进行配置使usb设备可以正常工作



二.usb驱动的probe匹配过程

前面我们分析到调用usb_new_device来进行配置使usb设备可以正常工作,我们现在分析一下具体过程。主要是找到对应的客户驱动程序和该USB设备挂钩

 usb_new_device中调用device_add,将设备添加到设备层次结构中。

  大概调用流程:device_add -> bus_probe_device -> device_initial_probe -> __device_attach -> bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); 

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
             void *data, int (*fn)(struct device_driver *, void *))
{
    struct klist_iter i;
    struct device_driver *drv;
    int error = 0;

    if (!bus)
        return -EINVAL;

    klist_iter_init_node(&bus->p->klist_drivers, &i,
                 start ? &start->p->knode_bus : NULL);
    while ((drv = next_driver(&i)) && !error) //遍历驱动链表
        error = fn(drv, data); //这里是调用__device_attach_driver
    klist_iter_exit(&i);
    return error;
}

该函数调用bus_for_each_drv()来从总线上已注册的所有驱动中找出匹配的驱动程序.遍历bus上的所有驱动程序,并为每个驱动调用fn()来查看是否匹配. 这里的fn就是__device_attach_driver.这里化简了

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    ret = driver_match_device(drv, dev); //这里调用drv->bus->match(dev, drv),就是usb_device_match,总线match函数,USB core部分注册了
    if (ret == 0) {
        /* no match */
        return 0;
    } else if (ret == -EPROBE_DEFER) {
        dev_dbg(dev, "Device match requests probe deferral\n");
        driver_deferred_probe_add(dev);
    } else if (ret < 0) {
        dev_dbg(dev, "Bus failed to match device: %d", ret);
        return ret;
    } /* ret > 0 means positive match */

    return driver_probe_device(drv, dev); //尝试将设备和驱动程序绑定在一起
}

1. usb_device_match

我们分析一下就是usb_device_match,这个函数只是做一些粗略的匹配, 如果匹配成功则返回1。这个函数只是做一些粗略的匹配, 如果匹配成功则返回1, 然后由driver_probe_device来做进一步的匹配, 如果匹配失败则返回0, 并且driver_probe_device也不会在执行. 这个函数的调用保证了dev, drv 要么都是设备级别的( 即dev 代表usb 设备,drv 代表usb 设备驱动), 要么都是接口级别的( 即dev 代表usb 设备的一个interface,drv 代表usb 接口驱动).

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    /* devices and interfaces are handled separately */
    if (is_usb_device(dev)) {
        /* interface drivers never match devices */ //是匹配USB设备的驱动,USB接口的驱动不能匹配
        if (!is_usb_device_driver(drv))
            return 0;
        /* TODO: Add real matching code */
        return 1;
    } else if (is_usb_interface(dev)) { //如果是USB接口
        struct usb_interface *intf;
        struct usb_driver *usb_drv;
        const struct usb_device_id *id;
        /* device drivers never match interfaces */
        //usb接口在注册driver时将for_devices设置为0,for_devices =1,表示设备驱动,for_devices = 0,表示接口驱动
        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); //匹配id table
        if (id)
            return 1;
        id = usb_match_dynamic_id(intf, usb_drv); //匹配动态id table
        if (id)
            return 1;
    }
    return 0;
}


2.driver_probe_device

driver_probe_device主要是调用really_probe -> (drv->probe)

对于usb 来说这个函数的调用有2 种分支, 1: dev,drv 代表的是设备级别的, 2 dev,drv 代表的是接口级别的. 其他情况组合在usb_device_match 中被过滤掉了.

分支1: dev,drv 代表的是设备级别:

此时的drv 肯定是usb_generic_driver. 因为在当前的usb 系统中只有这个driver 是代表整个设备的驱动, 它是在usb_init 中被注册的, 而我们通常写的usb 驱动都是代表一个interface 的.

因此, 此时的drv->probe 将调用generic_probe().再到usb_set_configuration

int usb_set_configuration(struct usb_device *dev, int configuration)
{
   for(I = 0; I < nintf; i++) {
   struct usb_interface *intf = cp->interface[i];
   device_add(&intf->dev); //又会进入匹配
}
该函数比较重要, 但我们只关心probe 过程因此省掉了很多东西. 它为当前配置下的每个interface 调用device_add() 函数, 根据前面的分析可知, 这个过程将会走到接下来我们要分析的分支2.

分支2: dev,drv 代表的是interface 级别:

此时的dev 代表着一个interface, 而drv 就代表了我们自己的usb 驱动. 但是我们应当看到drv是device_driver 类型, 而我们写的usb 驱动的类型一般是usb_driver, 因此这里的probe 和我们自己写的probe 显然不是同一个. 实际上这里的drv 是我们的驱动对象里内嵌的一个子对象( 因为linux 下所以的驱动都必须用device_driver 来代表,). 那这个子对象的probe 函数是在哪里赋值的呢? 

这就要看usb_register宏了,实际里面是调用usb_register_driver

int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
            const char *mod_name)
{
    new_driver->drvwrap.for_devices = 0;
    new_driver->drvwrap.driver.name = new_driver->name;
    new_driver->drvwrap.driver.bus = &usb_bus_type;
    new_driver->drvwrap.driver.probe = usb_probe_interface; //这里是probe函数
    new_driver->drvwrap.driver.remove = usb_unbind_interface; 
    new_driver->drvwrap.driver.owner = owner;
    new_driver->drvwrap.driver.mod_name = mod_name;
    new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
    spin_lock_init(&new_driver->dynids.lock);
    INIT_LIST_HEAD(&new_driver->dynids.list);

    retval = driver_register(&new_driver->drvwrap.driver);
    if (retval)
        goto out;
    retval = usb_create_newid_files(new_driver);
    if (retval)
        goto out_newid;

    pr_info("%s: registered new interface driver %s\n",
            usbcore_name, new_driver->name); //一般log会打印这个
}

跟踪这个函数我们可以看到这里的probe 函数实际上是usb_probe_interface( 所有的usb interface 驱动都是一样的).

 static int usb_probe_interface(struct device *dev)
{
  struct driver = to_usb_driver(dev->driver);  //dev->driver   在really_probe 中设置.
  error = driver->probe(intf, id);   // 这个就是我们自己写的probe 函数了.
}

 driver->probe(intf, id); 这就调用到我们自己写的代码里面了,


3.流程图

大概流程图是一样的,我搬运来了



参考:



posted @ 2021-01-15 14:32  luoyuna  阅读(11813)  评论(0编辑  收藏  举报