<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()分析
<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()分析 <kern…...

linux驱动学习3-外部中断
在做中断试验时,发现中断驱动总是insmod失败,之后定位到 gpio_request 失败,之后是想到使用的野火做好的系统,在uEnv.txt中会加载大量设备树插件,将key相关的设备树插件屏蔽即可。 linux中断API函数 中断号 每个中断…...
vue中的canvas插件
vue中canvas插件有vue-konva、vue-fabricjs、vue-canvas-effect、vue-chartjs和vue-threejs等。详细介绍:1、vue-konva是一个用于在Vue.js中使用Konva.js的插件,Konva.js是一个功能强大的HTML5 2D 渲染引擎,可以用于创建交互式的Canvas应用程…...

分享图片 | 快速浏览网页资源,批量保存、一键分享图片
前言 小伙伴学习吉他,有时需要在互联网搜索曲谱资源,而多数曲谱均为图片,并且为多页,在电脑上显示练习很不方便,需要停下来点击鼠标进行翻页,影响练习的连贯性。 为了解决上述问题,通常把图片…...
Programming abstractions in C阅读笔记:p123-p126
《Programming Abstractions In C》学习第50天,p123-p126,总结如下: 一、技术总结 1.notaion 这也是一个在计算机相关书籍中出现的词,但有时却不是那么好理解,因为它可以指代很多对象,这里做一个记录。示…...
自然语言处理从入门到应用——LangChain:链(Chains)-[通用功能:LLMChain、RouterChain和SequentialChain]
分类目录:《自然语言处理从入门到应用》总目录 LLMChain LLMChain是查询LLM对象最流行的方式之一。它使用提供的输入键值(如果有的话,还包括内存键值)格式化提示模板,将格式化的字符串传递给LLM,并返回LLM…...

ElasticSearch-安装部署全过程
本文已收录于专栏 《中间件合集》 目录 概念说明什么是ElasticSearch什么是Kibana什么是RESTful API 提供服务安装过程安装ElasticSearch1.下载ElasticSearch 安装包2.解压安装包3.进入解压之后的文件夹4.创建一个data文件夹用来存储数据5.进入config文件夹编辑elasticsearch.y…...
mathematica报错:Tag Plus is \ Protected
在使用化简函数Simplify的时候使用了规则的语法,但是规则可能没有使用等号。 例如 Simplify[(1 - c^2)/d^2, c^2 d^2 1]等号被认为是赋值符号,要修改为两个等号: Simplify[(1 - c^2)/d^2, c^2 d^2 1]这样就不会报错了。...

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

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

springBoot 配置文件引入 redis 的相关参数说明
在Spring Boot应用中使用Redis作为缓存或数据存储时,可以在应用的配置文件中配置相关参数。下面是常用的Redis配置参数及其说明: spring.redis.host: Redis服务器主机地址,默认为localhost。spring.redis.port: Redis服务器端口,…...
Docker的使用心得:简化开发与部署的利器
开发与测试的无缝衔接: Docker让开发与测试之间的切换变得前所未有的顺畅。我可以在本地开发环境中创建一个与生产环境一致的Docker容器,这样不仅可以确保开发过程中不会出现意外问题,还可以在测试阶段避免不必要的繁琐配置。 跨平台的可移植…...
vue3 基于element plus对el-pagination进行二次封装
vue3 基于element plus对el-pagination进行二次封装 1、前言2、在components文件夹中新建pagination.vue文件3、在组件内使用分页 1、前言 在vue3项目中,如果每个列表页都敲一遍分页方法,显然是不合理的,那么,下面我将基于elemen…...
RuntimeError: result type Float can‘t be cast to the desired output type __int64报错解决方法
小白刚开始学习YOLOv5,跟随老哥的步骤走了一遍目标检测--手把手教你搭建自己的YOLOv5目标检测平台 最后训练最后一步出现RuntimeError: result type Float can‘t be cast to the desired output type __int64报错 解决方法:找到5.0版报错的loss.py中最…...

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

详解Spring的循环依赖问题、三级缓存解决方案源码分析
0、基础:Bean的生命周期 在Spring中,由于IOC的控制反转,创建对象不再是简单的new出来,而是交给Spring去创建,会经历一系列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.题目 给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#x…...
C# Thread用法
C# 中的线程(Thread)是一种并发执行的机制,允许同时执行多个代码块,从而提高程序的性能和响应性。下面是关于如何使用 C# 线程的一些基本用法: 1. 创建线程: 使用 System.Threading 命名空间中的 Thread 类…...

新榜 | CityWalk本地生活商业价值洞察报告
如果说现在有人问,最新的网络热词是什么? “CityWalk”,这可能是大多数人的答案。 近段时间,“CityWalk”刷屏了各种社交媒体,给网友们带来了一场“城市漫步”之旅。 脱离群体狂欢,这个在社交媒体引发热议的词汇背后又…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...