<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;
else
unit_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;
else
udev->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;
else
port_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);
else
return 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;
} else
port_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);
else
return 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设备 并添加到设备管理中。后面文章继续分析。