当前位置: 首页 > news >正文

<kernel>kernel 6.4 USB-之-hub_port_connect()分析

<kernel>kernel 6.4 USB-之-hub_port_connect()分析

kernel 6.4 USB系列文章如下:
<kernel>kernel 6.4 USB-之-hub_event()分析
<kernel>kernel 6.4 USB-之-port_event()分析
<kernel>kernel 6.4 USB-之-hub_port_connect_change()分析

本文是基于linux kernel 6.4版本内核分析;源码下载路径:linux kernel
在这里插入图片描述

一、前言

本章主要分析的是在Linux内核的USB驱动中,用于处理USB端口连接的函数hub_port_connect。主要过程和作用包括:

(1) 断开此端口下的任何现有设备。

(2) 检查端口状态,如果连接不稳定或者没有设备连接,可能会尝试恢复端口电源(如果端口支持电源切换的话)。

(3) 为新连接的设备进行初始化,包括分配设备编号、设备状态设定、设备速度设定等。

(4) 对端口进行多次初始化尝试(由PORT_INIT_TRIES定义),在每次尝试中都会重置设备并获取描述符,如果设备支持延迟初始化,则会等待一段时间。

(5) 检查新连接的设备是否运行在其可能的最高速度下。如果不是,可能会进行一些处理。

(6) 将新设备添加到父集线器的子设备数组中,使其全局可访问。

(7) 如果新设备成功添加,会通知USB PHY(如果存在的话)设备已连接。

(8) 如果在尝试过程中发生错误,会禁用端口并释放设备。

(9) 最后,如果设备无法枚举并且端口没有被接管,会打印错误信息。如果HCD驱动支持,可能会放弃端口的控制权。

总的来说,这个函数的主要作用是处理USB端口的连接事件,包括设备的初始化、枚举和错误处理等。

二、hub_port_connect()函数

hub_port_connect()函数内容如下:

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,u16 portchange)
{int status = -ENODEV;int i;unsigned unit_load;struct usb_device *hdev = hub->hdev;struct usb_hcd *hcd = bus_to_hcd(hdev->bus);struct usb_port *port_dev = hub->ports[port1 - 1];struct usb_device *udev = port_dev->child;static int unreliable_port = -1;bool retry_locked;/* Disconnect any existing devices under this port */if (udev) {if (hcd->usb_phy && !hdev->parent)usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);usb_disconnect(&port_dev->child);}/* We can forget about a "removed" device when there's a physical* disconnect or the connect status changes.*/if (!(portstatus & USB_PORT_STAT_CONNECTION) ||(portchange & USB_PORT_STAT_C_CONNECTION))clear_bit(port1, hub->removed_bits);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;}}/* Return now if debouncing failed or nothing is connected or* the device was "removed".*/if (!(portstatus & USB_PORT_STAT_CONNECTION) ||test_bit(port1, hub->removed_bits)) {/** maybe switch power back on (e.g. root hub was reset)* but only if the port isn't owned by someone else.*/if (hub_is_port_power_switchable(hub)&& !usb_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;elseunit_load = 100;status = 0;for (i = 0; i < PORT_INIT_TRIES; i++) {if (hub_port_stop_enumerate(hub, port1, i)) {status = -ENODEV;break;}usb_lock_port(port_dev);mutex_lock(hcd->address0_mutex);retry_locked = true;/* reallocate for each attempt, since references* to the previous one can escape in various ways*/udev = usb_alloc_dev(hdev, hdev->bus, port1);if (!udev) {dev_err(&port_dev->dev,"couldn't allocate usb_device\n");mutex_unlock(hcd->address0_mutex);usb_unlock_port(port_dev);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);/* Devices connected to SuperSpeed hubs are USB 3.0 or later */if (hub_is_superspeed(hub->hdev))udev->speed = USB_SPEED_SUPER;elseudev->speed = USB_SPEED_UNKNOWN;choose_devnum(udev);if (udev->devnum <= 0) {status = -ENOTCONN;	/* Don't retry */goto loop;}/* reset (non-USB 3.0 devices) and get descriptor */status = hub_port_init(hub, udev, port1, i);if (status < 0)goto loop;mutex_unlock(hcd->address0_mutex);usb_unlock_port(port_dev);retry_locked = false;if (udev->quirks & USB_QUIRK_DELAY_INIT)msleep(2000);/* consecutive bus-powered hubs aren't reliable; they can* violate the voltage drop budget.  if the new child has* a "powered" LED, users should notice we didn't enable it* (without reading syslog), even without per-port LEDs* on the parent.*/if (udev->descriptor.bDeviceClass == USB_CLASS_HUB&& udev->bus_mA <= unit_load) {u16	devstat;status = usb_get_std_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;	/* Don't retry */goto loop_disable;}}/* check for devices running slower than they could */if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200&& udev->speed == USB_SPEED_FULL&& highspeed_hubs != 0)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.*/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.*/spin_lock_irq(&device_state_lock);if (hdev->state == USB_STATE_NOTATTACHED)status = -ENOTCONN;elseport_dev->child = udev;spin_unlock_irq(&device_state_lock);mutex_unlock(&usb_port_peer_mutex);/* 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);}}if (status)goto loop_disable;status = hub_power_remaining(hub);if (status)dev_dbg(hub->intfdev, "%dmA power budget left\n", status);return;loop_disable:hub_port_disable(hub, port1, 1);
loop:usb_ep0_reinit(udev);release_devnum(udev);hub_free_dev(udev);if (retry_locked) {mutex_unlock(hcd->address0_mutex);usb_unlock_port(port_dev);}usb_put_dev(udev);if ((status == -ENOTCONN) || (status == -ENOTSUPP))break;/* When halfway through our retry count, power-cycle the port */if (i == (PORT_INIT_TRIES - 1) / 2) {dev_info(&port_dev->dev, "attempt power cycle\n");usb_hub_set_port_power(hdev, hub, port1, false);msleep(2 * hub_power_on_good_delay(hub));usb_hub_set_port_power(hdev, hub, port1, true);msleep(hub_power_on_good_delay(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:hub_port_disable(hub, port1, 1);if (hcd->driver->relinquish_port && !hub->hdev->parent) {if (status != -ENOTCONN && status != -ENODEV)hcd->driver->relinquish_port(hcd, port1);}
}

下面就对hub_port_connect()函数内容详细分析:

2.1 第7-10行:创建和初始化一些指针变量

第7-10行:主要过程内容和作用是创建和初始化一些指针变量,这些变量用于后续的USB端口和设备处理。

struct usb_device *hdev = hub->hdev;:创建一个指向USB设备的指针hdev,并将其初始化为当前hub的设备。这个设备代表的是USB集线器自身。

struct usb_hcd *hcd = bus_to_hcd(hdev->bus);:创建一个指向USB主控制器设备(Host Controller Device,HCD)的指针hcd,并将其初始化为hdev所在的总线对应的HCD。HCD是硬件设备,负责管理USB设备的物理连接和数据传输。

struct usb_port *port_dev = hub->ports[port1 - 1];:创建一个指向USB端口的指针port_dev,并将其初始化为当前hub的第port1 - 1个端口。这个端口是我们要处理的USB端口。

struct usb_device *udev = port_dev->child;:创建一个指向USB设备的指针udev,并将其初始化为port_dev端口上的子设备。这个设备是我们要处理的USB设备。

static int unreliable_port = -1;:创建一个静态整数变量unreliable_port,并将其初始化为-1。这个变量用于标记不可靠的端口。

总的来说,这段代码的作用是创建和初始化一些用于USB设备和端口处理的变量。

2.2 第15-19行:断开此USB端口下的任何现有设备

第15-19行:主要作用是断开此USB端口下的任何现有设备。

if (udev) { … }:这是一个条件判断,如果udev(即,此USB端口下的子设备)存在,那么就执行大括号中的代码。

if (hcd->usb_phy && !hdev->parent) usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);:这是一个嵌套的条件判断,如果HCD(即,主控制器设备)有PHY(即,物理接口),并且hdev(即,当前hub的设备,也就是USB集线器自身)没有父设备,那么就调用usb_phy_notify_disconnect函数,通知PHY设备已经断开。这个函数的两个参数是PHY和udev的速度。

usb_disconnect(&port_dev->child);:调用usb_disconnect函数,断开port_dev(即,此USB端口)下的子设备。这个函数的参数是指向port_dev子设备的指针。

总的来说,这段代码的作用是断开此USB端口下的任何现有设备。这是在处理USB端口连接事件时的第一步,为新设备的连接做准备。

2.2.1 usb_phy_notify_disconnect()函数

路径:include\linux\usb\phy.h
static inline int
usb_phy_notify_disconnect(struct usb_phy *x, enum usb_device_speed speed)
{if (x && x->notify_disconnect)return x->notify_disconnect(x, speed);elsereturn 0;
}

该函数用于通知USB PHY(Physical Interface,物理接口)设备已断开连接。如果PHY设备存在,并且有处理断开连接的方法,那么就调用这个方法;否则,就什么都不做。

2.2.2 usb_phy_notify_disconnect()函数

void usb_disconnect(struct usb_device **pdev)
{struct usb_port *port_dev = NULL;struct usb_device *udev = *pdev;struct usb_hub *hub = NULL;int port1 = 1;/* mark the device as inactive, so any further urb submissions for* this device (and any of its children) will fail immediately.* this quiesces everything except pending urbs.*/usb_set_device_state(udev, USB_STATE_NOTATTACHED);dev_info(&udev->dev, "USB disconnect, device number %d\n",udev->devnum);/** Ensure that the pm runtime code knows that the USB device* is in the process of being disconnected.*/pm_runtime_barrier(&udev->dev);usb_lock_device(udev);hub_disconnect_children(udev);/* deallocate hcd/hardware state ... nuking all pending urbs and* cleaning up all state associated with the current configuration* so that the hardware is now fully quiesced.*/dev_dbg(&udev->dev, "unregistering device\n");usb_disable_device(udev, 0);usb_hcd_synchronize_unlinks(udev);if (udev->parent) {port1 = udev->portnum;hub = usb_hub_to_struct_hub(udev->parent);port_dev = hub->ports[port1 - 1];sysfs_remove_link(&udev->dev.kobj, "port");sysfs_remove_link(&port_dev->dev.kobj, "device");/** As usb_port_runtime_resume() de-references udev, make* sure no resumes occur during removal*/if (!test_and_set_bit(port1, hub->child_usage_bits))pm_runtime_get_sync(&port_dev->dev);}usb_remove_ep_devs(&udev->ep0);usb_unlock_device(udev);/* Unregister the device.  The device driver is responsible* for de-configuring the device and invoking the remove-device* notifier chain (used by usbfs and possibly others).*/device_del(&udev->dev);/* Free the device number and delete the parent's children[]* (or root_hub) pointer.*/release_devnum(udev);/* Avoid races with recursively_mark_NOTATTACHED() */spin_lock_irq(&device_state_lock);*pdev = NULL;spin_unlock_irq(&device_state_lock);if (port_dev && test_and_clear_bit(port1, hub->child_usage_bits))pm_runtime_put(&port_dev->dev);hub_free_dev(udev);put_device(&udev->dev);
}

主要用于处理USB设备断开连接的情况。函数的详细过程内容和作用如下:
(1) 首先,函数接收一个指向USB设备指针的指针作为参数,然后定义了一些局部变量,包括指向USB端口、USB设备和USB集线器的指针,以及一个整数变量port1。

(2) 然后,函数将USB设备的状态设置为USB_STATE_NOTATTACHED,表示设备已经断开连接。并输出一条信息,显示设备号和断开连接的状态。

(3) 接着,函数调用pm_runtime_barrier函数,确保电源管理运行时代码知道USB设备正在断开连接。

(4) 函数接着锁定设备,断开所有子设备的连接,禁用设备,并同步所有未完成的USB请求块(URB)。

(5) 如果设备有父设备,即设备是连接在一个USB集线器上的,那么函数会获取设备所在的端口号和集线器,然后移除设备和端口之间的sysfs链接。

(6) 函数接着移除设备的端点0的所有设备文件,然后解锁设备。

(7) 接下来,函数调用device_del函数,注销设备。这个过程中,设备驱动程序负责对设备进行去配置,并调用移除设备的通知链。

(8) 函数接着释放设备号,并删除父设备的children数组中对应的指针。

(9) 最后,函数清除端口的使用标记,释放设备占用的内存,并减少设备的引用计数。

总的来说,这个函数的作用是处理USB设备的断开连接,包括设备状态的设置、设备的锁定和解锁、设备的禁用、设备的注销、设备号的释放、设备内存的释放等。

2.3 第24-26行:在USB端口的物理断开或连接状态发生改变时忽略"removed"标记

第24-26行:主要作用是在USB端口的物理断开或连接状态发生改变时,忽略被标记为"removed"的设备。

if (!(portstatus & USB_PORT_STAT_CONNECTION) || (portchange & USB_PORT_STAT_C_CONNECTION)):这是一个条件判断。如果端口的状态portstatus中没有设置USB_PORT_STAT_CONNECTION标志位,或者端口的状态改变portchange中设置了USB_PORT_STAT_C_CONNECTION标志位,那么就执行大括号中的代码。USB_PORT_STAT_CONNECTION标志位表示端口的连接状态,USB_PORT_STAT_C_CONNECTION标志位表示端口的连接状态发生了改变。

clear_bit(port1, hub->removed_bits);:调用clear_bit函数,清除hub->removed_bits中的第port1位。hub->removed_bits是一个位图,用于标记哪些端口的设备被移除了。

总的来说,这段代码的作用是在USB端口的物理断开或连接状态发生改变时,忽略被标记为"removed"的设备。这是处理USB端口连接事件的一部分,用于准备新设备的连接。

2.4 第28-41行:USB端口的连接和使能状态发生变化稳定性检测

第28-41行:主要作用是处理USB端口的连接和使能状态发生变化的情况,并进行稳定性检测。

if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) {…}:这是一个条件判断。如果端口状态改变portchange中设置了USB_PORT_STAT_C_CONNECTION或USB_PORT_STAT_C_ENABLE标志位,那么就执行大括号中的代码。USB_PORT_STAT_C_CONNECTION标志位表示端口的连接状态发生了改变,USB_PORT_STAT_C_ENABLE标志位表示端口的使能状态发生了改变。

status = hub_port_debounce_be_stable(hub, port1);:调用hub_port_debounce_be_stable函数,对端口进行稳定性检测,然后将结果赋值给status。

if (status < 0) {…}:这是一个条件判断。如果status小于0,表示稳定性检测失败,那么就执行大括号中的代码。

if (status != -ENODEV && port1 != unreliable_port && printk_ratelimit()) dev_err(&port_dev->dev, “connect-debounce failed\n”);:这是一个嵌套的条件判断。如果status不等于-ENODEV,并且端口号port1不等于unreliable_port,并且printk_ratelimit函数返回真,那么就调用dev_err函数,输出一条错误信息,表示连接稳定性检测失败。

portstatus &= ~USB_PORT_STAT_CONNECTION;:清除portstatus中的USB_PORT_STAT_CONNECTION标志位,表示端口已经断开连接。

unreliable_port = port1;:将unreliable_port设置为port1,表示port1是不可靠的端口。

else { portstatus = status; }:这是上面条件判断的否定分支。如果status大于等于0,表示稳定性检测成功,那么就将status赋值给portstatus。

总的来说,这段代码的作用是处理USB端口的连接和使能状态发生变化的情况,并进行稳定性检测。如果稳定性检测失败,那么就清除端口的连接状态,并将端口标记为不可靠;如果稳定性检测成功,那么就更新端口的状态。

2.4.1 hub_port_debounce()函数

路径:drivers\usb\core\hub.h
static inline int hub_port_debounce_be_stable(struct usb_hub *hub,int port1)
{return hub_port_debounce(hub, port1, false);
}int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
{int ret;u16 portchange, portstatus;unsigned connection = 0xffff;int total_time, stable_time = 0;struct usb_port *port_dev = hub->ports[port1 - 1];for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);if (ret < 0)return ret;if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&(portstatus & USB_PORT_STAT_CONNECTION) == connection) {if (!must_be_connected ||(connection == USB_PORT_STAT_CONNECTION))stable_time += HUB_DEBOUNCE_STEP;if (stable_time >= HUB_DEBOUNCE_STABLE)break;} else {stable_time = 0;connection = portstatus & USB_PORT_STAT_CONNECTION;}if (portchange & USB_PORT_STAT_C_CONNECTION) {usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_CONNECTION);}if (total_time >= HUB_DEBOUNCE_TIMEOUT)break;msleep(HUB_DEBOUNCE_STEP);}dev_dbg(&port_dev->dev, "debounce total %dms stable %dms status 0x%x\n",total_time, stable_time, portstatus);if (stable_time < HUB_DEBOUNCE_STABLE)return -ETIMEDOUT;return portstatus;
}

主要用于处理USB端口的稳定性检测。

int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected) {…}:定义了一个名为hub_port_debounce的函数,接收三个参数:一个指向USB集线器的指针hub,一个整数port1表示端口号,一个布尔值must_be_connected表示端口是否必须连接。

int ret; u16 portchange, portstatus; unsigned connection = 0xffff; int total_time, stable_time = 0; struct usb_port *port_dev = hub->ports[port1 - 1];:定义了一些局部变量,包括ret用于存储返回值,portchange和portstatus用于存储端口的状态改变和状态,connection用于存储连接状态,total_time和stable_time用于计时,port_dev用于存储端口设备。

for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {…}:这是一个无限循环,每次循环都会增加total_time的值。

ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);:调用usb_hub_port_status函数,获取端口的状态和状态改变,然后将结果赋值给ret。

if (ret < 0) return ret;:如果ret小于0,表示获取状态失败,那么就返回ret。

if (!(portchange & USB_PORT_STAT_C_CONNECTION) && (portstatus & USB_PORT_STAT_CONNECTION) == connection) {…}:这是一个条件判断。如果端口的状态没有改变,并且端口的连接状态等于connection,那么就执行大括号中的代码。

if (portchange & USB_PORT_STAT_C_CONNECTION) {…}:这是一个条件判断。如果端口的状态改变了,那么就执行大括号中的代码。

if (total_time >= HUB_DEBOUNCE_TIMEOUT) break;:如果总时间超过了超时时间,那么就跳出循环。

msleep(HUB_DEBOUNCE_STEP);:让当前进程睡眠一段时间。

if (stable_time < HUB_DEBOUNCE_STABLE) return -ETIMEDOUT;:如果稳定时间小于稳定阈值,那么就返回-ETIMEDOUT。

return portstatus;:返回端口的状态。

总的来说,这个函数的作用是处理USB端口的稳定性检测。它会检测端口的连接状态,如果连接状态持续稳定一段时间,那么就返回端口的状态;如果连接状态持续不稳定,那么就返回超时错误。

2.5 第46-61行:处理USB端口连接稳定性检测失败

第46-61行:主要作用是在USB端口连接稳定性检测失败、没有设备连接或者设备被"移除"时,进行一些处理。

if (!(portstatus & USB_PORT_STAT_CONNECTION) || test_bit(port1, hub->removed_bits)) {…}:这是一个条件判断。如果端口的状态portstatus中没有设置USB_PORT_STAT_CONNECTION标志位,或者端口号port1在hub->removed_bits中被标记为"移除",那么就执行大括号中的代码。

if (hub_is_port_power_switchable(hub) && !usb_port_is_power_on(hub, portstatus) && !port_dev->port_owner) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);:这是一个嵌套的条件判断。如果端口的电源是可以切换的,且端口的电源没有打开,且端口没有被其他设备占用,那么就调用set_port_feature函数,设置端口的电源特性。

if (portstatus & USB_PORT_STAT_ENABLE) goto done;:这是一个嵌套的条件判断。如果端口的状态portstatus中设置了USB_PORT_STAT_ENABLE标志位,那么就跳转到done标签。

return;:返回,结束函数。

总的来说,这段代码的作用是在USB端口连接稳定性检测失败、没有设备连接或者设备被"移除"时,进行一些处理,包括可能的电源恢复和跳过后续的处理。

2.6 第62-65行:设置单位负载(unit load)的值

第62-65行:主要作用是根据USB集线器(hub)的速度等级,设置单位负载(unit load)的值。

if (hub_is_superspeed(hub->hdev)):这是一个条件判断。如果hub->hdev(即,hub的设备)是超高速(superspeed,即USB 3.0或更高版本),那么就执行大括号中的代码。

unit_load = 150;:将unit_load设置为150。这是因为在USB 3.0及更高版本中,一个USB端口的单位负载是150mA。

else:这是上面条件判断的否定分支。如果hub->hdev不是超高速,那么就执行大括号中的代码。

unit_load = 100;:将unit_load设置为100。这是因为在USB 2.0及更低版本中,一个USB端口的单位负载是100mA。

总的来说,这段代码的作用是根据USB集线器的速度等级,设置单位负载的值。这对于后续的电源管理和设备枚举等操作是必要的。

2.7 第69行:

第69行:是一个for循环的开始,用于尝试初始化USB端口。

for (i = 0; i < PORT_INIT_TRIES; i++) {…}:这是一个for循环,循环变量是i,循环次数是PORT_INIT_TRIES。PORT_INIT_TRIES是一个预定义的常量,表示初始化尝试的次数。
在这个循环中,会执行大括号里的代码,这部分代码通常是对USB端口进行初始化的操作,包括重置端口、获取设备描述符等。如果在尝试过程中发生错误,可能会进行错误处理,如禁用端口、释放设备等。

总的来说,这段代码的作用是尝试初始化USB端口,确保新连接的设备能够正常工作。

2.8 第70-73行:检测是否停止初始化

第70-73行:主要作用是在尝试初始化USB端口时,如果hub_port_stop_enumerate函数返回真,那么就停止初始化,并设置错误状态。

if (hub_port_stop_enumerate(hub, port1, i)) {…}:这是一个条件判断。如果hub_port_stop_enumerate函数返回真,那么就执行大括号中的代码。hub_port_stop_enumerate函数的作用通常是检查USB端口的初始化是否应该停止,它的参数是hub、端口号和当前的尝试次数。

status = -ENODEV;:将status设置为-ENODEV。-ENODEV是一个错误码,表示没有这样的设备,用于表示设备初始化失败。

break;:跳出循环。因为这段代码是在一个for循环中,所以break语句会结束这个循环。

总的来说,这段代码的作用是在尝试初始化USB端口时,如果hub_port_stop_enumerate函数返回真,那么就停止初始化,并设置错误状态。这是在处理USB端口连接事件时,对设备初始化过程的一个错误处理。

2.8.1 hub_port_stop_enumerate()函数

static bool hub_port_stop_enumerate(struct usb_hub *hub, int port1, int retries)
{struct usb_port *port_dev = hub->ports[port1 - 1];if (port_dev->early_stop) {if (port_dev->ignore_event)return true;/** We want unsuccessful attempts to fail quickly.* Since some devices may need one failure during* port initialization, we allow two tries but no* more.*/if (retries < 2)return false;port_dev->ignore_event = 1;} elseport_dev->ignore_event = 0;return port_dev->ignore_event;
}

这个函数的作用是检查USB端口的初始化是否应该停止。如果端口被标记为early_stop,并且重试次数大于等于2,那么就停止初始化;否则,就继续初始化。

2.9 第75-77行:锁定USB端口和主控制器设备的互斥锁保护并发访问

第75-77行:主要作用是锁定USB端口和主控制器设备的互斥锁,以保护对它们的并发访问。

usb_lock_port(port_dev);:调用usb_lock_port函数,锁定port_dev表示的USB端口。这是为了防止在初始化端口和设备时,其他线程同时对端口进行修改。

mutex_lock(hcd->address0_mutex);:调用mutex_lock函数,锁定hcd->address0_mutex表示的互斥锁。hcd是主控制器设备,address0_mutex是一个互斥锁,用于保护对地址0的访问。在USB中,地址0是默认的设备地址,所有新连接的设备都会首先被分配到这个地址。

retry_locked = true;:将retry_locked设置为真。这个变量通常用于标记是否已经获取了锁,如果已经获取了锁,那么在后续的错误处理中就需要释放这些锁。

总的来说,这段代码的作用是锁定USB端口和主控制器设备的互斥锁,以保护对它们的并发访问。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

2.10 第81-88行:分配一个usb_device结构体

第81-88行:主要作用是为新连接的USB设备分配一个usb_device结构体,并进行错误处理。

udev = usb_alloc_dev(hdev, hdev->bus, port1);:调用usb_alloc_dev函数,为新连接的USB设备分配一个usb_device结构体,并将结果赋值给udev。usb_alloc_dev函数的参数是父设备(即,USB集线器设备hdev)、总线(即,hdev所在的总线)和端口号。

if (!udev) {…}:这是一个条件判断。如果udev为空(即,usb_alloc_dev函数返回NULL),表示分配usb_device结构体失败,那么就执行大括号中的代码。

dev_err(&port_dev->dev, “couldn’t allocate usb_device\n”);:调用dev_err函数,输出一条错误信息,表示无法分配usb_device。

mutex_unlock(hcd->address0_mutex);:调用mutex_unlock函数,解锁hcd->address0_mutex表示的互斥锁。这是因为在之前的代码中,我们锁定了这个互斥锁,所以在错误处理时需要解锁。

usb_unlock_port(port_dev);:调用usb_unlock_port函数,解锁port_dev表示的USB端口。这是因为在之前的代码中,我们锁定了这个端口,所以在错误处理时需要解锁。

goto done;:跳转到done标签。这通常表示跳出当前的函数或循环,结束后续的处理。

总的来说,这段代码的作用是为新连接的USB设备分配一个usb_device结构体,并进行错误处理。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

2.11 第90-93行:设置新连接的USB设备基本属性

第90-93行:主要作用是设置新连接的USB设备的一些基本属性。

usb_set_device_state(udev, USB_STATE_POWERED);:调用usb_set_device_state函数,将设备udev的状态设置为USB_STATE_POWERED。这表示设备已经上电,但还没有被配置。

udev->bus_mA = hub->mA_per_port;:将设备udev的bus_mA属性设置为hub->mA_per_port。bus_mA表示设备从总线获取的电流,单位是毫安(mA);mA_per_port表示集线器每个端口提供的电流。

udev->level = hdev->level + 1;:将设备udev的level属性设置为hdev->level + 1。level表示设备在USB设备树中的层级,hdev是父设备,所以udev的层级应该比hdev的层级高1。

udev->wusb = hub_is_wusb(hub);:将设备udev的wusb属性设置为hub_is_wusb(hub)的返回值。wusb表示设备是否支持无线USB;hub_is_wusb函数检查集线器是否支持无线USB。

总的来说,这段代码的作用是设置新连接的USB设备的一些基本属性,包括设备状态、电流、层级和无线USB支持等。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

2.12 第96-99行:设置新连接的USB设备的速度等级

第96-99行:主要作用是设置新连接的USB设备的速度等级。

if (hub_is_superspeed(hub->hdev)):这是一个条件判断。如果hub->hdev(即,hub的设备)是超高速(superspeed,即USB 3.0或更高版本),那么就执行大括号中的代码。

udev->speed = USB_SPEED_SUPER;:将设备udev的速度等级设置为USB_SPEED_SUPER。这表示设备是超高速设备,即USB 3.0或更高版本。

else:这是上面条件判断的否定分支。如果hub->hdev不是超高速,那么就执行大括号中的代码。

udev->speed = USB_SPEED_UNKNOWN;:将设备udev的速度等级设置为USB_SPEED_UNKNOWN。这表示设备的速度等级未知。

总的来说,这段代码的作用是设置新连接的USB设备的速度等级。这对于后续的设备驱动程序加载和设备操作等是必要的。

2.13 第101-105行:为新连接的USB设备选择一个设备号

第101-105行:主要作用是为新连接的USB设备选择一个设备号,并进行错误处理。

choose_devnum(udev);:调用choose_devnum函数,为设备udev选择一个设备号。在USB系统中,设备号是用来唯一标识设备的一个数字。

if (udev->devnum <= 0) {…}:这是一个条件判断。如果设备udev的设备号小于等于0,表示选择设备号失败,那么就执行大括号中的代码。

status = -ENOTCONN;:将status设置为-ENOTCONN。-ENOTCONN是一个错误码,表示设备未连接,用于表示选择设备号失败。

goto loop;:跳转到loop标签。这通常表示跳出当前的函数或循环,结束后续的处理。

总的来说,这段代码的作用是为新连接的USB设备选择一个设备号,并进行错误处理。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

2.13.1 choose_devnum()函数

static void choose_devnum(struct usb_device *udev)
{int		devnum;struct usb_bus	*bus = udev->bus;/* be safe when more hub events are proceed in parallel */mutex_lock(&bus->devnum_next_mutex);if (udev->wusb) {devnum = udev->portnum + 1;BUG_ON(test_bit(devnum, bus->devmap.devicemap));} else {/* Try to allocate the next devnum beginning at* bus->devnum_next. */devnum = find_next_zero_bit(bus->devmap.devicemap, 128,bus->devnum_next);if (devnum >= 128)devnum = find_next_zero_bit(bus->devmap.devicemap,128, 1);bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);}if (devnum < 128) {set_bit(devnum, bus->devmap.devicemap);udev->devnum = devnum;}mutex_unlock(&bus->devnum_next_mutex);
}

这个函数的作用是为新连接的USB设备选择一个设备号。如果设备支持无线USB,那么设备号就是端口号加1;否则,设备号就是总线设备映射中下一个为0的位。设备号用于唯一标识USB设备,对于设备的管理和操作非常重要。

2.14 第108-110行:初始化USB端口并获取设备描述符

第108-110行:主要作用是初始化USB端口并获取设备描述符,同时处理可能出现的错误。

status = hub_port_init(hub, udev, port1, i);:调用hub_port_init函数,对USB端口进行初始化并获取设备描述符,然后将返回的状态值赋给status。hub_port_init函数的参数包括USB集线器hub,USB设备udev,端口号port1和尝试次数i。

if (status < 0) {…}:这是一个条件判断。如果status小于0,表示hub_port_init函数执行失败,那么就执行大括号中的代码。

goto loop;:跳转到loop标签。这通常表示跳出当前的函数或循环,返回到某个特定的代码位置进行下一轮的循环或操作。
总的来说,这段代码的作用是初始化USB端口并获取设备描述符,同时处理可能出现的错误。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

hub_port_init()函数的内容比较多,后续会单独一篇文章分析。

2.15 第112-114行:解锁

第112-114行:主要作用是解锁先前锁定的USB端口和主控制器设备的互斥锁,并将retry_locked设置为false。

2.16 第116-117行:

第116-117行:主要作用是在初始化USB设备时,如果设备的特性(quirks)中包含USB_QUIRK_DELAY_INIT,那么就延迟2秒(2000毫秒)。

if (udev->quirks & USB_QUIRK_DELAY_INIT) {…}:这是一个条件判断。如果设备udev的特性(quirks)中包含USB_QUIRK_DELAY_INIT,那么就执行大括号中的代码。在USB系统中,quirks是用来表示设备特性或者行为的一个位掩码,USB_QUIRK_DELAY_INIT是其中的一个位,表示设备在初始化时需要延迟。

msleep(2000);:调用msleep函数,延迟2000毫秒,即2秒。这是为了满足一些特殊设备在初始化时需要延迟一段时间的需求。

总的来说,这段代码的作用是在初始化USB设备时,如果设备的特性(quirks)中包含USB_QUIRK_DELAY_INIT,那么就延迟2秒(2000毫秒)。这是在处理USB设备初始化过程中,对设备特性的一个常见处理。

2.17 第125-149行:检查新连接的USB设备是否是集线器

第125-149行:主要作用是检查新连接的USB设备是否是一个连续的、由总线供电的集线器,并进行相应的处理。

if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= unit_load) {…}:这是一个条件判断。如果设备udev是一个集线器,并且设备从总线获取的电流小于等于单位负载,那么就执行大括号中的代码。在USB系统中,连续的由总线供电的集线器可能会违反电压下降预算,因此需要进行特殊处理。

status = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, &devstat);:调用usb_get_std_status函数,获取设备udev的状态,并将结果赋值给status。这是为了检查设备是否是自供电的。

if (status) {…}:这是一个条件判断。如果status不为0,表示获取设备状态失败,那么就执行大括号中的代码。

dev_dbg(&udev->dev, “get status %d ?\n”, status);:调用dev_dbg函数,输出一条调试信息,表示获取设备状态失败。

goto loop_disable;:跳转到loop_disable标签。这通常表示跳出当前的函数或循环,结束后续的处理。

if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {…}:这是一个条件判断。如果设备udev不是自供电的,那么就执行大括号中的代码。

dev_err(&udev->dev, “can’t connect bus-powered hub to this port\n”);:调用dev_err函数,输出一条错误信息,表示无法将由总线供电的集线器连接到这个端口。

if (hub->has_indicators) {…}:这是一个嵌套的条件判断。如果集线器hub有指示器,那么就执行大括号中的代码。

hub->indicator[port1-1] = INDICATOR_AMBER_BLINK;:将集线器hub的第port1-1个指示器的状态设置为INDICATOR_AMBER_BLINK,表示黄灯闪烁。

queue_delayed_work(system_power_efficient_wq, &hub->leds, 0);:调用queue_delayed_work函数,将hub->leds表示的工作项添加到system_power_efficient_wq表示的工作队列中,延迟0毫秒执行。这是为了更新集线器的LED指示器。

status = -ENOTCONN;:将status设置为-ENOTCONN。-ENOTCONN是一个错误码,表示设备未连接,用于表示无法将由总线供电的集线器连接到这个端口。

goto loop_disable;:跳转到loop_disable标签。这通常表示跳出当前的函数或循环,结束后续的处理。

总的来说,这段代码的作用是检查新连接的USB设备是否是一个连续的、由总线供电的集线器,并进行相应的处理。如果设备是由总线供电的集线器,并且不是自供电的,那么就输出错误信息,并更新集线器的LED指示器。

2.17.1 usb_get_status()函数

路径:include\linux\usb.h
static inline int usb_get_std_status(struct usb_device *dev,int recip, int target, void *data)
{return usb_get_status(dev, recip, USB_STATUS_TYPE_STANDARD, target,data);
}路径:drivers\usb\core\message.c
int usb_get_status(struct usb_device *dev, int recip, int type, int target,void *data)
{int ret;void *status;int length;switch (type) {case USB_STATUS_TYPE_STANDARD:length = 2;break;case USB_STATUS_TYPE_PTM:if (recip != USB_RECIP_DEVICE)return -EINVAL;length = 4;break;default:return -EINVAL;}status =  kmalloc(length, GFP_KERNEL);if (!status)return -ENOMEM;ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),USB_REQ_GET_STATUS, USB_DIR_IN | recip, USB_STATUS_TYPE_STANDARD,target, status, length, USB_CTRL_GET_TIMEOUT);switch (ret) {case 4:if (type != USB_STATUS_TYPE_PTM) {ret = -EIO;break;}*(u32 *) data = le32_to_cpu(*(__le32 *) status);ret = 0;break;case 2:if (type != USB_STATUS_TYPE_STANDARD) {ret = -EIO;break;}*(u16 *) data = le16_to_cpu(*(__le16 *) status);ret = 0;break;default:ret = -EIO;}kfree(status);return ret;
}
EXPORT_SYMBOL_GPL(usb_get_status);

这个函数的作用是获取USB设备的状态,包括设备是否自供电、是否启用了远程唤醒设施、批量或中断端点是否被阻塞等。这对于USB设备的管理和操作非常重要。

2.18 第152-155行:检查新连接的USB设备运行速度

第152-155行:主要作用是检查新连接的USB设备是否运行在比其实际能力更慢的速度上。

if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) {…}:这是一个条件判断。如果设备udev的USB版本(由udev->descriptor.bcdUSB表示)大于等于2.0(即,设备支持高速USB)、设备的运行速度(由udev->speed表示)是全速(即,设备运行在全速模式下,而不是高速模式下)并且存在高速USB集线器(由highspeed_hubs表示),那么就执行大括号中的代码。

check_highspeed(hub, udev, port1);:调用check_highspeed函数,检查设备udev是否可以运行在高速模式下。check_highspeed函数的参数包括USB集线器hub,USB设备udev和端口号port1。

总的来说,这段代码的作用是检查新连接的USB设备是否运行在比其实际能力更慢的速度上。如果设备支持高速USB,但是运行在全速模式下,并且存在高速USB集线器,那么就需要检查设备是否可以运行在高速模式下。这是为了确保USB设备可以充分利用其性能,提供最佳的性能和用户体验。

2.19 第161-175行:将新连接的USB设备添加到其父集线器的子设备列表中

第161-175行:主要作用是将新连接的USB设备添加到其父集线器的子设备列表中,并进行错误处理。如果父设备已经被断开连接,那么就设置错误码并结束处理;否则,就将新设备添加到父设备的子设备列表中。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

2.20 第178-191行:初始化新连接的USB设备

第178-191行:主要作用是初始化新连接的USB设备,并进行错误处理。

if (!status) {…}:这是一个条件判断。如果状态值status为0,表示之前的操作成功,那么就执行大括号中的代码。

status = usb_new_device(udev);:调用usb_new_device函数,初始化设备udev,并将返回的状态值赋给status。

if (status) {…}:这是一个嵌套的条件判断。如果status不为0,表示设备初始化失败,那么就执行大括号中的代码。

mutex_lock(&usb_port_peer_mutex);:调用mutex_lock函数,锁定usb_port_peer_mutex表示的互斥锁。usb_port_peer_mutex是一个全局的互斥锁,用于保护USB端口和其对等端口之间的关系。

spin_lock_irq(&device_state_lock);:调用spin_lock_irq函数,锁定device_state_lock表示的自旋锁,并禁用本地中断。device_state_lock是一个全局的自旋锁,用于保护设备状态的并发访问。

port_dev->child = NULL;:将port_dev的子设备设置为NULL。这表示设备udev没有成功初始化,因此从父设备的子设备列表中移除。

spin_unlock_irq(&device_state_lock);:调用spin_unlock_irq函数,解锁device_state_lock表示的自旋锁,并恢复本地中断。

mutex_unlock(&usb_port_peer_mutex);:调用mutex_unlock函数,解锁usb_port_peer_mutex表示的互斥锁。

else {…}:这是上面条件判断的否定分支。如果status为0,表示设备初始化成功,那么就执行大括号中的代码。

if (hcd->usb_phy && !hdev->parent) {…}:这是一个条件判断。如果主控制器设备hcd有USB PHY接口,并且设备hdev没有父设备(即,hdev是根集线器),那么就执行大括号中的代码。

usb_phy_notify_connect(hcd->usb_phy, udev->speed);:调用usb_phy_notify_connect函数,通知PHY接口设备已经连接,并传递设备的运行速度。
总的来说,这段代码的作用是初始化新连接的USB设备,并进行错误处理。如果设备初始化失败,那么就从父设备的子设备列表中移除;否则,如果设备是根集线器,那么就通知PHY接口设备已经连接。这是在处理USB端口连接事件时,对设备初始化过程的一个常见步骤。

usb_new_device()函数内容比较多,后面单独文章分析。

2.20.1 usb_phy_notify_connect()

路径:include\linux\usb\phy.h
static inline int
usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
{if (x && x->notify_connect)return x->notify_connect(x, speed);elsereturn 0;
}

上述usb_phy 结构体中的notify_connect函数成员是由phy设备管理的。
在drivers\usb\phy\phy-mxs-usb.c 文件中:
static int mxs_phy_probe(struct platform_device *pdev) 函数内有:
mxs_phy->phy.notify_connect = mxs_phy_on_connect;
可知最终是调用mxs_phy_on_connect()函数实现notify的,函数内容如下:

路径:drivers\usb\phy\phy-mxs-usb.c
static int mxs_phy_on_connect(struct usb_phy *phy,enum usb_device_speed speed)
{dev_dbg(phy->dev, "%s device has connected\n",(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");if (speed == USB_SPEED_HIGH)writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,phy->io_priv + HW_USBPHY_CTRL_SET);return 0;
}

usb_phy_notify_disconnect()函数与 usb_phy_notify_connect的调用同理。

2.21 第193-200行:检查USB集线器的剩余电源

第193-200行:主要作用是检查USB集线器的剩余电源,并进行错误处理。

if (status) {…}:这是一个条件判断。如果状态值status不为0,表示之前的操作出错,那么就执行大括号中的代码。

goto loop_disable;:跳转到loop_disable标签。这通常表示跳出当前的函数或循环,返回到某个特定的代码位置进行下一轮的循环或操作。
status = hub_power_remaining(hub);:调用hub_power_remaining函数,获取集线器hub的剩余电源,并将结果赋值给status。

if (status) {…}:这是一个条件判断。如果状态值status不为0,表示集线器还有剩余电源,那么就执行大括号中的代码。

dev_dbg(hub->intfdev, “%dmA power budget left\n”, status);:调用dev_dbg函数,输出一条调试信息,表示集线器还有status毫安的剩余电源。
return;:结束函数的执行。

总的来说,这段代码的作用是检查USB集线器的剩余电源,并进行错误处理。如果之前的操作出错,那么就跳转到错误处理部分;否则,就获取集线器的剩余电源,并输出调试信息。这是在处理USB集线器电源管理时的一个常见步骤。

2.22 第202-203行:标签loop_disable

第202-203行:主要作用是在标签loop_disable:处调用hub_port_disable函数来禁用指定的USB集线器端口。

loop_disable::这是一个标签,用于在代码中标记一个位置,以便在其他地方通过goto语句跳转到这里。

hub_port_disable(hub, port1, 1);:调用hub_port_disable函数,禁用集线器hub的端口port1。函数的第三个参数为1,表示在禁用端口后,还要重置端口。这通常用于错误处理,当端口出现错误时,需要禁用并重置端口以恢复其正常状态。

总的来说,这段代码的作用是在出现错误时,跳转到loop_disable标签处,禁用并重置出现错误的USB集线器端口。这是一个常见的错误处理步骤,用于恢复设备的正常状态。

2.22.1 hub_port_disable()函数

static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{struct usb_port *port_dev = hub->ports[port1 - 1];struct usb_device *hdev = hub->hdev;int ret = 0;if (!hub->error) {if (hub_is_superspeed(hub->hdev)) {hub_usb3_port_prepare_disable(hub, port_dev);ret = hub_set_port_link_state(hub, port_dev->portnum,USB_SS_PORT_LS_U3);} else {ret = usb_clear_port_feature(hdev, port1,USB_PORT_FEAT_ENABLE);}}if (port_dev->child && set_state)usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);if (ret && ret != -ENODEV)dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);return ret;
}

主要作用是定义一个函数hub_port_disable,该函数用于禁用指定的USB集线器端口。

static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) {…}:定义了一个名为hub_port_disable的函数,接收一个指向usb_hub结构体的指针hub、一个整型参数port1和一个整型参数set_state作为参数。

struct usb_port *port_dev = hub->ports[port1 - 1];:从集线器hub的端口数组中获取第port1 - 1个端口,将其地址赋值给port_dev。

struct usb_device *hdev = hub->hdev;:获取集线器hub的设备,将其地址赋值给hdev。

int ret = 0;:定义一个整型变量ret,并初始化为0。ret通常用于表示函数或操作的返回值,0通常表示成功。

if (!hub->error) {…}:这是一个条件判断。如果集线器hub没有错误,那么就执行大括号中的代码。

if (hub_is_superspeed(hub->hdev)) {…}:这是一个嵌套的条件判断。如果设备hdev是一个超速设备,那么就执行大括号中的代码。

hub_usb3_port_prepare_disable(hub, port_dev);:调用hub_usb3_port_prepare_disable函数,准备禁用端口port_dev。

ret = hub_set_port_link_state(hub, port_dev->portnum, USB_SS_PORT_LS_U3);:调用hub_set_port_link_state函数,将端口port_dev的链接状态设置为USB_SS_PORT_LS_U3,并将返回的状态值赋值给ret。

else {…}:这是上面条件判断的否定分支。如果设备hdev不是一个超速设备,那么就执行大括号中的代码。

ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);:调用usb_clear_port_feature函数,清除端口port1的使能特性,并将返回的状态值赋值给ret。
if (port_dev->child && set_state) {…}:这是一个条件判断。如果端口port_dev有子设备,并且set_state为真,那么就执行大括号中的代码。

usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);:调用usb_set_device_state函数,将端口port_dev的子设备的状态设置为USB_STATE_NOTATTACHED。
if (ret && ret != -ENODEV) {…}:这是一个条件判断。如果ret不为0并且ret不等于-ENODEV,那么就执行大括号中的代码。

dev_err(&port_dev->dev, “cannot disable (err = %d)\n”, ret);:调用dev_err函数,输出一条错误信息,表示无法禁用端口,错误码为ret。
return ret;:返回ret。

总的来说,这个函数的作用是禁用指定的USB集线器端口。如果集线器没有错误,那么就根据设备的类型,选择不同的方式来禁用端口;如果端口有子设备,并且需要设置状态,那么就将子设备的状态设置为USB_STATE_NOTATTACHED;如果禁用端口失败,那么就输出错误信息。

2.23 第204-231行:处理USB设备的初始化和枚举过程中的错误和异常情况

第204-231行:主要用于处理USB设备的初始化和枚举过程中的错误和异常情况。

loop::这是一个标签,用于在代码中标记一个位置,以便在其他地方通过goto语句跳转到这里。

usb_ep0_reinit(udev);:调用usb_ep0_reinit函数,重新初始化设备udev的端点0。端点0是USB设备的默认控制端点,用于处理设备级的命令和请求。

release_devnum(udev);:调用release_devnum函数,释放设备udev的设备号。设备号是用于唯一标识USB设备的一个编号。

hub_free_dev(udev);:调用hub_free_dev函数,释放设备udev。这通常表示设备udev已经被移除或者禁用。

if (retry_locked) {…}:这是一个条件判断。如果retry_locked为真,表示需要重试,那么就执行大括号中的代码。

mutex_unlock(hcd->address0_mutex);:调用mutex_unlock函数,解锁hcd->address0_mutex表示的互斥锁。hcd->address0_mutex是一个全局的互斥锁,用于保护设备地址的并发访问。

usb_unlock_port(port_dev);:调用usb_unlock_port函数,解锁端口port_dev。这通常表示端口port_dev已经完成了一个操作,可以进行下一个操作。

usb_put_dev(udev);:调用usb_put_dev函数,减少设备udev的引用计数。当设备的引用计数减少到0时,设备会被释放。

if ((status == -ENOTCONN) || (status == -ENOTSUPP)) {…}:这是一个条件判断。如果状态值status等于-ENOTCONN或者-ENOTSUPP,那么就执行大括号中的代码。

break;:跳出当前的循环。
if (i == (PORT_INIT_TRIES - 1) / 2) {…}:这是一个条件判断。如果当前是在尝试次数的一半时,那么就执行大括号中的代码。

dev_info(&port_dev->dev, “attempt power cycle\n”);:调用dev_info函数,输出一条信息,表示尝试进行电源循环。

usb_hub_set_port_power(hdev, hub, port1, false);:调用usb_hub_set_port_power函数,关闭集线器hub的端口port1的电源。

msleep(2 * hub_power_on_good_delay(hub));:调用msleep函数,暂停执行一段时间。暂停的时间是集线器hub的上电好延迟的两倍。

usb_hub_set_port_power(hdev, hub, port1, true);:调用usb_hub_set_port_power函数,打开集线器hub的端口port1的电源。

msleep(hub_power_on_good_delay(hub));:调用msleep函数,暂停执行一段时间。暂停的时间是集线器hub的上电好延迟。

if (hub->hdev->parent || !hcd->driver->port_handed_over || !(hcd->driver->port_handed_over)(hcd, port1)) {…}:这是一个条件判断。如果集线器hub有父设备,或者主控制器设备hcd的驱动没有port_handed_over函数,或者port_handed_over函数的返回值为假,那么就执行大括号中的代码。

if (status != -ENOTCONN && status != -ENODEV) {…}:这是一个嵌套的条件判断。如果状态值status不等于-ENOTCONN并且不等于-ENODEV,那么就执行大括号中的代码。

dev_err(&port_dev->dev, “unable to enumerate USB device\n”);:调用dev_err函数,输出一条错误信息,表示无法枚举USB设备。
总的来说,这段代码的作用是在USB设备的初始化和枚举过程中处理错误和异常情况。如果设备的初始化或枚举失败,那么就释放设备,解锁端口,减少设备的引用计数,并根据错误的类型进行相应的处理。如果设备的初始化或枚举多次失败,那么就尝试进行电源循环,以恢复设备的正常状态。

2.24 第233-238行:处理USB设备初始化和枚举过程结束后的操作

第7-10行:主要用于处理USB设备初始化和枚举过程结束后的一些操作。

done::这是一个标签,用于在代码中标记一个位置,以便在其他地方通过goto语句跳转到这里。

hub_port_disable(hub, port1, 1);:调用hub_port_disable函数,禁用集线器hub的端口port1。函数的第三个参数为1,表示在禁用端口后,还要重置端口。这通常用于错误处理,当端口出现错误时,需要禁用并重置端口以恢复其正常状态。

if (hcd->driver->relinquish_port && !hub->hdev->parent) {…}:这是一个条件判断。如果主控制器设备hcd的驱动有relinquish_port函数,并且集线器hub没有父设备(即,hub是根集线器),那么就执行大括号中的代码。

if (status != -ENOTCONN && status != -ENODEV) {…}:这是一个嵌套的条件判断。如果状态值status不等于-ENOTCONN并且不等于-ENODEV,那么就执行大括号中的代码。

hcd->driver->relinquish_port(hcd, port1);:调用hcd->driver->relinquish_port函数,释放主控制器设备hcd的端口port1。这通常表示端口port1已经完成了一个操作,可以被其他设备或操作使用。
总的来说,这段代码的作用是在USB设备的初始化和枚举过程结束后处理一些清理工作,如禁用并重置出现错误的端口,释放已经完成操作的端口等。这些操作是为了确保USB设备和端口的正确状态,避免因错误或异常而导致的问题。

三、总结

以上是对USB port event下的端口链接发生变化处理函数hub_port_connect()的分析。后面将继续分析,hub_port_init () 和 usb_new_device()函数,前者是初始化一个port端口,后者则是在USB设备枚举后创建一个具体的USB设备 并添加到设备管理中。后面文章继续分析。

相关文章:

<kernel>kernel 6.4 USB-之-hub_port_connect()分析

&#xff1c;kernel&#xff1e;kernel 6.4 USB-之-hub_port_connect()分析 kernel 6.4 USB系列文章如下&#xff1a; &#xff1c;kernel&#xff1e;kernel 6.4 USB-之-hub_event()分析 &#xff1c;kernel&#xff1e;kernel 6.4 USB-之-port_event()分析 &#xff1c;kern…...

linux驱动学习3-外部中断

在做中断试验时&#xff0c;发现中断驱动总是insmod失败&#xff0c;之后定位到 gpio_request 失败&#xff0c;之后是想到使用的野火做好的系统&#xff0c;在uEnv.txt中会加载大量设备树插件&#xff0c;将key相关的设备树插件屏蔽即可。 linux中断API函数 中断号 每个中断…...

vue中的canvas插件

vue中canvas插件有vue-konva、vue-fabricjs、vue-canvas-effect、vue-chartjs和vue-threejs等。详细介绍&#xff1a;1、vue-konva是一个用于在Vue.js中使用Konva.js的插件&#xff0c;Konva.js是一个功能强大的HTML5 2D 渲染引擎&#xff0c;可以用于创建交互式的Canvas应用程…...

分享图片 | 快速浏览网页资源,批量保存、一键分享图片

前言 小伙伴学习吉他&#xff0c;有时需要在互联网搜索曲谱资源&#xff0c;而多数曲谱均为图片&#xff0c;并且为多页&#xff0c;在电脑上显示练习很不方便&#xff0c;需要停下来点击鼠标进行翻页&#xff0c;影响练习的连贯性。 为了解决上述问题&#xff0c;通常把图片…...

Programming abstractions in C阅读笔记:p123-p126

《Programming Abstractions In C》学习第50天&#xff0c;p123-p126&#xff0c;总结如下&#xff1a; 一、技术总结 1.notaion 这也是一个在计算机相关书籍中出现的词&#xff0c;但有时却不是那么好理解&#xff0c;因为它可以指代很多对象&#xff0c;这里做一个记录。示…...

自然语言处理从入门到应用——LangChain:链(Chains)-[通用功能:LLMChain、RouterChain和SequentialChain]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 LLMChain LLMChain是查询LLM对象最流行的方式之一。它使用提供的输入键值&#xff08;如果有的话&#xff0c;还包括内存键值&#xff09;格式化提示模板&#xff0c;将格式化的字符串传递给LLM&#xff0c;并返回LLM…...

ElasticSearch-安装部署全过程

本文已收录于专栏 《中间件合集》 目录 概念说明什么是ElasticSearch什么是Kibana什么是RESTful API 提供服务安装过程安装ElasticSearch1.下载ElasticSearch 安装包2.解压安装包3.进入解压之后的文件夹4.创建一个data文件夹用来存储数据5.进入config文件夹编辑elasticsearch.y…...

mathematica报错:Tag Plus is \ Protected

在使用化简函数Simplify的时候使用了规则的语法&#xff0c;但是规则可能没有使用等号。 例如 Simplify[(1 - c^2)/d^2, c^2 d^2 1]等号被认为是赋值符号&#xff0c;要修改为两个等号&#xff1a; Simplify[(1 - c^2)/d^2, c^2 d^2 1]这样就不会报错了。...

Python Django 模型概述与应用

今天来为大家介绍 Django 框架的模型部分&#xff0c;模型是真实数据的简单明确的描述&#xff0c;它包含了储存的数据所必要的字段和行为&#xff0c;Django 遵循 DRY Principle 。它的目标是你只需要定义数据模型&#xff0c;然后其它的杂七杂八代码你都不用关心&#xff0c;…...

Golang Gorm 更新字段 save update updates

更新和删除操作的前提条件都是要在找到数据的情况下&#xff0c;先要查询到数据才可以做操作。 更新的前提的先查询到记录&#xff0c;Save保存所有字段&#xff0c;用于单个记录的全字段更新它会保控所有字段&#xff0c;即使零值也会保存。 在更新和删除之前&#xff0c;要利…...

springBoot 配置文件引入 redis 的相关参数说明

在Spring Boot应用中使用Redis作为缓存或数据存储时&#xff0c;可以在应用的配置文件中配置相关参数。下面是常用的Redis配置参数及其说明&#xff1a; spring.redis.host: Redis服务器主机地址&#xff0c;默认为localhost。spring.redis.port: Redis服务器端口&#xff0c;…...

Docker的使用心得:简化开发与部署的利器

开发与测试的无缝衔接&#xff1a; Docker让开发与测试之间的切换变得前所未有的顺畅。我可以在本地开发环境中创建一个与生产环境一致的Docker容器&#xff0c;这样不仅可以确保开发过程中不会出现意外问题&#xff0c;还可以在测试阶段避免不必要的繁琐配置。 跨平台的可移植…...

vue3 基于element plus对el-pagination进行二次封装

vue3 基于element plus对el-pagination进行二次封装 1、前言2、在components文件夹中新建pagination.vue文件3、在组件内使用分页 1、前言 在vue3项目中&#xff0c;如果每个列表页都敲一遍分页方法&#xff0c;显然是不合理的&#xff0c;那么&#xff0c;下面我将基于elemen…...

RuntimeError: result type Float can‘t be cast to the desired output type __int64报错解决方法

小白刚开始学习YOLOv5&#xff0c;跟随老哥的步骤走了一遍目标检测--手把手教你搭建自己的YOLOv5目标检测平台 最后训练最后一步出现RuntimeError: result type Float can‘t be cast to the desired output type __int64报错 解决方法&#xff1a;找到5.0版报错的loss.py中最…...

解析Python爬虫常见异常及处理方法

作为专业爬虫程序猿长期混迹于爬虫ip解决方案中&#xff0c;我们经常会遇到各种各样的异常情况。在爬虫开发过程中&#xff0c;处理这些异常是不可或缺的一部分。本文将为大家总结常见的Python爬虫异常&#xff0c;并分享相应的处理方法&#xff0c;帮助你避免绊倒在爬虫之路上…...

详解Spring的循环依赖问题、三级缓存解决方案源码分析

0、基础&#xff1a;Bean的生命周期 在Spring中&#xff0c;由于IOC的控制反转&#xff0c;创建对象不再是简单的new出来&#xff0c;而是交给Spring去创建&#xff0c;会经历一系列Bean的生命周期才创建出相应的对象。而循环依赖问题也是由Bean的生命周期过程导致的问题&#…...

oracle分析函数学习

0、建表及插入测试数据 --CREATE TEST TABLE AND INSERT TEST DATA. create table students (id number(15,0), area varchar2(10), stu_type varchar2(2), score number(20,2));insert into students values(1, 111, g, 80 ); insert into students values(1, 111, j, 80 ); …...

代码随想录训练营day17|110.平衡二叉树 257. 二叉树的所有路径 404.左叶子之和 v...

TOC 前言 代码随想录算法训练营day17 一、Leetcode 110.平衡二叉树 1.题目 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#x…...

C# Thread用法

C# 中的线程&#xff08;Thread&#xff09;是一种并发执行的机制&#xff0c;允许同时执行多个代码块&#xff0c;从而提高程序的性能和响应性。下面是关于如何使用 C# 线程的一些基本用法&#xff1a; 1. 创建线程&#xff1a; 使用 System.Threading 命名空间中的 Thread 类…...

新榜 | CityWalk本地生活商业价值洞察报告

如果说现在有人问&#xff0c;最新的网络热词是什么? “CityWalk”&#xff0c;这可能是大多数人的答案。 近段时间&#xff0c;“CityWalk”刷屏了各种社交媒体&#xff0c;给网友们带来了一场“城市漫步”之旅。 脱离群体狂欢&#xff0c;这个在社交媒体引发热议的词汇背后又…...

LVS负载均衡集群-NAT模式部署

集群 集群&#xff1a;将多台主机作为一个整体&#xff0c;然后对外提供相同的服务 集群使用场景&#xff1a;高并发的场景 集群的分类 1.负载均衡器集群 减少响应延迟&#xff0c;提高并发处理的能力 2&#xff0c;高可用集群 增强系统的稳定性可靠性&…...

C++学习笔记总结练习:effective 学习日志

准则 1.少使用define define所定义的常量会在预处理的时候被替代&#xff0c;出错编译器不容易找到错误。而且还没有作用范围限制&#xff0c;推荐使用constdefine宏定义的函数&#xff0c;容易出错&#xff0c;而且参数需要加上小括号&#xff0c;推荐使用inline有的类中例如…...

Vue教程(五):样式绑定——class和style

1、样式代码准备 样式提前准备 <style>.basic{width: 400px;height: 100px;border: 1px solid black;}.happy{border: 4px solid red;background-color: rgba(255, 255, 0, 0.644);background: linear-gradient(30deg, yellow, pink, orange, yellow);}.sad{border: 4px …...

开放网关架构演进

作者&#xff1a;庄文弘&#xff08;弘智&#xff09; 淘宝开放平台是阿里与外部生态互联互通的重要开放途径&#xff0c;通过开放的产品技术把阿里经济体一系列基础服务&#xff0c;像水、电、煤一样输送给我们的商家、开发者、社区媒体以及其他合作伙伴&#xff0c;推动行业的…...

torch一些操作

Pytorch文档 Pytorch 官方文档 https://pytorch.org/docs/stable/index.html pytorch 里的一些基础tensor操作讲的不错 https://blog.csdn.net/abc13526222160/category_8614343.html 关于pytorch的Broadcast,合并与分割,数学运算,属性统计以及高阶操作 https://blog.csd…...

ICCV23 | Ada3D:利用动态推理挖掘3D感知任务中数据冗余性

​ 论文地址&#xff1a;https://arxiv.org/abs/2307.08209 项目主页&#xff1a;https://a-suozhang.xyz/ada3d.github.io/ 01. 背景与动因 3D检测(3D Detection)任务是自动驾驶任务中的重要任务。由于自动驾驶任务的安全性至关重要(safety-critic)&#xff0c;对感知算法的延…...

软件工程模型-架构师之路(四)

软件工程模型 敏捷开发&#xff1a; 个体和交互 胜过 过程和工具、可以工作的软件 胜过 面面俱到的文件、客户合作胜过合同谈判、响应变化 胜过 循序计划。&#xff08;适应需求变化&#xff0c;积极响应&#xff09; 敏捷开发与其他结构化方法区别特点&#xff1a;面向人的…...

ubuntu20.04共享文件夹—— /mnt/hgfs里没有共享文件夹

参考文章&#xff1a;https://blog.csdn.net/Edwinwzy/article/details/129580636 虚拟机启用共享文件夹后&#xff0c;/mnt/hgfs下面为空&#xff0c;使用 vmware-hgfsclient 查看设置的共享文件夹名字也是为空。 解决方法&#xff1a; 1. 重新安装vmware tools. 在菜单…...

Redis中的有序集合及其底层跳表

前言 本文着重介绍Redis中的有序集合的底层实现中的跳表 有序集合 Sorted Set Redis中的Sorted Set 是一个有序的无重复值的集合&#xff0c;他底层是使用压缩列表和跳表实现的&#xff0c;和Java中的HashMap底层数据结构&#xff08;1.8&#xff09;链表红黑树异曲同工之妙…...

js 小程序限流函数 return闭包函数执行不了

问题&#xff1a; 调用限流 &#xff0c;没走闭包的函数&#xff1a; checkBalanceReq&#xff08;&#xff09; loadsh.js // 限流 const throttle (fn, context, interval) > {console.log(">>>>cmm throttle", context, interval)let canRun…...

建站源码/如何制作网址链接

本文由yuanbin和九章算法协同著作。 网站推荐 GeeksforGeeks.org 非常著名的漏题网站之一。上面会时不时的有各种公司的面试真题漏出。有一些题也会有解法分析。CareerCup.com CC150作者搞的网站&#xff0c;也是著名的漏题网站之一。大家会在上面讨论各个公司的面试题。Glassd…...

seo短视频网页/汕头seo托管

要使用 Spring Cloud Alibaba 实现第三方单点登录&#xff0c;并以微信作为第三方身份提供者&#xff0c;你可以遵循以下步骤&#xff1a; 引入相关依赖。在你的 pom.xml 文件中添加以下依赖&#xff1a; <dependency><groupId>org.springframework.boot</gro…...

如何建设网站兴田德润可以吗/新闻发稿平台有哪些

内容&#xff1a;2019年大连市各类型POI。 数据格式&#xff1a;.csv格式&#xff0c;经纬度为wgs1984坐标系 数据类型&#xff1a;餐饮、公共设施、公司企业、购物、交通设施服务、金融保险服 务、科教文化服务、商务住宅、生活服务、体育休闲服务、医疗保健服务、政府机构及社…...

宝安建设网站公司/营销推广活动策划方案

GraphQL 是一种用于 API 的查询语言&#xff0c;GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述&#xff0c;使得客户端能够准确地获得它需要的数据&#xff0c;而且没有任何冗余&#xff0c;也让 API 更容易地随着时间推移而演进&#xff0c;还能用于构建强大的开发…...

西安学校网站建设/网络营销的特征

WebService标签 使用WebService标签&#xff0c;需要配置在类上&#xff0c;代表这是一个提供WS的服务类。 endpointInterface&#xff1a;定义服务抽象WebService 协定的服务端点接口的完整名称。不允许在端点上使用此成员值&#xff0c;该元素的值必须有WebService标签。默认…...

wordpress dedecms discuz/推广公司运营模式

mv功能说明&#xff1a;移动或剪切文件或目录&#xff0c;也可以改名语法&#xff1a;mv [-fiu] source destinationmv [options] source1 source2 source3... directory参数&#xff1a;-f &#xff1a;force强制的意思&#xff0c;若目标文件已经存在&#xff0c;不会询问而直…...