usb摄像头驱动-core层hub.c

news2024/11/19 0:44:18

usb摄像头驱动-core层hub.c


文章目录

  • usb摄像头驱动-core层hub.c
  • usb_hub_init
  • hub_probe
  • hub_event
  • port_event
  • hub_port_connect_change
  • hub_port_connect
  • usb_new_device
  • announce_device


在这里插入图片描述

在USB摄像头驱动中,hub.c文件扮演着USB集线器(Hub)驱动的角色。USB集线器是用于连接多个USB设备的设备,它提供了额外的USB端口,并负责数据传输的分配和管理。hub.c文件中的内容主要涉及USB集线器的初始化、事件处理、数据传输和管理等功能。以下是该文件中常见的功能和作用的概括:

  1. 集线器的初始化和设备连接管理:

    • 检测和识别连接到集线器的USB设备。
    • 分配和配置集线器的USB端口。
    • 跟踪已连接设备的状态和属性,如端口状态、速度等。
    • 处理设备的插拔事件,并通知相应的驱动程序进行设备的初始化或释放。
  2. 端口状态和速度管理:

    • 监控集线器端口的状态变化,如设备插入或拔出。
    • 管理端口的电源状态、连接状态和速度信息。
    • 处理集线器和设备之间的速度协商和协议转换。
  3. 数据传输和分配:

    • 为连接到集线器的设备分配传输带宽。
    • 管理端口和传输通道的分配和释放。
    • 处理数据的转发和路由,确保正确的数据传输路径。
  4. 集线器事件和通知处理:

    • 监听和处理集线器和端口相关的事件,如连接状态变化、错误状态等。
    • 向上层驱动程序和应用程序发送事件通知,以便它们可以做出相应的处理。
  5. 集线器电源管理:

    • 管理集线器的电源控制,如启用/禁用电源、进入/退出睡眠状态等。
    • 处理集线器的电源管理策略,如自动挂起、节能模式等。

总体而言,hub.c文件中的内容实现了USB摄像头驱动中与USB集线器相关的功能,包括集线器的初始化、事件处理、数据传输和管理等。它负责管理USB集线器和连接的USB设备之间的通信,并确保数据的正确传输和设备的正常运行。

/driver/usb/core/hub.c

usb_hub_init

在内核中,这个函数通常在系统启动期间的初始化阶段被调用。具体来说,它会在USB子系统初始化期间被调用。
这个函数的主要作用是初始化USB hub驱动程序。USB hub是一种用于扩展USB接口的设备,它允许将多个USB设备连接到单个USB端口上。USB hub驱动程序负责管理和控制USB hub设备,并处理与USB设备的连接、断开、通信等操作。
在函数内部,它会注册USB hub驱动程序,使得内核能够正确识别和处理连接到USB hub的设备。它还分配一个工作队列(workqueue),用于处理USB hub相关的后台任务,例如处理连接和断开USB设备的事件。
总之,这个函数在内核中的作用是初始化USB hub驱动程序,确保USB hub设备能够正常工作并与其他USB设备进行通信。它是USB子系统中重要的初始化函数之一。

static struct usb_driver hub_driver = {
    .name =        "hub", // 驱动程序名称
    .probe =    hub_probe, // 探测函数
    .disconnect =    hub_disconnect, // 断开连接函数
    .suspend =    hub_suspend, // 暂停函数
    .resume =    hub_resume, // 恢复函数
    .reset_resume =    hub_reset_resume, // 重置恢复函数
    .pre_reset =    hub_pre_reset, // 重置前函数
    .post_reset =    hub_post_reset, // 重置后函数
    .unlocked_ioctl = hub_ioctl, // ioctl函数
    .id_table =    hub_id_table, // 设备表
    .supports_autosuspend =    1, // 支持自动挂起
};
// 初始化USB hub驱动程序
int usb_hub_init(void)
{
    // 注册USB hub驱动程序
    if (usb_register(&hub_driver) < 0) {
        printk(KERN_ERR "%s: 无法注册hub驱动程序\n",
            usbcore_name);
        return -1;
    }

    /*
     * 工作队列需要是可冻结的,以避免干扰USB-PERSIST端口移交。
     * 否则,它可能会在EHCI控制器将其端口移交给附属全速控制器之前看到一个全速设备已经消失。
     */
    hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
    if (hub_wq)
        return 0;

    /* 如果kernel_thread失败,则跳过 */
    usb_deregister(&hub_driver);
    pr_err("%s: 无法为USB hub分配工作队列\n", usbcore_name);

    return -1;
}

这个函数是用于初始化USB hub驱动程序的。下面是对函数的分析:

  1. 首先,通过调用usb_register(&hub_driver)来注册USB hub驱动程序。usb_register函数返回一个小于0的值表示注册失败。如果注册失败,会打印错误信息并返回-1。
  2. 接下来,函数尝试为USB hub分配一个工作队列(workqueue)。工作队列是用于异步执行后台任务的机制。它使用alloc_workqueue函数进行分配,并设置为可冻结(WQ_FREEZABLE)。如果成功分配工作队列,函数将返回0。
  3. 如果无法分配工作队列,说明内核线程(kernel_thread)失败。在这种情况下,函数调用usb_deregister(&hub_driver)来取消注册之前注册的USB hub驱动程序,以避免不完整的初始化。然后打印错误信息并返回-1。

总体而言,这个函数的目标是注册USB hub驱动程序并分配一个工作队列。如果其中任何一个步骤失败,函数将返回-1,表示初始化过程出现问题。
/driver/usb/core/hub.c

hub_probe

这个函数是一个USB hub驱动程序中的probe函数,它在与USB hub设备匹配时被调用。下面是对该函数的作用的解释:
作用:

  1. 该函数用于处理USB hub设备的探测和初始化过程。当一个USB设备与这个驱动程序匹配时,就会调用该函数。

  2. 在probe函数中,可以执行与特定设备相关的初始化任务,例如配置设备、分配资源、注册设备接口等。

  3. 通过该函数,驱动程序可以与内核USB子系统进行交互,以确保设备正确识别并能够与其他子系统进行通信。

  4. 在probe函数中,还可以注册设备的操作函数和回调函数,以处理设备的读写操作、事件处理等。

总之,这个函数的作用是处理与USB hub设备的探测和初始化相关的任务,以确保设备能够正常工作并与其他子系统进行通信。它是USB hub驱动程序中重要的函数之一。

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_host_interface *desc; // USB主机接口描述符
    struct usb_endpoint_descriptor *endpoint; // USB端点描述符
    struct usb_device *hdev; // USB设备
    struct usb_hub *hub; // USB集线器

    desc = intf->cur_altsetting; // 获取当前USB接口的描述符
    hdev = interface_to_usbdev(intf); // 获取USB设备

    /*
     * Set default autosuspend delay as 0 to speedup bus suspend,
     * based on the below considerations:
     *
     * - Unlike other drivers, the hub driver does not rely on the
     *   autosuspend delay to provide enough time to handle a wakeup
     *   event, and the submitted status URB is just to check future
     *   change on hub downstream ports, so it is safe to do it.
     *
     * - The patch might cause one or more auto supend/resume for
     *   below very rare devices when they are plugged into hub
     *   first time:
     *
     *      devices having trouble initializing, and disconnect
     *      themselves from the bus and then reconnect a second
     *      or so later
     *
     *      devices just for downloading firmware, and disconnects
     *      themselves after completing it
     *
     *   For these quite rare devices, their drivers may change the
     *   autosuspend delay of their parent hub in the probe() to one
     *   appropriate value to avoid the subtle problem if someone
     *   does care it.
     *
     * - The patch may cause one or more auto suspend/resume on
     *   hub during running 'lsusb', but it is probably too
     *   infrequent to worry about.
     *
     * - Change autosuspend delay of hub can avoid unnecessary auto
     *   suspend timer for hub, also may decrease power consumption
     *   of USB bus.
     *
     * - If user has indicated to prevent autosuspend by passing
     *   usbcore.autosuspend = -1 then keep autosuspend disabled.
     */
#ifdef CONFIG_PM
    if (hdev->dev.power.autosuspend_delay >= 0)
        pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
#endif

    /*
     * 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) {     /* 普通设备 */
        usb_enable_autosuspend(hdev); // 启用自动挂起
    } else {            /* 根集线器 */
        const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;

        if (drv->bus_suspend && drv->bus_resume)
            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)) { // 如果子类不为0或1
descriptor_error:
        dev_err (&intf->dev, "bad descriptor, ignoring hub\n"); // 输出错误信息
        return -EIO; // 返回错误码
    }

    /* Multiple endpoints? What kind of mutant ninja-hub is this? */ // 多个端点?这是什么神奇的集线器?
    if (desc->desc.bNumEndpoints != 1) // 如果端点数不为1
        goto descriptor_error; // 跳转到错误处理

    endpoint = &desc->endpoint[0].desc; // 获取端点描述符

    /* If it's not an interrupt in endpoint, we'd better punt! */ // 如果不是中断端点,我们最好放弃!
    if (!usb_endpoint_is_int_in(endpoint)) // 如果不是中断输入端点
        goto descriptor_error; // 跳转到错误处理

    /* We found a hub */ // 我们找到了一个集线器
    dev_info (&intf->dev, "USB hub found\n"); // 输出信息

    hub = kzalloc(sizeof(*hub), GFP_KERNEL); // 分配内存
    if (!hub) { // 如果分配失败
        dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n"); // 输出调试信息
        return -ENOMEM; // 返回错误码
    }

    kref_init(&hub->kref); // 初始化引用计数
    hub->intfdev = &intf->dev; // 设置接口设备
    hub->hdev = hdev; // 设置USB设备
    INIT_DELAYED_WORK(&hub->leds, led_work); // 初始化延迟工作
    INIT_DELAYED_WORK(&hub->init_work, NULL); // 初始化延迟工作
    INIT_WORK(&hub->events, hub_event); // 初始化工作
    usb_get_intf(intf); // 获取接口
    usb_get_dev(hdev); // 获取USB设备

    usb_set_intfdata (intf, hub); // 设置接口数据
    intf->needs_remote_wakeup = 1; // 设置需要远程唤醒
    pm_suspend_ignore_children(&intf->dev, true); // 忽略子设备的挂起

    if (hdev->speed == USB_SPEED_HIGH) // 如果速度为高速
        highspeed_hubs++; // 高速集线器数加1

    if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND) // 如果驱动信息中包含检查端口自动挂起的标志
        hub->quirk_check_port_auto_suspend = 1; // 设置检查端口自动挂起的标志

    if (hub_configure(hub, endpoint) >= 0) // 如果配置集线器成功
        return 0; // 返回0

    hub_disconnect (intf); // 断开集线器
    return -ENODEV; // 返回错误码
}
  1. 获取USB接口描述符和USB设备。

  2. 根据设备类型和层级判断设备是否为普通设备或根集线器,并根据情况启用自动挂起功能。

  3. 验证USB hub设备的描述符是否有效,包括子类和端点的检查。

  4. 分配内存并初始化USB hub结构体,设置相关参数和工作队列。

  5. 设置接口数据和设备引用计数,设置需要远程唤醒,并忽略子设备的挂起。

  6. 根据设备的速度和驱动信息进行相应处理,例如统计高速集线器数和设置检查端口自动挂起的标志。

  7. 如果配置集线器成功,返回0;否则断开集线器并返回错误码。

总之,这个函数的主要功能是初始化USB hub设备并进行相关设置,以确保设备能够正常工作。它涉及到USB设备描述符的验证、内存分配、参数设置等操作,是USB hub驱动程序中关键的探测和初始化函数之一
line1811:

INIT_WORK(&hub->events, hub_event); // 初始化工作

这行代码创建了一个名为events的工作结构体,并将其初始化为待处理的工作任务。hub_event函数是在这个工作任务被调度时将会执行的函数。
通过初始化工作结构体,可以将特定的函数(例如hub_event)与一个工作队列相关联,以便在适当的时机异步执行这个函数。这对于处理USB hub设备的事件或异步操作非常有用,因为可以将这些任务放入工作队列中,并在适当的时候由内核调度执行,而不会阻塞其他任务的执行。
总之,INIT_WORK(&hub->events, hub_event);这行代码的作用是初始化一个工作结构体,并将其与hub_event函数关联,以便在适当的时机异步执行hub_event函数的任务。

hub_event

hub_probe->hub_event

在内核中,static void hub_event(struct work_struct *work)函数是USB hub驱动程序中的一个工作函数,用于处理USB hub设备的事件。
这个函数在特定的工作队列中被调度执行,以处理与USB hub相关的事件。通常,在USB hub设备发生状态变化、端口插拔、速度变更等事件时,内核会调度执行这个函数。

static void hub_event(struct work_struct *work)
{
    struct usb_device *hdev; // USB设备
    struct usb_interface *intf; // USB接口
    struct usb_hub *hub; // USB集线器
    struct device *hub_dev; // 集线器设备
    u16 hubstatus; // 集线器状态
    u16 hubchange; // 集线器状态改变
    int i, ret;

    hub = container_of(work, struct usb_hub, events); // 获取USB集线器
    hdev = hub->hdev; // 获取USB设备
    hub_dev = hub->intfdev; // 获取集线器设备
    intf = to_usb_interface(hub_dev); // 获取USB接口

    dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
            hdev->state, hdev->maxchild,
            /* NOTE: expects max 15 ports... */
            (u16) hub->change_bits[0],
            (u16) hub->event_bits[0]); // 打印调试信息

    /* Lock the device, then check to see if we were
     * disconnected while waiting for the lock to succeed. */
    usb_lock_device(hdev); // 锁定USB设备
    if (unlikely(hub->disconnected)) // 如果集线器已经断开连接
        goto out_hdev_lock;

    /* If the hub has died, clean up after it */
    if (hdev->state == USB_STATE_NOTATTACHED) { // 如果USB设备未连接
        hub->error = -ENODEV; // 设置错误码
        hub_quiesce(hub, HUB_DISCONNECT); // 关闭集线器
        goto out_hdev_lock;
    }

    /* Autoresume */
    ret = usb_autopm_get_interface(intf); // 自动恢复
    if (ret) { // 如果恢复失败
        dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); // 打印调试信息
        goto out_hdev_lock;
    }


    /* If this is an inactive hub, do nothing */
    if (hub->quiescing) // 如果集线器正在关闭
        goto out_autopm;

    if (hub->error) { // 如果集线器出现错误
        dev_dbg(hub_dev, "resetting for error %d\n", hub->error); // 打印调试信息

        ret = usb_reset_device(hdev); // 重置USB设备
        if (ret) { // 如果重置失败
            dev_dbg(hub_dev, "error resetting hub: %d\n", ret); // 打印调试信息
            goto out_autopm;
        }

        hub->nerrors = 0;
        hub->error = 0;
    }

    /* 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)) { // 或者端口唤醒位被设置
            /*
             * 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); // 释放端口设备的运行时电源管理
        }
    }


    /* 处理集线器状态变化 */
    if (test_and_clear_bit(0, hub->event_bits) == 0)
        ;   /* 什么也不做 */
    else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
        dev_err(hub_dev, "get_hub_status failed\n"); // 打印调试信息
    else {
        if (hubchange & HUB_CHANGE_LOCAL_POWER) { // 如果本地电源改变
            dev_dbg(hub_dev, "power change\n"); // 打印调试信息
            clear_hub_feature(hdev, C_HUB_LOCAL_POWER); // 清除本地电源特性
            if (hubstatus & HUB_STATUS_LOCAL_POWER)
                /* FIXME: Is this always true? */
                hub->limited_power = 1; // 设置有限电源
            else
                hub->limited_power = 0; // 取消有限电源
        }
        if (hubchange & HUB_CHANGE_OVERCURRENT) { // 如果过流改变
            u16 status = 0;
            u16 unused;

            dev_dbg(hub_dev, "over-current change\n"); // 打印调试信息
            clear_hub_feature(hdev, C_HUB_OVER_CURRENT); // 清除过流特性
            msleep(500);    /* Cool down */ // 等待500ms
            hub_power_on(hub, true); // 打开集线器电源
            hub_hub_status(hub, &status, &unused); // 获取集线器状态
            if (status & HUB_STATUS_OVERCURRENT)
                dev_err(hub_dev, "over-current condition\n"); // 打印调试信息
        }
    }

out_autopm:
    /* 平衡上面的usb_autopm_get_interface() */
    usb_autopm_put_interface_no_suspend(intf);
out_hdev_lock:
    usb_unlock_device(hdev); // 解锁USB设备

    /* 平衡kick_hub_wq()中的内容并允许自动挂起 */
    usb_autopm_put_interface(intf);
    kref_put(&hub->kref, hub_release);
}
  • 获取USB设备、USB接口、USB集线器和集线器设备的指针。

  • 打印调试信息,包括USB设备的状态、端口数量、状态变化位和事件位。

  • 锁定USB设备,检查设备是否已经断开连接。

  • 如果USB设备未连接,设置错误码并关闭USB集线器。

  • 执行自动恢复操作,自动恢复USB接口的运行状态。

  • 如果集线器正在关闭,直接退出。

  • 如果集线器出现错误,重置USB设备。

  • 遍历集线器的所有端口,处理端口状态变化的事件。

  • 处理集线器状态变化的事件,包括本地电源改变和过流改变。

  • 释放锁定的USB设备和端口,并进行自动挂起。

  • 释放USB集线器的引用计数,并执行相应的清理操作。

总之,这个函数负责处理USB hub设备的各种事件,包括端口状态变化和集线器状态变化。它通过锁定设备、执行必要的操作以及释放资源来确保USB hub的正常运行。

段代码处理USB hub的端口状态变化事件。它通过遍历所有的端口,检查端口的事件位、状态改变位和唤醒位是否被设置,如果设置了任意一个位,则执行相应的处理操作。

  /* 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)) { // 或者端口唤醒位被设置
            /*
             * 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); // 释放端口设备的运行时电源管理
        }
}

具体的处理步骤如下:

  1. 使用变量i从1遍历到hdev->maxchild,表示遍历所有的端口。
  2. 获取当前端口的设备指针,存储在变量port_dev中,hub->ports[i - 1]表示获取USB hub中的第i个端口设备。
  3. 如果当前端口的事件位、状态改变位或唤醒位被设置,即调用test_bit()函数进行判断。
  4. 在满足上述条件的情况下,执行以下操作:
  • 调用pm_runtime_get_noresume()函数获取端口设备的运行时电源管理,确保在处理端口事件期间保持端口处于活动状态。
  • 调用pm_runtime_barrier()函数等待端口设备的运行时电源管理操作完成。
  • 调用usb_lock_port()函数锁定端口设备,以确保在处理端口事件期间其他操作不会干扰。
  • 调用port_event()函数处理端口事件,将USB hub和端口号作为参数传递给该函数。
  • 调用usb_unlock_port()函数解锁端口设备。
  • 调用pm_runtime_put_sync()函数释放端口设备的运行时电源管理。

这段代码的作用是针对每个端口处理其状态变化的事件。它获取并锁定端口设备,执行相应的事件处理函数,并在处理完成后释放端口设备,同时处理端口的运行时电源管理,确保在处理事件期间端口保持活动状态。

port_event

hub_probe->hub_event->port_event
port_event()函数用于处理USB hub的端口事件,它会在特定情况下被调用。
函数签名中的__must_hold(&port_dev->status_lock)表示该函数在调用之前必须获取端口设备的status_lock锁,并在函数执行期间持有该锁。这是为了确保在处理端口事件期间不会有其他线程同时修改端口的状态,保证数据的一致性和正确性。
具体来说,port_event()函数的作用如下:

  1. 接收USB hub和端口号作为参数,即hub和port1。
  2. 获取与port1对应的端口设备的指针。
  3. 根据端口设备的状态进行相应的处理,例如检测连接状态、重置端口、配置设备等。
  4. 更新端口设备的状态,包括连接状态、速度、带宽等信息。
  5. 根据端口的状态变化执行相应的操作,例如通知驱动程序、发送事件通知等。

port_event()函数会在以下情况下被调用:

  • 当USB hub检测到某个端口的事件发生时,例如设备连接、断开、速度变化等。

  • 当USB hub的状态发生变化时,需要通知端口设备进行相应的处理。

  • 当需要对特定端口进行配置或重置操作时。

总而言之,port_event()函数是用于处理USB hub端口事件的核心函数,它在特定的事件发生时被调用,并负责处理和更新与端口相关的操作和状态信息。

// 处理端口事件
static void port_event(struct usb_hub *hub, int port1)
        __must_hold(&port_dev->status_lock)
{
    // 是否需要连接变化
    int connect_change;
    // 获取端口设备
    struct usb_port *port_dev = hub->ports[port1 - 1];
    // 获取端口下的 USB 设备
    struct usb_device *udev = port_dev->child;
    // 获取 USB hub 设备
    struct usb_device *hdev = hub->hdev;
    // 端口状态和变化
    u16 portstatus, portchange;

    // 测试端口是否有变化
    connect_change = test_bit(port1, hub->change_bits);
    // 清除事件位和唤醒位
    clear_bit(port1, hub->event_bits);
    clear_bit(port1, hub->wakeup_bits);

    // 获取端口状态和变化
    if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
        return;

    // 如果连接状态发生变化
    if (portchange & USB_PORT_STAT_C_CONNECTION) {
        // 清除连接状态变化标志
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
        // 标记需要连接变化
        connect_change = 1;
    }

    // 如果启用状态发生变化
    if (portchange & USB_PORT_STAT_C_ENABLE) {
        // 如果不需要连接变化
        if (!connect_change)
            // 打印启用状态变化信息
            dev_dbg(&port_dev->dev, "enable change, status %08x\n",
                    portstatus);
        // 清除启用状态变化标志
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

        /*
         * EM interference sometimes causes badly shielded USB devices
         * to be shutdown by the hub, this hack enables them again.
         * Works at least with mouse driver.
         */
        // 如果端口未启用且存在 USB 设备
        if (!(portstatus & USB_PORT_STAT_ENABLE)
            && !connect_change && udev) {
            // 打印信息
            dev_err(&port_dev->dev, "disabled by hub (EMI?), re-enabling...\n");
            // 标记需要连接变化
            connect_change = 1;
        }
    }


    // 如果端口过流
    if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
        u16 status = 0, unused;

        dev_dbg(&port_dev->dev, "over-current change\n"); // 打印调试信息
        usb_clear_port_feature(hdev, port1,
                USB_PORT_FEAT_C_OVER_CURRENT); // 清除端口过流标志
        msleep(100);    /* Cool down */ // 等待100ms
        hub_power_on(hub, true); // 打开集线器电源
        hub_port_status(hub, port1, &status, &unused); // 获取端口状态
        if (status & USB_PORT_STAT_OVERCURRENT) // 如果端口过流
            dev_err(&port_dev->dev, "over-current condition\n"); // 打印错误信息
    }

    // 如果端口复位
    if (portchange & USB_PORT_STAT_C_RESET) {
        dev_dbg(&port_dev->dev, "reset change\n"); // 打印调试信息
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET); // 清除端口复位标志
    }
    // 如果端口处于SS.Inactive状态
    if ((portchange & USB_PORT_STAT_C_BH_RESET)
        && hub_is_superspeed(hdev)) {
        dev_dbg(&port_dev->dev, "warm reset change\n"); // 打印调试信息
        usb_clear_port_feature(hdev, port1,
                USB_PORT_FEAT_C_BH_PORT_RESET); // 清除端口复位标志
    }
    // 如果端口连接状态发生变化
    if (portchange & USB_PORT_STAT_C_LINK_STATE) {
        dev_dbg(&port_dev->dev, "link state change\n"); // 打印调试信息
        usb_clear_port_feature(hdev, port1,
                USB_PORT_FEAT_C_PORT_LINK_STATE); // 清除端口连接状态标志
    }
    // 如果端口配置错误
    if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
        dev_warn(&port_dev->dev, "config error\n"); // 打印警告信息
        usb_clear_port_feature(hdev, port1,
                USB_PORT_FEAT_C_PORT_CONFIG_ERROR); // 清除端口配置错误标志
    }

    // 如果端口未启用
    if (!pm_runtime_active(&port_dev->dev))
        return;

    // 处理远程唤醒
    if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
        connect_change = 1;

    /*
     * 如果端口处于SS.Inactive状态,进行热复位
     */
    if (hub_port_warm_reset_required(hub, port1, portstatus)) {
        dev_dbg(&port_dev->dev, "do warm reset\n"); // 打印调试信息
        if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
                || udev->state == USB_STATE_NOTATTACHED) {
            if (hub_port_reset(hub, port1, NULL,
                    HUB_BH_RESET_TIME, true) < 0)
                hub_port_disable(hub, port1, 1);
        } else {
            usb_unlock_port(port_dev);
            usb_lock_device(udev);
            usb_reset_device(udev);
            usb_unlock_device(udev);
            usb_lock_port(port_dev);
            connect_change = 0;
        }
    }

    // 如果连接状态发生变化
    if (connect_change)
        hub_port_connect_change(hub, port1, portstatus, portchange); // 处理连接状态变化
}

该函数用于处理USB hub的端口事件,并且需要持有端口设备的status_lock锁。
函数内容的概括如下:
获取与给定端口号port1对应的端口设备port_dev。
获取端口设备下的USB设备udev以及USB hub设备hdev。
检测端口是否发生连接状态变化,并清除相应的事件位和唤醒位。

获取端口的状态和状态变化。

根据状态变化进行不同的处理:

如果连接状态发生变化,清除连接状态变化标志,并标记需要连接变化。

如果启用状态发生变化,清除启用状态变化标志,并根据特定情况处理端口启用状态变化。

如果端口发生过流,清除过流标志,等待一段时间后重新打开集线器电源,并根据端口状态判断是否存在过流情况。

如果端口发生复位,清除复位标志。

如果端口处于SS.Inactive状态并且为超速设备,清除端口复位标志。

如果端口连接状态发生变化,清除连接状态变化标志。

如果端口发生配置错误,清除配置错误标志。

如果端口设备未处于活动状态,则返回。

处理远程唤醒情况,如果成功处理,则标记需要连接变化。

如果端口处于SS.Inactive状态,进行热复位操作:

如果USB设备不存在、端口未连接或USB设备处于未连接状态,则执行集线器端口复位操作或禁用端口。

否则,对USB设备进行复位操作。

如果连接状态发生变化,处理连接状态变化。

综上所述,该函数根据端口事件的不同情况,执行相应的处理操作,包括处理连接状态变化、启用状态变化、过流情况、复位等,以确保USB hub端口的正常工作。
如果连接状态发生变化,调用端口连接变化处理函数hub_port_connect_change

// 如果连接状态发生变化
if (connect_change)
hub_port_connect_change(hub, port1, portstatus, portchange); // 处理连接状态变化

hub_port_connect_change

hub_probe->hub_event->port_event->hub_port_connect_change
该函数用于处理USB hub端口的连接状态变化,并且需要持有端口设备的status_lock锁。
函数的作用是在端口连接状态变化时执行相应的处理操作,包括连接设备、断开设备、通知驱动程序等。
函数被调用的时机是在port_event()函数中,当检测到端口的连接状态发生变化时,会调用hub_port_connect_change()函数进行处理。

// USB hub 端口连接变化处理函数
static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
        __must_hold(&port_dev->status_lock)
{
    // 获取端口设备
    struct usb_port *port_dev = hub->ports[port1 - 1];
    // 获取端口下的 USB 设备
    struct usb_device *udev = port_dev->child;
    // 状态
    int status = -ENODEV;

    // 打印端口状态和变化信息
    dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
            portchange, portspeed(hub, portstatus));

    // 如果 USB hub 有指示灯
    if (hub->has_indicators) {
        // 设置端口指示灯为自动模式
        set_port_led(hub, port1, HUB_LED_AUTO);
        // 设置端口指示灯状态为自动模式
        hub->indicator[port1-1] = INDICATOR_AUTO;
    }

#ifdef  CONFIG_USB_OTG
    /* during HNP, don't repeat the debounce */
    // 如果是 HNP 模式
    if (hub->hdev->bus->is_b_host)
        // 清除连接状态和启用状态变化标志
        portchange &= ~(USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE);
#endif

    // 如果端口已连接且存在 USB 设备且 USB 设备状态不是未连接状态
    if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
            udev->state != USB_STATE_NOTATTACHED) {
        // 如果端口已启用
        if (portstatus & USB_PORT_STAT_ENABLE) {
            status = 0;     /* Nothing to do */
#ifdef CONFIG_PM
        // 如果 USB 设备状态是挂起状态且持久化使能
        } else if (udev->state == USB_STATE_SUSPENDED &&
                udev->persist_enabled) {
            /* 对于挂起的设备,将其视为远程唤醒事件。 */
            // 解锁端口
            usb_unlock_port(port_dev);
            // 远程唤醒 USB 设备
            status = usb_remote_wakeup(udev);
            // 锁定端口
            usb_lock_port(port_dev);
#endif
        } else {
            /* Don't resuscitate */;
        }
    }
    // 清除连接状态变化标志
    clear_bit(port1, hub->change_bits);

    // 如果成功重新验证连接
    if (status == 0)
        return;

    // 解锁端口
    usb_unlock_port(port_dev);
    // USB hub 端口连接处理
    hub_port_connect(hub, port1, portstatus, portchange);
    // 锁定端口
    usb_lock_port(port_dev);
}

函数内容的具体作用如下:
获取与给定端口号port1对应的端口设备port_dev和端口下的USB设备udev。
打印端口的状态和变化信息。

如果USB hub具有指示灯功能,将端口的指示灯设置为自动模式。

根据配置选项和HNP模式的情况,对端口状态变化标志进行调整。

根据端口的连接状态和USB设备的状态执行以下操作:

如果端口已连接且存在USB设备且USB设备的状态不是未连接状态:

如果端口已启用,表示连接状态正常,无需进行任何操作。
如果USB设备处于挂起状态且启用了持久化功能,将其视为远程唤醒事件,通过调用usb_remote_wakeup()函数远程唤醒USB设备。
清除连接状态变化标志。

如果成功重新验证连接(即状态为0),直接返回,无需进行后续处理。

解锁端口设备。

调用hub_port_connect()函数处理USB hub端口的连接,包括连接设备、初始化设备信息等。
锁定端口设备。

综上所述,hub_port_connect_change()函数在USB hub端口的连接状态变化时被调用,用于处理设备的连接和断开操作,并进行相应的指示灯设置、HNP模式处理和远程唤醒等操作。

hub_port_connect

hub_probe->hub_event->port_event->hub_port_connect_change->hub_port_connect
hub_port_connect()函数用于处理USB hub端口的连接操作。
函数的作用是在端口连接状态发生变化时,根据连接状态和变化信息执行相应的操作,包括连接设备、初始化设备信息、设置电源属性等。
该函数会被调用的时机包括以下情况:
当检测到端口的连接状态发生变化,并且需要进行连接操作时,会调用该函数进行处理。

// 当前端口上有设备连接时,执行此函数
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
        u16 portchange)
{
    int status, i;
    unsigned unit_load;
    struct usb_device *hdev = hub->hdev; // 获取hub所在的设备
    struct usb_hcd *hcd = bus_to_hcd(hdev->bus); // 获取hub所在的HCD
    struct usb_port *port_dev = hub->ports[port1 - 1]; // 获取当前端口的port
    struct usb_device *udev = port_dev->child; // 获取当前端口上的设备
    static int unreliable_port = -1; // 记录连接不稳定的端口

    // 如果当前端口上已经有设备连接,则断开连接
    if (udev) {
        if (hcd->usb_phy && !hdev->parent)
            usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
        usb_disconnect(&port_dev->child);
    }

    // 如果当前端口上没有设备连接,或者连接状态发生了变化,则清除removed_bits
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            (portchange & USB_PORT_STAT_C_CONNECTION))
        clear_bit(port1, hub->removed_bits);

    // 如果连接状态或者使能状态发生了变化,则进行debounce
    if (portchange & (USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE)) {
        status = hub_port_debounce_be_stable(hub, port1);
        if (status < 0) {
            if (status != -ENODEV &&
                port1 != unreliable_port &&
                printk_ratelimit())
                dev_err(&port_dev->dev, "connect-debounce failed\n");
            portstatus &= ~USB_PORT_STAT_CONNECTION;
            unreliable_port = port1;
        } else {
            portstatus = status;
        }
    }


    /* 如果debounce失败,或者没有设备连接,或者设备已经被移除,则直接返回 */
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            test_bit(port1, hub->removed_bits)) {

        /*
         * 如果端口可供电,但是当前端口的电源关闭了,且端口没有被占用,则重新开启电源
         * (例如,根集线器被重置),但是只有当端口没有被其他设备占用时才能重新开启电源。
         */
        if (hub_is_port_power_switchable(hub)
                && !port_is_power_on(hub, portstatus)
                && !port_dev->port_owner)
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);

        if (portstatus & USB_PORT_STAT_ENABLE)
            goto done;
        return;
    }
    if (hub_is_superspeed(hub->hdev))
        unit_load = 150;
    else
        unit_load = 100;

    status = 0;
    for (i = 0; i < SET_CONFIG_TRIES; i++) {

        /* 为每次尝试重新分配内存,因为对于前一个内存的引用可能会以各种方式逃逸 */
        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);
        udev->bus_mA = hub->mA_per_port;
        udev->level = hdev->level + 1;
        udev->wusb = hub_is_wusb(hub);

        /* 只有USB 3.0设备连接到超级速度集线器。 */
        if (hub_is_superspeed(hub->hdev))
            udev->speed = USB_SPEED_SUPER;
        else
            udev->speed = USB_SPEED_UNKNOWN;

        choose_devnum(udev);
        if (udev->devnum <= 0) {
            status = -ENOTCONN; /* 不要重试 */
            goto loop;
        }


        /* 重置(非USB 3.0设备)并获取描述符 */
        usb_lock_port(port_dev);
        status = hub_port_init(hub, udev, port1, i);
        usb_unlock_port(port_dev);
        if (status < 0)
            goto loop;

        /* 检测设备的quirks */
        usb_detect_quirks(udev);
        if (udev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(1000);

        /* 连续的总线供电集线器不可靠;它们可能会违反电压降预算。如果新的子设备有“供电”LED,则用户应该注意到我们没有启用它(无需阅读syslog),即使父设备没有每个端口的LED也可以。 */
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
                && udev->bus_mA <= unit_load) {
            u16 devstat;

            status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                    &devstat);
            if (status) {
                dev_dbg(&udev->dev, "get status %d ?\n", status);
                goto loop_disable;
            }
            if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                dev_err(&udev->dev,
                    "can't connect bus-powered hub "
                    "to this port\n");
                if (hub->has_indicators) {
                    hub->indicator[port1-1] =
                        INDICATOR_AMBER_BLINK;
                    queue_delayed_work(
                        system_power_efficient_wq,
                        &hub->leds, 0);
                }
                status = -ENOTCONN; /* 不要重试 */
                goto loop_disable;
            }
        }


        /* check for devices running slower than they could */
        if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 // 如果设备支持 USB 2.0 或更高版本
                && udev->speed == USB_SPEED_FULL // 如果设备速度为全速
                && highspeed_hubs != 0) // 如果存在高速 USB hub
            check_highspeed (hub, udev, port1); // 检查是否需要升级到高速

        /* Store the parent's children[] pointer.  At this point
         * udev becomes globally accessible, although presumably
         * no one will look at it until hdev is unlocked.
         */ // 存储父设备的 children[] 指针。此时,udev 变为全局可访问,尽管可能没有人会在 hdev 解锁之前查看它。
        status = 0;

        mutex_lock(&usb_port_peer_mutex);

        /* We mustn't add new devices if the parent hub has
         * been disconnected; we would race with the
         * recursively_mark_NOTATTACHED() routine.
         */ // 如果父 USB hub 已断开连接,则不能添加新设备;我们将与 recursively_mark_NOTATTACHED() 例程竞争。
        spin_lock_irq(&device_state_lock);
        if (hdev->state == USB_STATE_NOTATTACHED) // 如果父 USB hub 已断开连接
            status = -ENOTCONN; // 设置状态为未连接
        else
            port_dev->child = udev; // 将 udev 存储到 port_dev 的 child 中
        spin_unlock_irq(&device_state_lock);
        mutex_unlock(&usb_port_peer_mutex);

        /* Run it through the hoops (find a driver, etc) */ // 运行它通过 hoops(查找驱动程序等)
        if (!status) {
            status = usb_new_device(udev); // 新建 USB 设备
            if (status) { // 如果新建设备失败
                mutex_lock(&usb_port_peer_mutex);
                spin_lock_irq(&device_state_lock);
                port_dev->child = NULL; // 将 port_dev 的 child 设置为 NULL
                spin_unlock_irq(&device_state_lock);
                mutex_unlock(&usb_port_peer_mutex);
            } else { // 如果新建设备成功
                if (hcd->usb_phy && !hdev->parent) // 如果存在 USB 物理层并且 hdev 不是根 USB hub
                    usb_phy_notify_connect(hcd->usb_phy, udev->speed); // 通知 USB 物理层连接成功
            }
        }


        // 获取 USB hub 剩余电源预算
        status = hub_power_remaining(hub);
        if (status)
            dev_dbg(hub->intfdev, "%dmA power budget left\n", status);

        return;

loop_disable:
        // 禁用 USB hub 端口
        hub_port_disable(hub, port1, 1);
loop:
        // 重新初始化 USB 设备的端点 0
        usb_ep0_reinit(udev);
        // 释放 USB 设备编号
        release_devnum(udev);
        // 释放 USB 设备
        hub_free_dev(udev);
        // 释放 USB 设备引用计数
        usb_put_dev(udev);
        // 如果状态为未连接或不支持,则跳出循环
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }
    // 如果存在父 USB hub 或者驱动程序没有移交端口或者驱动程序移交端口失败
    if (hub->hdev->parent ||
            !hcd->driver->port_handed_over ||
            !(hcd->driver->port_handed_over)(hcd, port1)) {
        // 如果状态不是未连接或未定义
        if (status != -ENOTCONN && status != -ENODEV)
            // 打印错误信息
            dev_err(&port_dev->dev,
                    "unable to enumerate USB device\n");
    }

done:
    // 禁用 USB hub 端口
    hub_port_disable(hub, port1, 1);
    // 如果驱动程序释放端口并且 hdev 不是根 USB hub
    if (hcd->driver->relinquish_port && !hub->hdev->parent)
        // 驱动程序释放端口
        hcd->driver->relinquish_port(hcd, port1);

}

hub_port_connect()函数的作用是处理USB hub端口的连接操作。
函数首先检查当前端口上是否已经连接了USB设备,如果有,会断开连接。接下来,检查连接状态和变化信息,进行去抖动操作。如果连接状态或使能状态发生了变化,则进行去抖动以确保稳定连接。
如果端口没有连接或设备已被移除,则会执行相应的操作并返回。
如果端口连接正常,则根据USB hub的类型和设备的属性进行初始化设置。然后为USB设备分配设备号、初始化端点等操作。
如果设备是USB 2.0设备且连接到高速USB hub上,则检查设备是否可以以更高的速度运行。
然后,将新连接的设备添加到父USB hub的子设备列表中,并通知USB物理层设备连接成功。
最后,禁用端口并释放相关资源,如果驱动程序支持端口释放,则进行相应的处理。
总体而言,hub_port_connect()函数用于处理USB hub端口的连接操作,包括断开连接、去抖动、初始化设备和端口等操作。它是USB hub驱动程序中的重要函数之一。

usb_new_device

hub_probe->hub_event->port_event->hub_port_connect_change->hub_port_connect->usb_new_device
usb_new_device()函数的作用是为USB设备进行初始化并与合适的驱动程序进行匹配。
该函数在以下情况下被调用:
当一个USB设备被连接到USB总线上时,USB核心会检测到设备的存在并创建一个
usb_device结构体来表示该设备。
当设备被检测到后,USB核心会调用
usb_new_device()函数为该设备进行初始化,并为设备分配资源。
初始化过程中,USB核心会遍历已加载的驱动程序,尝试将设备与合适的驱动程序进行匹配。
如果找到了适合的驱动程序,USB核心会调用该驱动程序的
probe()函数来完成设备的特定初始化和配置。

int usb_new_device(struct usb_device *udev)
{
    int err;

    if (udev->parent) {
        /* 初始化非根集线器设备唤醒为禁用状态;
         * 设备(非)配置控制唤醒能力
         * sysfs power/wakeup 控制唤醒启用/禁用
         */
        device_init_wakeup(&udev->dev, 0);
    }

    /* 告诉运行时-PM框架设备处于活动状态 */
    pm_runtime_set_active(&udev->dev);
    pm_runtime_get_noresume(&udev->dev);
    pm_runtime_use_autosuspend(&udev->dev);
    pm_runtime_enable(&udev->dev);

    /* 默认情况下,禁止所有设备的自动挂起。它将被允许在绑定期间用于集线器。 */
    usb_disable_autosuspend(udev);

    err = usb_enumerate_device(udev);   /* 读取描述符 */
    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)));
    /* 导出usbdev设备节点供libusb使用 */
    udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
            (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

    /* 告诉世界! */
    announce_device(udev);


    if (udev->serial)
        add_device_randomness(udev->serial, strlen(udev->serial)); // 添加设备随机性
    if (udev->product)
        add_device_randomness(udev->product, strlen(udev->product)); // 添加设备随机性
    if (udev->manufacturer)
        add_device_randomness(udev->manufacturer,
                      strlen(udev->manufacturer)); // 添加设备随机性

    device_enable_async_suspend(&udev->dev); // 启用异步挂起

    /* 检查集线器或固件是否将此端口标记为不可移除 */
    if (udev->parent)
        set_usb_port_removable(udev); // 设置USB端口是否可移除

    /* 注册设备。设备驱动程序负责配置设备并调用添加设备通知器链(由usbfs和可能的其他程序使用)。 */
    err = device_add(&udev->dev); // 添加设备
    if (err) {
        dev_err(&udev->dev, "can't device_add, error %d\n", err); // 添加设备失败
        goto fail;
    }


    /* 为子设备和USB端口设备之间创建链接文件 */
    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); // 创建端点设备
    usb_mark_last_busy(udev); // 标记设备最后一次活动时间
    pm_runtime_put_sync_autosuspend(&udev->dev); // 唤醒设备并挂起

    return err; // 返回错误码

fail:
    usb_set_device_state(udev, USB_STATE_NOTATTACHED); // 设置设备状态为未连接
    pm_runtime_disable(&udev->dev); // 禁用设备运行时
    pm_runtime_set_suspended(&udev->dev); // 设置设备运行时为挂起状态
    return err; // 返回错误码
}

该函数的主要目的是初始化USB设备并进行一系列配置和设置。以下是该函数的概括:
如果设备不是根集线器设备,将设备唤醒功能禁用。
设置设备为活动状态,并启用运行时电源管理。
禁用设备的自动挂起。
调用
usb_enumerate_device()函数读取设备描述符。
导出USB设备节点供其他程序使用。
发送设备连接通知。
如果设备有序列号、产品名称或制造商信息,添加设备随机性。
启用异步挂起。
如果设备有父集线器,设置USB端口是否可移除。
注册设备并添加到设备链表中。
如果设备有父集线器,创建链接文件以连接子设备和USB端口设备。
创建端点设备。
标记设备的最后一次活动时间。
唤醒设备并挂起,启用自动挂起。
如果发生错误,设置设备状态为未连接,并禁用设备运行时。
函数返回错误码来指示操作的成功与否。
总的来说,这个函数负责对USB设备进行初始化、配置和设置,并在必要时与父集线器和其他设备建立链接。

announce_device

hub_probe->hub_event->port_event->hub_port_connect_change->hub_port_connect->usb_new_device->announce_device

发现新的 USB 设备时,打印设备信息

/**
 * announce_device - 发现新的 USB 设备时,打印设备信息
 * @udev: 新发现的 USB 设备
 */
static void announce_device(struct usb_device *udev)
{
    dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
        le16_to_cpu(udev->descriptor.idVendor),
        le16_to_cpu(udev->descriptor.idProduct));
    dev_info(&udev->dev,
        "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
        udev->descriptor.iManufacturer,
        udev->descriptor.iProduct,
        udev->descriptor.iSerialNumber);
    show_string(udev, "Product", udev->product); // 打印设备的产品名称
    show_string(udev, "Manufacturer", udev->manufacturer); // 打印设备的制造商名称
    show_string(udev, "SerialNumber", udev->serial); // 打印设备的序列号
}

接如usb摄像头时对应打印信息如下

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

<数据结构>顺序表和链表的比较|缓存命中率

&#x1f4ad;前言&#xff1a;通过之前对顺序表和链表的实现&#xff0c;我们可以发现在增删查改某些操作上两者的效率前言有所差异&#xff0c;本篇文章将总结二者的异同。 顺序表的实现http://t.csdn.cn/Lxyg2单链表的实现http://t.csdn.cn/rHgjG双链表的实现http://t.csdn.…

国考省考行测:现期比重,基期比重

国考省考行测&#xff1a; 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到寒冬&am…

【c++】位图与布隆过滤器

一.位图 1.位图的概念 给 40 亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在 这 40 亿个数中。【腾讯】 刚开始许多同学能想到的方法有&#xff1a; 1. 遍历&#xff0c;时间复杂度 O(N) 2. 排序 (O(NlogN)) &#x…

分代回收工作流程

GC回收算法之分代回收 GC回收垃圾主要有三个算法&#xff0c;分别是标记清除&#xff0c;复制算法以及标记整理。 三种算法各有优缺点&#xff0c;其中标记清除的优点就是简单高效&#xff0c;缺点就是很容易带来内存碎片化问题。 复制算法的话&#xff0c;优点确实解决了内存碎…

springcloud-alibaba (02)nacos学习笔记

nacos 一&#xff0c;介绍1. 什么是Spring Cloud Alibaba Nacos2. Nacos的特点和优势 二&#xff0c;安装和配置三&#xff0c;创建项目第一步&#xff0c;创建父工程第二步&#xff0c;创建基础公共模块第三步&#xff0c;创建服务模块第四步&#xff0c;开启多个实例 四&…

用Python进行AB测试之T检验的案例学习【文末源码地址】

文章目录 案例背景假设检验解读一、提出假设二、检验统计量三、代码实现四、结论 源码地址 案例背景 产品经理对APP做了许多改动&#xff0c;想要通过AB测试评估一下改动后的效果如何&#xff1f; 其中有一项评估是&#xff1a;签到按钮从蓝色改为红色后&#xff0c;客户点击…

Java 成员变量与局部变量有什么区别?

节选自JavaGuide(Github 标星 134k star!「Java学习 + 面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识) 语法形式:从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符…

Smart HTML Elements Crack

Smart HTML Elements Crack Smart HTML Elements是一个现代的Vanilla JS和ES6库以及下一代前端框架。企业级Web组件包括可访问性功能(WAI-ARIA&#xff0c;第508/WTAG法规遵从性)、本地化、从右到左键盘导航和主题化。与Angular、ReactJS、Vue.js、Bootstrap、Meteor和任何其他…

vue编写组件 根据指定日期获取一周内所有 日期与农历日期展示 并标记当天

首先 我们要引入一下对应的第三方依赖 npm install --save chinese-lunar-calendar sass sass-loader这里 我们需要 chinese-lunar-calendar 将日期变成农历日期的工具 sass是因为 我这里为了方便 用了 sass写样式 组件代码如下 <template><headerclass "ske…

学习自动化这3个坏习惯要你命,90%测试人躺枪!

将自动化测试当成很了不起的资本&#xff0c;源于国内对Coding的崇拜 盲目的学习自动化&#xff0c;不光对你的工作没有帮助&#xff0c;可能对你的测试之路还会起反作用&#xff01; 1,为什么说盲目学习自动化可能会让你一无是处&#xff1f; 没有全面理解软件测试的基本原理…

让自己再认识一下分区吧

在上个博客中&#xff0c;我们讨论了复制 —— 即数据在不同节点上的副本&#xff0c;对于非常大的数据集&#xff0c;或非常高的吞吐量&#xff0c;仅仅进行复制是不够的&#xff1a;我们需要将数据进行 分区&#xff08;partitions&#xff09;&#xff0c;也称为 分片&#…

【Proteus仿真】| 51单片机——MAX7219 驱动数码管

系列文章 todo: 文章目录 前言1. 简单了解MAX72191.1 引脚说明1.2 寄存器说明1.2.1 选位寄存器1.2.2 BCD译码器设置寄存器1.2.3 亮度寄存器1.2.4 扫描限制寄存器1.2.5 显示测试寄存器1.2.6 关停寄存器 1.3 spi总线通信时序1.4 数据格式 2. 使用MAX7219驱动数码管2.1 驱动数码管…

【2023/05/18】TPU

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第13天。 Share The waterfall sings,"I find my song,when I find my freedom." 译文&#xff1a; 瀑布歌唱到&#xff1a;“我得到自由时便有了歌声。” I cannot tell why this heart la…

精彩直击 | 迅镭激光参展CIBF2023年电池技术盛会

5月16日&#xff0c;全球规模最大的电池、能源行业盛会——CIBF2023第十五届中国国际电池技术展览会(以下简称2023CIBF电池展)&#xff0c;在深圳国际会展中心(宝安新馆)隆重开幕! 迅镭激光携一系列新能源自动化解决方案亮相9T263展位&#xff0c;与客户分享创新技术及自动化产…

【Jmeter第四章】Jmeter添加断言(捕捉自定义错误信息)

文章目录 1、断言介绍2、Jmeter使用断言3、效果展示 1、断言介绍 前言&#xff1a;关于Jmeter中的断言&#xff0c;其实可以理解为对返回信息&#xff0c;返回代码的一种处理&#xff0c; 因为对应HTTP状态200而言&#xff0c;但我们会定义不同的返回值&#xff0c;例如 code…

【数据结构<顺序表>】C语言

前言 线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条…

linux swap交换区满了怎么办(已解决)

swap交换区满了怎么办 一、不增加交换区的方法 free -m 或free -h查看占用情况 使用如下指令来查看占用swap的前十进程 for i in $( cd /proc;ls |grep "^[0-9]"|awk $0 >100) ;do awk /Swap:/{aa$2}END{print "$i",a/1024"M"} /proc/$i…

仓库信息管理系统设计与实现

一、数据库设计 1.数据库模型设计概览 2.数据库表设计 ①depository 描述&#xff1a; 该表存储仓库的信息&#xff0c;比如仓库名称&#xff0c;仓库地址和仓库介绍 表结构&#xff1a; 序号 字段名 数据类型 主键 非空 默认值 描述 1 id INT(10) 是 是 2…

LlamaIndex 联合创始人下场揭秘:如何使用私有数据提升 LLM 的能力?

ChatGPT 的爆火证明了大型语言模型&#xff08;LLM&#xff09;在生成知识和推理方面的能力。不过&#xff0c;ChatGPT 是使用公共数据集进行预训练的模型&#xff0c;因此可能无法提供与用户业务相关的特定答案或结果。 那么&#xff0c;如何使用私有数据最大化发挥 LLM 的能力…

SpringMVC第三阶段:源码解析SpringMVC如何调用Controller目标方法

源码解析SpringMVC如何调用Controller目标方法&#xff1a; 浏览器如何访问到Controller目标方法. 1、所有请求进入时候,会先进入org.springframework.web.servlet.DispatcherServlet前端控制器的doDispatch() 方法 2 、在 1016 行 getHandler() 方法中,会通过请求的资源路径…