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

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

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()分析
<kernel>kernel 6.4 USB-之-hub_port_connect()分析
<kernel>kernel 6.4 USB-之-hub_port_init()分析
<kernel>kernel 6.4 USB-之-usb_new_device()分析

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

一、前言

在前一篇文章分析了hub_event()函数,之后hub_event()函数会进一步调用 port_event函数;

这个函数port_event的主要作用是处理USB集线器(Hub)上某个特定端口的事件。
具体来看,代码的主要功能如下:

首先,它会检查端口的变化位(change bits)。如果端口的连接状态(connection)或使能状态(enable)发生变化,它会清除相应的端口特性(port feature)。如果端口被禁用并且没有连接变化,它会尝试重新启用端口并将连接更改标志设置为1。

然后,它会检查是否发生了过流(overcurrent)变化。如果过流变化发生,它会清除过流特性,并尝试打开集线器的电源。如果存在过流条件,它会打印一条错误信息。

接着,它会检查端口是否发生了复位(reset)变化。如果复位变化发生,它会清除复位特性。

然后,它会检查端口是否需要进行热复位(warm reset)。如果需要,它会进行一系列的操作来进行热复位,包括尝试复位端口、禁用端口、或者复位设备。

最后,如果连接状态发生变化,它会调用hub_port_connect_change函数来处理连接状态的变化。

所以,这个函数的主要作用是处理USB集线器上某个特定端口的事件,包括连接变化、使能变化、过流变化、复位变化等,以及对这些变化进行相应的处理。

二、port_event()函数

port_event()函数内容如下:

路径:drivers\usb\core\hub.c
static void port_event(struct usb_hub *hub, int port1)
		__must_hold(&port_dev->status_lock)
{
	int connect_change;
	struct usb_port *port_dev = hub->ports[port1 - 1];
	struct usb_device *udev = port_dev->child;
	struct usb_device *hdev = hub->hdev;
	u16 portstatus, portchange;
	int i = 0;

	connect_change = test_bit(port1, hub->change_bits);
	clear_bit(port1, hub->event_bits);
	clear_bit(port1, hub->wakeup_bits);

	if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0)
		return;

	if (portchange & USB_PORT_STAT_C_CONNECTION) {
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
		connect_change = 1;
	}

	if (portchange & USB_PORT_STAT_C_ENABLE) {
		if (!connect_change)
			dev_dbg(&port_dev->dev, "enable change, status %08x\n",
					portstatus);
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

		/*
		 * EM interference sometimes causes badly shielded USB devices
		 * to be shutdown by the hub, this hack enables them again.
		 * Works at least with mouse driver.
		 */
		if (!(portstatus & USB_PORT_STAT_ENABLE)
		    && !connect_change && udev) {
			dev_err(&port_dev->dev, "disabled by hub (EMI?), re-enabling...\n");
			connect_change = 1;
		}
	}

	if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
		u16 status = 0, unused;
		port_dev->over_current_count++;
		port_over_current_notify(port_dev);

		dev_dbg(&port_dev->dev, "over-current change #%u\n",
			port_dev->over_current_count);
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_OVER_CURRENT);
		msleep(100);	/* Cool down */
		hub_power_on(hub, true);
		usb_hub_port_status(hub, port1, &status, &unused);
		if (status & USB_PORT_STAT_OVERCURRENT)
			dev_err(&port_dev->dev, "over-current condition\n");
	}

	if (portchange & USB_PORT_STAT_C_RESET) {
		dev_dbg(&port_dev->dev, "reset change\n");
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET);
	}
	if ((portchange & USB_PORT_STAT_C_BH_RESET)
	    && hub_is_superspeed(hdev)) {
		dev_dbg(&port_dev->dev, "warm reset change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_BH_PORT_RESET);
	}
	if (portchange & USB_PORT_STAT_C_LINK_STATE) {
		dev_dbg(&port_dev->dev, "link state change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_PORT_LINK_STATE);
	}
	if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
		dev_warn(&port_dev->dev, "config error\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
	}

	/* skip port actions that require the port to be powered on */
	if (!pm_runtime_active(&port_dev->dev))
		return;

	/* skip port actions if ignore_event and early_stop are true */
	if (port_dev->ignore_event && port_dev->early_stop)
		return;

	if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
		connect_change = 1;

	/*
	 * Avoid trying to recover a USB3 SS.Inactive port with a warm reset if
	 * the device was disconnected. A 12ms disconnect detect timer in
	 * SS.Inactive state transitions the port to RxDetect automatically.
	 * SS.Inactive link error state is common during device disconnect.
	 */
	while (hub_port_warm_reset_required(hub, port1, portstatus)) {
		if ((i++ < DETECT_DISCONNECT_TRIES) && udev) {
			u16 unused;

			msleep(20);
			usb_hub_port_status(hub, port1, &portstatus, &unused);
			dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n");
			continue;
		} else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
				|| udev->state == USB_STATE_NOTATTACHED) {
			dev_dbg(&port_dev->dev, "do warm reset, port only\n");
			if (hub_port_reset(hub, port1, NULL,
					HUB_BH_RESET_TIME, true) < 0)
				hub_port_disable(hub, port1, 1);
		} else {
			dev_dbg(&port_dev->dev, "do warm reset, full device\n");
			usb_unlock_port(port_dev);
			usb_lock_device(udev);
			usb_reset_device(udev);
			usb_unlock_device(udev);
			usb_lock_port(port_dev);
			connect_change = 0;
		}
		break;
	}

	if (connect_change)
		hub_port_connect_change(hub, port1, portstatus, portchange);
}

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

2.1 第6行

第6行:从集线器的ports数组中获取第port1个端口的struct usb_port结构体的指针,并将这个指针赋值给port_dev变量。这里的port1是端口的编号,编号从1开始,所以在数组中的索引需要减1。struct usb_port结构体代表了USB集线器上的一个端口,它包含了端口的状态、端口上连接的设备等信息。通过获取struct usb_port结构体的指针,我们可以方便地操作和管理集线器上的端口。

第7行:从port_dev结构体的child成员中获取连接在这个端口上的USB设备的struct usb_device结构体的指针,并将这个指针赋值给udev变量。struct usb_device结构体代表了一个USB设备,它包含了设备的状态、设备的配置等信息。通过获取struct usb_device结构体的指针,我们可以方便地操作和管理USB设备。

2.2 第12-14行:检查USB集线器(Hub)上的特定端口是否发生了连接变化

第12-14行:主要作用是检查USB集线器(Hub)上的特定端口是否发生了连接变化,并清除该端口的事件位和唤醒位。
具体来看,代码的作用如下:
(1)connect_change = test_bit(port1, hub->change_bits);:这行代码检查集线器的变化位中第port1个位是否被设置。如果被设置,那么表示该端口发生了连接变化。这个结果被赋值给connect_change变量。
(2)clear_bit(port1, hub->event_bits);:这行代码清除集线器的事件位中第port1个位。这通常表示已经处理了该端口的事件。
(3)clear_bit(port1, hub->wakeup_bits);:这行代码清除集线器的唤醒位中第port1个位。这通常表示已经处理了该端口的唤醒事件。
所以,这段代码的主要作用是检查USB集线器上的特定端口是否发生了连接变化,并清除该端口的事件位和唤醒位。

2.3 第16-17行:获取USB集线器(Hub)上特定端口的状态和状态变化

第16-17行:主要作用是获取USB集线器(Hub)上特定端口的状态和状态变化。
(1)if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0) {…}:这个if语句调用usb_hub_port_status函数来获取集线器上第port1个端口的状态和状态变化。portstatus和portchange是输出参数,用于保存端口的状态和状态变化。如果usb_hub_port_status函数返回值小于0,表示获取状态失败,那么就直接返回,不进行后续的操作。
(2)usb_hub_port_status函数通常用于读取集线器上的端口状态寄存器,获取端口的当前状态和状态变化。端口的状态包括连接状态、使能状态、复位状态等;状态变化包括连接变化、使能变化、复位变化等。
所以,这段代码的主要作用是获取USB集线器上特定端口的状态和状态变化,如果获取状态失败,那么就直接返回,不进行后续的操作。

2.3.1 usb_hub_port_status()函数

usb_hub_port_status()函数内容如下:

static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
			       u16 *status, u16 *change, u32 *ext_status)
{
	int ret;
	int len = 4;

	if (type != HUB_PORT_STATUS)
		len = 8;

	mutex_lock(&hub->status_mutex);
	ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
	if (ret < len) {
		if (ret != -ENODEV)
			dev_err(hub->intfdev,
				"%s failed (err = %d)\n", __func__, ret);
		if (ret >= 0)
			ret = -EIO;
	} else {
		*status = le16_to_cpu(hub->status->port.wPortStatus);
		*change = le16_to_cpu(hub->status->port.wPortChange);
		if (type != HUB_PORT_STATUS && ext_status)
			*ext_status = le32_to_cpu(
				hub->status->port.dwExtPortStatus);
		ret = 0;
	}
	mutex_unlock(&hub->status_mutex);
	return ret;
}

int usb_hub_port_status(struct usb_hub *hub, int port1,
		u16 *status, u16 *change)
{
	return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
				   status, change, NULL);
}

2.4 第19-22行:检查USB集线器(Hub)上特定端口是否发生了连接变化

第19-22行:主要作用是检查USB集线器(Hub)上特定端口是否发生了连接变化,并清除连接变化标志。
具体来看,代码的作用如下:
(1) if (portchange & USB_PORT_STAT_C_CONNECTION) {…}:这个if语句检查portchange中的连接变化位是否被设置。USB_PORT_STAT_C_CONNECTION是一个位掩码,用于在portchange中选出连接变化位。如果连接变化位被设置,那么表示该端口的连接状态发生了变化,就执行后面的代码块。
(2) usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);:这行代码清除集线器上第port1个端口的连接变化特性。这通常表示已经处理了该端口的连接变化。

2.4.1 usb_clear_port_feature()

/* USB 2.0规范第11.24.2.2节 */
int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
{
	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
		NULL, 0, 1000);
}

(3) connect_change = 1;:这行代码将connect_change变量设置为1,表示该端口的连接状态发生了变化。

所以,这段代码的主要作用是检查USB集线器上特定端口是否发生了连接变化,并清除连接变化标志。

2.5 第24-40行:处理USB端口的状态变化

第24-40行:主要处理USB端口的状态变化,特别是当USB端口的使能状态发生变化时的情况。
(1) 首先,通过检查portchange & USB_PORT_STAT_C_ENABLE来判断USB端口的使能状态是否发生变化。如果变化了,那么进入该if语句块。
(2) 然后,如果connect_change为假(表示没有连接变化),则打印一条调试信息,显示当前端口的状态。
(3) 接着,调用usb_clear_port_feature函数来清除端口的使能状态变化特性。
在注释中提到,由于电磁干扰,一些屏蔽不良的USB设备可能会被集线器关闭。因此,这段代码的下一部分就是处理这种情况的。如果端口没有被使能,并且没有连接变化,且udev不为空(表示有设备连接),那么打印一条错误信息,并将connect_change设置为1,表示需要重新使能设备。

2.6 第42-56行:处理USB端口的过流状态

第42-56行:主要处理USB端口的过流(over-current)状态变化。
(1) 首先,通过检查portchange & USB_PORT_STAT_C_OVERCURRENT来判断USB端口的过流状态是否发生变化。如果变化了,那么进入该if语句块。
(2) 然后,port_dev->over_current_count++用于累计过流次数。
(3) port_over_current_notify(port_dev)函数用来通知相关设备或系统USB端口过流。

2.6.1 port_over_current_notify(port_dev)函数

/* Handle notifying userspace about hub over-current events */
static void port_over_current_notify(struct usb_port *port_dev)
{
	char *envp[3] = { NULL, NULL, NULL };
	struct device *hub_dev;
	char *port_dev_path;

	sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count");

	hub_dev = port_dev->dev.parent;

	if (!hub_dev)
		return;

	port_dev_path = kobject_get_path(&port_dev->dev.kobj, GFP_KERNEL);
	if (!port_dev_path)
		return;

	envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path);
	if (!envp[0])
		goto exit;

	envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u",
			port_dev->over_current_count);
	if (!envp[1])
		goto exit;

	kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);

exit:
	kfree(envp[1]);
	kfree(envp[0]);
	kfree(port_dev_path);
}
首先,sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count")用于通知用户空间,USB端口的过流计数已经发生变化。
然后,获取USB端口所在的集线器设备。如果集线器设备不存在,则直接返回。
接着,获取USB端口设备在sysfs文件系统中的路径。如果获取失败,则直接返回。
然后,构造两个环境变量,一个是表示过流的USB端口的路径,一个是表示过流次数。这两个环境变量将会被发送到用户空间。
kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp)用于发送一个kobject事件,通知用户空间USB端口过流。
最后,释放之前分配的内存。
总结起来,这段代码的主要作用就是当USB端口过流时,通知用户空间进行相应的处理。

(4) 接着,打印一条调试信息,显示当前过流变化的次数。
(5) usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_OVER_CURRENT)函数用来清除端口的过流状态变化特性。

(6) msleep(100)函数让当前线程暂停100毫秒,这是为了让USB端口"冷却"下来。

(7) hub_power_on(hub, true)函数用来打开集线器的电源。

2.6.2 hub_power_on(hub, true)函数

static void hub_power_on(struct usb_hub *hub, bool do_delay)
{
	int port1;

	/*启用每个端口的电源。一些集线器的描述符中保留了LPSM(>2)值,
	即使它们是USB 2.0集线器。有些集线器不实现端口电源切换,
	而只是模拟它。在所有情况下,除非我们将这些消息发送到集线器,
	否则端口都不会工作*/
	if (hub_is_port_power_switchable(hub))
		dev_dbg(hub->intfdev, "enabling power on all ports\n");
	else
		dev_dbg(hub->intfdev, "trying to enable port power on "
				"non-switchable hub\n");
	for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
		if (test_bit(port1, hub->power_bits))
			set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
		else
			usb_clear_port_feature(hub->hdev, port1,
						USB_PORT_FEAT_POWER);
	if (do_delay)
		msleep(hub_power_on_good_delay(hub));
}

首先,通过hub_is_port_power_switchable(hub)函数判断集线器的端口是否支持电源切换。如果支持,就打印一条调试信息;如果不支持,也打印一条调试信息,但是信息内容不同。
然后,遍历集线器的所有端口。对于每个端口,如果power_bits中对应的位被设置,那么就调用set_port_feature函数打开端口的电源;否则,调用usb_clear_port_feature函数关闭端口的电源。
最后,如果do_delay参数为真,那么就调用msleep函数暂停一段时间。暂停的时间由hub_power_on_good_delay(hub)函数计算得出。
总的来说,这段代码的作用就是打开USB集线器的所有端口的电源。如果集线器的端口不支持电源切换,那么尝试打开电源可能会失败,但是这段代码仍然会尝试打开电源。

(8) usb_hub_port_status(hub, port1, &status, &unused)函数用来获取端口的状态。
最后,如果端口仍然处于过流状态,那么打印一条错误信息。整体就是判断端口过流,通知系统,并清理端口状态重启端口的供电,然后重新获取状态,仍然过流则输出错误log。

2.6.3 usb_hub_port_status(hub, port1, &status, &unused)函数

int usb_hub_port_status(struct usb_hub *hub, int port1,u16 *status, u16 *change)
-->>hub_ext_port_status(hub, port1, HUB_PORT_STATUS,status, change, NULL);
------->>get_port_status(hub->hdev, port1, &hub->status->port, type, len);

/*
*USB 2.0规范第11.24.2.7节
*USB 3.1考虑使用wValue和wLength字段,规范第10.16.2.6节
*/
static int get_port_status(struct usb_device *hdev, int port1,
			   void *data, u16 value, u16 length)
{
	int i, status = -ETIMEDOUT;

	for (i = 0; i < USB_STS_RETRIES &&
			(status == -ETIMEDOUT || status == -EPIPE); i++) {
		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
			port1, data, length, USB_STS_TIMEOUT);
	}
	return status;
}
//就是从设备中获取状态信息。

2.7 第58-61行:处理USB端口复位状态

第58-61行:作用是处理USB端口复位状态的变化。
首先,通过检查portchange & USB_PORT_STAT_C_RESET来判断USB端口的复位状态是否发生变化。如果变化了,那么进入该if语句块。
然后,打印一条调试信息,显示"reset change"。
最后,调用usb_clear_port_feature函数来清除端口的复位状态变化特性。
总的来说,这段代码的作用就是当USB端口的复位状态发生变化时,清除该状态变化,并打印一条调试信息。

2.8 第62-67行:处理超高速(superspeed)USB端口的热复位

第62-67行:主要作用是处理超高速(superspeed)USB端口的热复位(warm reset)状态变化。
(1) 首先,通过portchange & USB_PORT_STAT_C_BH_RESET判断USB端口的热复位状态是否发生变化,并通过hub_is_superspeed(hdev)判断该USB设备是否支持超高速模式。如果这两个条件都满足,那么进入if语句块。

static inline int hub_is_superspeed(struct usb_device *hdev)
{
	return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
}

(2) 然后,打印一条调试信息,显示"warm reset change"。
(3) 最后,调用usb_clear_port_feature函数来清除端口的热复位状态变化特性。
总的来说,这段代码的作用就是当支持超高速模式的USB端口的热复位状态发生变化时,清除该状态变化,并打印一条调试信息。。

2.9 第68-72行: 处理USB端口链路状态(Link State)的变化

第68-72行:主要作用是处理USB端口链路状态(Link State)的变化。
首先,通过portchange & USB_PORT_STAT_C_LINK_STATE判断USB端口的链路状态是否发生变化。如果发生变化,那么进入if语句块。
然后,打印一条调试信息,显示"link state change"。
最后,调用usb_clear_port_feature函数来清除端口的链路状态变化特性。
总的来说,这段代码的作用就是当USB端口的链路状态发生变化时,清除该状态变化,并打印一条调试信息。

2.10 第73-77行:处理USB端口配置错误状态的变化

第73-77行:主要作用是处理USB端口配置错误状态的变化。
首先,通过portchange & USB_PORT_STAT_C_CONFIG_ERROR判断USB端口的配置错误状态是否发生变化。如果发生变化,那么进入if语句块。
然后,打印一条警告信息,显示"config error"。
最后,调用usb_clear_port_feature函数来清除端口的配置错误状态变化特性。
总的来说,这段代码的作用就是当USB端口的配置错误状态发生变化时,清除该状态变化,并打印一条警告信息。

2.11 第80-81行:检查USB端口设备的运行时电源管理状态

第80-81行:作用是检查USB端口设备的运行时电源管理状态。
pm_runtime_active函数会返回设备的运行时电源管理(Runtime Power Management,简称RPM)是否处于活动状态。如果处于活动状态,函数会返回true;否则,返回false。
在这段代码中,如果USB端口设备的RPM不处于活动状态,那么函数将立即返回,不会执行后续的代码。这样可以避免在设备处于低功耗状态时执行可能会唤醒设备的操作,从而节省能源。

2.11.1 pm_runtime_active函数

路径:include\linux\pm_runtime.h
/**
 * pm_runtime_active - Check whether or not a device is runtime-active.
 * @dev: Target device.
 *
 * Return %true if runtime PM is disabled for @dev or its runtime PM status is
 * %RPM_ACTIVE, or %false otherwise.
 *
 * Note that the return value of this function can only be trusted if it is
 * called under the runtime PM lock of @dev or under conditions in which
 * runtime PM cannot be either disabled or enabled for @dev and its runtime PM
 * status cannot change.
 */
static inline bool pm_runtime_active(struct device *dev)
{
	return dev->power.runtime_status == RPM_ACTIVE
		|| dev->power.disable_depth;
}
static inline bool pm_runtime_active(struct device *dev) { return true; }

定义了一个名为pm_runtime_active的内联函数,用于检查一个设备是否处于运行时电源管理(Runtime Power Management,简称RPM)的活动状态。

dev:是一个指向设备结构体的指针,表示要检查的目标设备。

dev->power.runtime_status == RPM_ACTIVE:检查设备的运行时电源管理状态是否为RPM_ACTIVE,即设备是否处于活动状态。

dev->power.disable_depth:检查设备的运行时电源管理是否被禁用。如果disable_depth的值大于0,表示运行时电源管理被禁用。

如果设备的运行时电源管理状态为RPM_ACTIVE或者设备的运行时电源管理被禁用,那么函数返回true,表示设备处于活动状态。否则,返回false,表示设备不处于活动状态。

需要注意的是,这个函数的返回值只有在持有设备的运行时电源管理锁,或者在设备的运行时电源管理无法被禁用或启用,且其状态无法改变的情况下,才能被信任。

2.12 第84-85行

第84-85行:作用是判断是否应该忽略对USB端口事件的处理。
port_dev->ignore_event:如果这个值为真,表示应该忽略对USB端口事件的处理。
port_dev->early_stop:如果这个值为真,表示应该提前停止对USB端口事件的处理。
如果这两个条件都满足,那么函数立即返回,不会执行后续的代码。
总的来说,这段代码的作用就是提供一种机制,允许在满足某些条件时跳过对USB端口事件的处理。这可以用于优化性能,或者处理特殊情况。

2.13 第87-88行

第87-88行:主要作用是处理USB集线器的远程唤醒(Remote Wakeup)事件。
hub_handle_remote_wakeup函数会检查是否有远程唤醒事件发生。如果有,那么函数会处理这个事件,并返回1;否则,返回0。
如果hub_handle_remote_wakeup函数返回1,那么就将connect_change设置为1。这表示USB设备的连接状态发生了变化,需要重新枚举设备。
总的来说,这段代码的作用就是当USB设备通过远程唤醒事件改变了其连接状态时,设置connect_change为1,以便后续的代码可以处理这个变化。

2.13.1 hub_handle_remote_wakeup函数

/* Returns 1 if there was a remote wakeup and a connect status change. */
static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
		u16 portstatus, u16 portchange)
		__must_hold(&port_dev->status_lock)
{
	struct usb_port *port_dev = hub->ports[port - 1];
	struct usb_device *hdev;
	struct usb_device *udev;
	int connect_change = 0;
	u16 link_state;
	int ret;

	hdev = hub->hdev;
	udev = port_dev->child;
	if (!hub_is_superspeed(hdev)) {
		if (!(portchange & USB_PORT_STAT_C_SUSPEND))
			return 0;
		usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
	} else {
		link_state = portstatus & USB_PORT_STAT_LINK_STATE;
		if (!udev || udev->state != USB_STATE_SUSPENDED ||
				(link_state != USB_SS_PORT_LS_U0 &&
				 link_state != USB_SS_PORT_LS_U1 &&
				 link_state != USB_SS_PORT_LS_U2))
			return 0;
	}

	if (udev) {
		/* TRSMRCY = 10 msec */
		msleep(10);

		usb_unlock_port(port_dev);
		ret = usb_remote_wakeup(udev);
		usb_lock_port(port_dev);
		if (ret < 0)
			connect_change = 1;
	} else {
		ret = -ENODEV;
		hub_port_disable(hub, port, 1);
	}
	dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
	return connect_change;
}

主要作用是处理USB集线器的远程唤醒事件。

首先,判断USB设备是否支持超高速模式。如果不支持超高速模式,那么检查portchange & USB_PORT_STAT_C_SUSPEND,如果该值为0,表示没有远程唤醒事件,函数返回0。如果该值不为0,表示有远程唤醒事件,使用usb_clear_port_feature函数清除该状态。

如果USB设备支持超高速模式,那么检查USB设备的状态是否为挂起状态,并且链路状态是否为U0、U1或U2。如果不满足这些条件,表示没有远程唤醒事件,函数返回0。

然后,如果USB端口上有设备连接,那么先等待10毫秒,然后尝试唤醒设备。如果唤醒失败,那么将connect_change设置为1。

如果USB端口上没有设备连接,那么禁用该端口,并将返回值设置为-ENODEV。

最后,打印一条调试信息,并返回connect_change。

总的来说,这个函数的作用就是处理USB集线器的远程唤醒事件。如果有远程唤醒事件,并且唤醒设备失败,那么函数会返回1,表示设备的连接状态发生了变化。

2.14 第96-120行:处理USB3超速(Superspeed)端口热复位

第96-120行:主要作用是处理USB3超速(Superspeed)端口在SS.Inactive状态下需要进行热复位(warm reset)的情况。
(1) 首先,通过hub_port_warm_reset_required函数判断是否需要进行热复位。如果需要,就进入while循环。

(2) 在循环中,首先检查是否已经进行了多次重试(由DETECT_DISCONNECT_TRIES定义)。如果没有达到重试次数,并且udev(表示USB设备)不为空,那么就暂停20毫秒,然后重新获取端口状态,并打印一条调试信息,然后继续下一次循环。

(3) 如果达到了重试次数,或者udev为空,或者端口没有连接,或者设备处于未连接状态,那么就只对端口进行热复位,并打印一条调试信息。如果热复位失败,那么就禁用该端口。

(4) 如果以上条件都不满足,那么就对整个设备进行复位,并打印一条调试信息。复位设备需要先解锁端口,然后锁定设备,然后复位设备,然后解锁设备,最后再锁定端口。并且将connect_change设置为0,表示连接状态没有变化。

总的来说,这段代码的作用就是处理USB3超速端口在SS.Inactive状态下需要进行热复位的情况。如果设备仍然连接在端口上,那么会尝试复位整个设备;否则,只复位端口。

2.14.1 hub_port_warm_reset_required()

static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
		u16 portstatus)
{
	u16 link_state;

	if (!hub_is_superspeed(hub->hdev))
		return false;

	if (test_bit(port1, hub->warm_reset_bits))
		return true;

	link_state = portstatus & USB_PORT_STAT_LINK_STATE;
	return link_state == USB_SS_PORT_LS_SS_INACTIVE
		|| link_state == USB_SS_PORT_LS_COMP_MOD;
}

其主要作用是判断一个USB 3.0端口是否处于Inactive或Compliance Mode状态,如果是,那么需要进行热复位(warm reset)恢复。

首先,通过hub_is_superspeed(hub->hdev)判断集线器设备是否为超速(Superspeed)设备。如果不是,那么直接返回false,表示不需要热复位。

然后,通过test_bit(port1, hub->warm_reset_bits)判断指定端口是否已经标记为需要热复位。如果已经标记,那么返回true,表示需要热复位。

接着,通过link_state = portstatus & USB_PORT_STAT_LINK_STATE获取端口的链接状态。

最后,判断链接状态是否为USB_SS_PORT_LS_SS_INACTIVE或USB_SS_PORT_LS_COMP_MOD。如果是,那么返回true,表示需要热复位。

总的来说,这个函数的作用就是判断一个USB 3.0端口是否需要进行热复位。如果端口处于Inactive或Compliance Mode状态,或者已经被标记为需要热复位,那么就需要进行热复位。

2.14.2 hub_port_reset()

/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
static int hub_port_reset(struct usb_hub *hub, int port1,
			struct usb_device *udev, unsigned int delay, bool warm)
{
	int i, status;
	u16 portchange, portstatus;
	struct usb_port *port_dev = hub->ports[port1 - 1];
	int reset_recovery_time;

	if (!hub_is_superspeed(hub->hdev)) {
		if (warm) {
			dev_err(hub->intfdev, "only USB3 hub support "
						"warm reset\n");
			return -EINVAL;
		}
		/* Block EHCI CF initialization during the port reset.
		 * Some companion controllers don't like it when they mix.
		 */
		down_read(&ehci_cf_port_reset_rwsem);
	} else if (!warm) {
		/*
		 * If the caller hasn't explicitly requested a warm reset,
		 * double check and see if one is needed.
		 */
		if (usb_hub_port_status(hub, port1, &portstatus,
					&portchange) == 0)
			if (hub_port_warm_reset_required(hub, port1,
							portstatus))
				warm = true;
	}
	clear_bit(port1, hub->warm_reset_bits);

	/* Reset the port */
	for (i = 0; i < PORT_RESET_TRIES; i++) {
		status = set_port_feature(hub->hdev, port1, (warm ?
					USB_PORT_FEAT_BH_PORT_RESET :
					USB_PORT_FEAT_RESET));
		if (status == -ENODEV) {
			;	/* The hub is gone */
		} else if (status) {
			dev_err(&port_dev->dev,
					"cannot %sreset (err = %d)\n",
					warm ? "warm " : "", status);
		} else {
			status = hub_port_wait_reset(hub, port1, udev, delay,
								warm);
			if (status && status != -ENOTCONN && status != -ENODEV)
				dev_dbg(hub->intfdev,
						"port_wait_reset: err = %d\n",
						status);
		}

		/*
		 * Check for disconnect or reset, and bail out after several
		 * reset attempts to avoid warm reset loop.
		 */
		if (status == 0 || status == -ENOTCONN || status == -ENODEV ||
		    (status == -EBUSY && i == PORT_RESET_TRIES - 1)) {
			usb_clear_port_feature(hub->hdev, port1,
					USB_PORT_FEAT_C_RESET);

			if (!hub_is_superspeed(hub->hdev))
				goto done;

			usb_clear_port_feature(hub->hdev, port1,
					USB_PORT_FEAT_C_BH_PORT_RESET);
			usb_clear_port_feature(hub->hdev, port1,
					USB_PORT_FEAT_C_PORT_LINK_STATE);

			if (udev)
				usb_clear_port_feature(hub->hdev, port1,
					USB_PORT_FEAT_C_CONNECTION);

			/*
			 * If a USB 3.0 device migrates from reset to an error
			 * state, re-issue the warm reset.
			 */
			if (usb_hub_port_status(hub, port1,
					&portstatus, &portchange) < 0)
				goto done;

			if (!hub_port_warm_reset_required(hub, port1,
					portstatus))
				goto done;

			/*
			 * If the port is in SS.Inactive or Compliance Mode, the
			 * hot or warm reset failed.  Try another warm reset.
			 */
			if (!warm) {
				dev_dbg(&port_dev->dev,
						"hot reset failed, warm reset\n");
				warm = true;
			}
		}

		dev_dbg(&port_dev->dev,
				"not enabled, trying %sreset again...\n",
				warm ? "warm " : "");
		delay = HUB_LONG_RESET_TIME;
	}

	dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");

done:
	if (status == 0) {
		if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM)
			usleep_range(10000, 12000);
		else {
			/* TRSTRCY = 10 ms; plus some extra */
			reset_recovery_time = 10 + 40;

			/* Hub needs extra delay after resetting its port. */
			if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET)
				reset_recovery_time += 100;

			msleep(reset_recovery_time);
		}

		if (udev) {
			struct usb_hcd *hcd = bus_to_hcd(udev->bus);

			update_devnum(udev, 0);
			/* The xHC may think the device is already reset,
			 * so ignore the status.
			 */
			if (hcd->driver->reset_device)
				hcd->driver->reset_device(hcd, udev);

			usb_set_device_state(udev, USB_STATE_DEFAULT);
		}
	} else {
		if (udev)
			usb_set_device_state(udev, USB_STATE_NOTATTACHED);
	}

	if (!hub_is_superspeed(hub->hdev))
		up_read(&ehci_cf_port_reset_rwsem);

	return status;
}

该函数用于对USB集线器的端口进行复位操作。复位操作包括常规复位和热复位两种,热复位主要针对USB3.0协议的端口。复位操作主要用于设备枚举和错误恢复。

以下是这段代码的详细过程和作用:

首先,判断是否为超速设备。如果不是,并且进行的是热复位,打印错误信息并返回。如果是超速设备,但没有明确要求进行热复位,那么检查端口状态,如果需要,设定进行热复位。

清除端口的热复位标记。

进行端口复位操作,复位操作可能需要多次尝试。在每次尝试中,首先设置端口复位特性,然后等待端口复位完成。

检查复位操作的结果。如果复位成功,或者端口已经断开连接,或者端口已经禁用,或者复位操作繁忙并且已经达到最大尝试次数,那么清除端口的复位状态变化特性,并且如果是超速设备,还需要清除端口的热复位状态变化特性和链路状态变化特性。如果端口在复位后进入了错误状态,那么需要再次进行热复位。

如果复位操作失败,那么打印错误信息并返回。

如果复位操作成功,那么等待一段时间以便设备恢复,然后更新设备的设备号,并将设备状态设置为默认状态。

如果复位操作失败,那么将设备状态设置为未连接状态。

总的来说,这段代码的作用就是对USB集线器的端口进行复位操作,以便设备枚举和错误恢复。

2.14.2.1 update_devnum()

static void update_devnum(struct usb_device *udev, int devnum)
{
	/* The address for a WUSB device is managed by wusbcore. */
	if (!udev->wusb)
		udev->devnum = devnum;
	if (!udev->devaddr)
		udev->devaddr = (u8)devnum;
}

主要作用是更新USB设备的设备编号(device number)。

以下是这段代码的详细过程和作用:

首先检查udev->wusb的值。如果udev->wusb为0,表示这不是一个无线USB(WUSB)设备,那么就将输入参数devnum赋值给udev->devnum,作为设备的新编号。

然后检查udev->devaddr的值。如果udev->devaddr为0,表示设备的地址还没有被设置,那么就将输入参数devnum的值(转换为u8类型)赋值给udev->devaddr,作为设备的新地址。

总的来说,这个函数的作用就是更新USB设备的设备编号和设备地址。对于无线USB设备,其设备编号是由无线USB核心管理的,所以这个函数不会更新无线USB设备的设备编号。

2.14.2.2 usb_set_device_state()

void usb_set_device_state(struct usb_device *udev,
		enum usb_device_state new_state)
{
	unsigned long flags;
	int wakeup = -1;

	spin_lock_irqsave(&device_state_lock, flags);
	if (udev->state == USB_STATE_NOTATTACHED)
		;	/* do nothing */
	else if (new_state != USB_STATE_NOTATTACHED) {

		/* root hub wakeup capabilities are managed out-of-band
		 * and may involve silicon errata ... ignore them here.
		 */
		if (udev->parent) {
			if (udev->state == USB_STATE_SUSPENDED
					|| new_state == USB_STATE_SUSPENDED)
				;	/* No change to wakeup settings */
			else if (new_state == USB_STATE_CONFIGURED)
				wakeup = (udev->quirks &
					USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 :
					udev->actconfig->desc.bmAttributes &
					USB_CONFIG_ATT_WAKEUP;
			else
				wakeup = 0;
		}
		if (udev->state == USB_STATE_SUSPENDED &&
			new_state != USB_STATE_SUSPENDED)
			udev->active_duration -= jiffies;
		else if (new_state == USB_STATE_SUSPENDED &&
				udev->state != USB_STATE_SUSPENDED)
			udev->active_duration += jiffies;
		udev->state = new_state;
	} else
		recursively_mark_NOTATTACHED(udev);
	spin_unlock_irqrestore(&device_state_lock, flags);
	if (wakeup >= 0)
		device_set_wakeup_capable(&udev->dev, wakeup);
}
EXPORT_SYMBOL_GPL(usb_set_device_state);

主要用于改变USB设备的状态。

以下是这段代码的详细过程和作用:

函数首先获取device_state_lock自旋锁,以保护设备状态的修改操作。

然后检查设备当前的状态是否为USB_STATE_NOTATTACHED。如果是,函数不做任何操作。

如果新的状态new_state不为USB_STATE_NOTATTACHED,则进一步检查设备的状态。如果设备不是根集线器,并且设备当前状态或新的状态为USB_STATE_SUSPENDED,则不改变设备的唤醒设置。如果新的状态为USB_STATE_CONFIGURED,则根据设备是否支持远程唤醒(USB_QUIRK_IGNORE_REMOTE_WAKEUP)和配置描述符的唤醒属性(USB_CONFIG_ATT_WAKEUP)来设置唤醒标志wakeup。否则,将唤醒标志wakeup设置为0。

接着,如果设备当前状态为USB_STATE_SUSPENDED并且新的状态不为USB_STATE_SUSPENDED,则从设备的活动持续时间中减去当前时间。如果新的状态为USB_STATE_SUSPENDED并且设备当前状态不为USB_STATE_SUSPENDED,则向设备的活动持续时间中添加当前时间。

最后,将设备的状态设置为新的状态。

如果新的状态为USB_STATE_NOTATTACHED,则调用recursively_mark_NOTATTACHED函数将设备及其所有子设备的状态都设置为USB_STATE_NOTATTACHED。

释放device_state_lock自旋锁。

如果唤醒标志wakeup大于等于0,调用device_set_wakeup_capable函数设置设备的唤醒能力。

总的来说,这个函数的作用就是改变USB设备的状态,并在需要时更新设备的唤醒设置和活动持续时间。

2.14.3 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;
}

主要用于关闭指定的USB端口。以下是这段代码的详细过程和作用:
函数首先获取指定端口的设备和集线器对象。

然后,检查集线器是否有错误。如果没有错误,那么就关闭端口。关闭端口的方式取决于集线器是否支持超速(Superspeed)模式。如果支持超速模式,那么就调用hub_usb3_port_prepare_disable函数和hub_set_port_link_state函数将端口的链接状态设置为U3(U3是USB3.0协议中的一种电源管理状态,表示端口被关闭)。如果不支持超速模式,那么就调用usb_clear_port_feature函数清除端口的使能特性,以关闭端口。

接着,如果端口上有设备连接,并且set_state参数为真,那么就将设备的状态设置为USB_STATE_NOTATTACHED,表示设备已经从端口上断开。

最后,如果关闭端口失败,并且错误码不是-ENODEV(表示设备不存在),那么就打印一条错误信息。

总的来说,这个函数的作用就是关闭指定的USB端口。如果端口上有设备连接,那么还会将设备的状态设置为已断开。

2.14.3.1 hub_usb3_port_prepare_disable()
static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
					  struct usb_port *port_dev)
{
	struct usb_device *udev = port_dev->child;
	int ret;

	if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
		ret = hub_set_port_link_state(hub, port_dev->portnum,
					      USB_SS_PORT_LS_U0);
		if (!ret) {
			msleep(USB_RESUME_TIMEOUT);
			ret = usb_disable_remote_wakeup(udev);
		}
		if (ret)
			dev_warn(&udev->dev,
				 "Port disable: can't disable remote wake\n");
		udev->do_remote_wakeup = 0;
	}
}

主要用于为关闭USB3.0端口做准备工作,特别是确保关闭端口的远程唤醒功能。

以下是这段代码的详细过程和作用:

函数首先检查端口上是否有设备连接,并且设备是否处于挂起状态,以及设备的远程唤醒功能是否已经打开。如果这三个条件都满足,那么就进入if语句块。

在if语句块中,首先调用hub_set_port_link_state函数将端口的链接状态设置为U0(U0是USB3.0协议中的一种链接状态,表示端口处于活动状态)。然后等待一段时间(由USB_RESUME_TIMEOUT定义),以便设备从挂起状态恢复。

接着,调用usb_disable_remote_wakeup函数关闭设备的远程唤醒功能。如果关闭远程唤醒功能失败,那么就打印一条警告信息。

最后,无论关闭远程唤醒功能是否成功,都将设备的do_remote_wakeup标志设置为0,表示设备的远程唤醒功能已经关闭。

总的来说,这个函数的作用就是为关闭USB3.0端口做准备工作,特别是确保关闭端口的远程唤醒功能。这是因为在USB3.0协议中,端口的远程唤醒功能需要在端口被关闭之前关闭。

2.14.3.2 hub_set_port_link_state()
static int hub_set_port_link_state(struct usb_hub *hub, int port1,
			unsigned int link_status)
{
	return set_port_feature(hub->hdev,
			port1 | (link_status << 3),
			USB_PORT_FEAT_LINK_STATE);
}

主要用于设置USB集线器端口的链路状态。

以下是这段代码的详细过程和作用:

函数接收三个参数:一个指向usb_hub结构体的指针hub,表示要操作的USB集线器;一个整数port1,表示要操作的端口号;一个无符号整数link_status,表示要设置的链路状态。

在函数中,首先通过移位操作(link_status << 3)将link_status的值左移3位。然后,将移位后的link_status的值和port1的值进行按位或操作。这样可以将port1的值和link_status的值合并到一个16位的整数中。

最后,调用set_port_feature函数,将合并后的值和USB_PORT_FEAT_LINK_STATE作为参数传递给set_port_feature函数。set_port_feature函数会将USB_PORT_FEAT_LINK_STATE特性设置到指定的端口上,从而改变端口的链路状态。

总的来说,这个函数的作用就是设置USB集线器端口的链路状态。这是通过调用set_port_feature函数实现的,该函数会将指定的特性设置到端口上。

2.14.4 usb_reset_device()

int usb_reset_device(struct usb_device *udev)
{
	int ret;
	int i;
	unsigned int noio_flag;
	struct usb_port *port_dev;
	struct usb_host_config *config = udev->actconfig;
	struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);

	if (udev->state == USB_STATE_NOTATTACHED) {
		dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
				udev->state);
		return -EINVAL;
	}

	if (!udev->parent) {
		/* this requires hcd-specific logic; see ohci_restart() */
		dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
		return -EISDIR;
	}

	if (udev->reset_in_progress)
		return -EINPROGRESS;
	udev->reset_in_progress = 1;

	port_dev = hub->ports[udev->portnum - 1];

	/*
	 * Don't allocate memory with GFP_KERNEL in current
	 * context to avoid possible deadlock if usb mass
	 * storage interface or usbnet interface(iSCSI case)
	 * is included in current configuration. The easist
	 * approach is to do it for every device reset,
	 * because the device 'memalloc_noio' flag may have
	 * not been set before reseting the usb device.
	 */
	noio_flag = memalloc_noio_save();

	/* Prevent autosuspend during the reset */
	usb_autoresume_device(udev);

	if (config) {
		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
			struct usb_interface *cintf = config->interface[i];
			struct usb_driver *drv;
			int unbind = 0;

			if (cintf->dev.driver) {
				drv = to_usb_driver(cintf->dev.driver);
				if (drv->pre_reset && drv->post_reset)
					unbind = (drv->pre_reset)(cintf);
				else if (cintf->condition ==
						USB_INTERFACE_BOUND)
					unbind = 1;
				if (unbind)
					usb_forced_unbind_intf(cintf);
			}
		}
	}

	usb_lock_port(port_dev);
	ret = usb_reset_and_verify_device(udev);
	usb_unlock_port(port_dev);

	if (config) {
		for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
			struct usb_interface *cintf = config->interface[i];
			struct usb_driver *drv;
			int rebind = cintf->needs_binding;

			if (!rebind && cintf->dev.driver) {
				drv = to_usb_driver(cintf->dev.driver);
				if (drv->post_reset)
					rebind = (drv->post_reset)(cintf);
				else if (cintf->condition ==
						USB_INTERFACE_BOUND)
					rebind = 1;
				if (rebind)
					cintf->needs_binding = 1;
			}
		}

		/* If the reset failed, hub_wq will unbind drivers later */
		if (ret == 0)
			usb_unbind_and_rebind_marked_interfaces(udev);
	}

	usb_autosuspend_device(udev);
	memalloc_noio_restore(noio_flag);
	udev->reset_in_progress = 0;
	return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_device);

主要用于对USB设备进行复位操作。

以下是这段代码的详细过程和作用:

函数首先检查设备是否处于USB_STATE_NOTATTACHED状态。如果是,那么打印一条调试信息并返回错误。

接着,如果设备是根集线器,那么打印一条调试信息并返回错误。

如果设备已经在进行复位操作,那么返回-EINPROGRESS,表示复位操作正在进行中。

然后,获取设备所连接的端口和集线器。

为了避免在当前上下文中分配内存可能导致的死锁,保存并改变内存分配的标志。

调用usb_autoresume_device函数,防止设备在复位操作期间自动挂起。

如果设备有活动的配置,那么遍历配置中的所有接口,对每个接口进行以下操作:

如果接口已经绑定到驱动,并且驱动提供了pre_reset和post_reset回调,那么调用pre_reset回调,并根据回调的返回值决定是否需要解绑接口;
如果接口已经绑定到驱动,但驱动没有提供pre_reset和post_reset回调,那么解绑接口;
如果接口没有绑定到驱动,那么不做任何操作。
对设备进行复位和验证操作。

如果设备有活动的配置,那么再次遍历配置中的所有接口,对每个接口进行以下操作:

如果接口需要绑定,并且接口已经绑定到驱动,并且驱动提供了post_reset回调,那么调用post_reset回调,并根据回调的返回值决定是否需要重新绑定接口;
如果接口需要绑定,并且接口已经绑定到驱动,但驱动没有提供post_reset回调,那么重新绑定接口;
如果接口不需要绑定,那么不做任何操作。
如果复位操作成功,那么对所有需要重新绑定的接口进行解绑和绑定操作。

调用usb_autosuspend_device函数,允许设备在空闲时自动挂起。

恢复内存分配的标志。

清除设备的复位标志。

总的来说,这个函数的作用就是对USB设备进行复位操作。在复位操作开始和结束时,会通知所有绑定到设备的驱动。如果驱动没有提供相应的回调,那么会解绑驱动并在复位操作结束后重新绑定驱动。

2.15 第122-123行:处理USB集线器端口的连接状态变化

第122-123行:主要作用是处理USB集线器端口的连接状态变化。

connect_change:这是一个布尔变量,如果其值为真,表示USB设备的连接状态发生了变化。

hub_port_connect_change:这是一个函数,用于处理USB设备的连接状态变化。它接收四个参数:一个指向usb_hub结构体的指针hub,表示要操作的USB集线器;一个整数port1,表示要操作的端口号;一个整数portstatus,表示端口的当前状态;一个整数portchange,表示端口状态的变化。

如果connect_change的值为真,那么就调用hub_port_connect_change函数,处理USB设备的连接状态变化。这可能包括设备的连接、断开连接、重新枚举等操作。

三、总结

以上是对USB port event的处理过程。后面将继续分析,如果USB device connect 发生变化的处理过程,例如USB设备插入、USB设备拔出等链接发生变化情况的处理。 由hub_port_connect_change()函数处理。请看篇分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

waterAdmin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值