<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;
	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设备 并添加到设备管理中。后面文章继续分析。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode.141,142——环形链表,环形链表Ⅱ

LeetCode.141——环形链表&#xff1a; 题目如下&#xff1a; 通过题目中对于环形链表的大体描述&#xff0c;可以知道&#xff0c;环形链表最后一个结点保存了一个地址&#xff0c;用于返回链表中某个结点。并且。这个返回的结点并不是返回图中保存数据的结点。而是返回链表…

TCP特点UDP编程

目录 1、tcp协议和udp协议 2、多线程并发和多进程并发&#xff1a; &#xff08;1&#xff09;多进程并发服务端 &#xff08;2&#xff09;多进程并发客户端&#xff1a; 3、tcp: 4、粘包 5、UDP协议编程流程 (1)服务器端&#xff1a; (2)客户端&#xff1a; 6、tcp状…

成集云 | 乐享问题邀请同步企微提醒 | 解决方案

源系统成集云目标系统 方案介绍 腾讯乐享是腾讯公司开发的一款企业社区化知识管理平台&#xff0c;它提供了包括知识库、问答、课堂、考试、活动、投票和论坛等核心应用。这个平台凝聚了腾讯10年的管理经验&#xff0c;可以满足政府、企业和学校在知识管理、学习培训、文化建…

Java实现钉钉企业内部应用机器和自定义机器人发送消息

前言 公司让写一个服务监控的功能,当监测到服务停止时,向钉钉群里推送报警信息。之前大概看到钉钉的开放平台的API文档,好像能群发消息的只有机器人。 钉钉开放平台目前提供三种机器人: 企业内部应用机器人 群模板机器人 自定义机器人 本来向用自己比较熟悉的自定义机器人…

8年经验之谈 —— 基于jmeter的性能全流程测试

01、做性能测试的步骤 1、服务器性能监控 首先要在对应服务器上面安装性能监控工具&#xff0c;比如linux系统下的服务器&#xff0c;可以选择nmon或者其他的监控工具&#xff0c;然后在jmeter模拟场景跑脚本的时候&#xff0c;同时启动监控工具&#xff0c;这样就可以获得jm…

Android Studio run app 设置 release 模式

背景 为验证我们的 SDK 集成在客户应用上的质量&#xff0c;需要我们的测试环境尽量的与客户应用保持一致。客户普遍都会打 release 包并混淆&#xff0c;然后进行上线应用&#xff0c;因此我们在测试过程中也需要使用 release 包进行验证。对于 Android Studio 运行项目&…

Jmeter数据驱动 —— csv高效用例

目录 1、设置测试用例&#xff0c;创建用例数据文件&#xff1a;testcase.csv 2、新建一个线程组&#xff0c;命名为&#xff1a;数据驱动&#xff0c;添加配置元件-HTTP请求默认值&#xff0c;配置好IP地址和端口号 3、添加逻辑控制器-循环控制器。循环控制器的作用可以控制…

【C++】运算符重载 | 赋值运算符重载

Ⅰ. 运算符重载 引入 ❓什么叫运算符重载&#xff1f; 就是&#xff1a;运用函数&#xff0c;将现有的运算符重新定义&#xff0c;使其能满足各种自定义类型的运算。 回想一下&#xff0c;我们以前运算的对象是不是都是int、char这种内置类型&#xff1f; 那我们自定义的“…

CST HFSS MATLAB参数方程定义曲面绘制

CST HFSS 函数定义曲面绘制 简介环境HFSSCSTMATLAB 简介 若在柱坐标系中半径r随z和phi都会变&#xff0c;无法使用一般的方法绘制&#xff0c;这时可以使用参数方程定义的曲面来绘制。举一个例子如下&#xff0c; r 100 0.5 ( c o s ( 0.2 ∗ p i ∗ z ) − 1 ) c o s ( φ …

Ganache 本地测试网远程连接

文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 前言 Ganache 是DApp的测试网络&#xff0c;提供图形化界面&#xff0c;log日志等&#xff1b;智能合约部署时需要连接测试网络。 Ganache 是一个运行在本地测试的网络,通过结合cpol…

哪些人适合参加大数据培训班?

互联网加速职场变革&#xff0c;大数据浪潮席卷全球。日前&#xff0c;Python、大数据、人工智能是当今最热门的话题。大数据存储、大数据分析、 人工智能等开发人才需求旺盛。 大数据培训班有大数据分析培训班、大数据开发培训班&#xff0c;JAVA培训班 大数据班适学人群…

【RabbitMQ】RabbitMQ整合SpringBoot案例

文章目录 1、前情提要【RabbitMQ】2、RabbitMQ-SpringBoot案例 -fanout模式2.1 实现架构总览2.2 具体实现2.2.1生产者2.2.1消费者 1、前情提要【RabbitMQ】 【RabbitMQ】消息队列-RabbitMQ篇章 RabbitMQ实现流程 2、RabbitMQ-SpringBoot案例 -fanout模式 2.1 实现架构总览…

快速学习GO语言总结

备注&#xff1a;本博客将自己初步学习GO的总结进行分享&#xff0c;希望大家通过本博客可以在短时间内快速掌握GO的基本程序编码能力&#xff0c;如有错误请留言指正&#xff0c;谢谢&#xff01; 一、初步了解Go语言 &#xff08;一&#xff09;Go语言诞生的主要问题和目标…

Elasticsearch:语义搜索 - Semantic Search in python

当 OpenAI 于 2022 年 11 月发布 ChatGPT 时&#xff0c;引发了人们对人工智能和机器学习的新一波兴趣。 尽管必要的技术创新已经出现了近十年&#xff0c;而且基本原理的历史甚至更早&#xff0c;但这种巨大的转变引发了各种发展的“寒武纪大爆炸”&#xff0c;特别是在大型语…

【数据结构】_7.二叉树

目录 1.树形结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的应用—表示文件系统的目录树结构 ​编辑​2.二叉树 2.1 概念 2.2 特殊二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 2.4.1 顺序存储结构&#xff08;数组存储结构&#xff09; 2.4.2…

7个改变玩法规则的ChatGPT应用场景

ChatGPT因各种原因受到了广泛关注&#xff1a;ChatGPT可以充当各种改善生活改进工作的小助手&#xff0c;如内容写手、客户支持、语言翻译、编码专家等等。只需在你的聊天内容中添加适当的提示&#xff0c;人工智能将为你提供各项支持。[1] 1.ChatGPT作为内容写手 通过AI的帮助…

flink jira 提交开源bug

注册apache issue账号,并申请flink空间的权限后. 提问题/bug 查看已经提交的问题:

PHP“牵手”淘宝商品评论数据采集方法,淘宝API接口申请指南

淘宝天猫商品评论数据接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取商品的详细信息&#xff0c;包括商品的标题、描述、图片等信息。在电商平台的开发中&#xff0c;详情接口API是非常常用的 API&#xff0c;因此本文将详细介绍详情接口 API 的使用…

SRM系统询价竞价管理:优化采购流程的全面解析

SRM系统的询价竞价管理模块是现代企业采购管理中的重要工具。通过该模块&#xff0c;企业可以实现供应商的询价、竞价和合同管理等关键环节的自动化和优化。 一、概述 SRM系统是一种用于管理和优化供应商关系的软件系统。它通过集成各个环节&#xff0c;包括供应商信息管理、询…

龙讯旷腾PWmat已部署至曙光智算平台

编者荐语&#xff1a; 近期&#xff0c;龙讯旷腾核心产品PWmat已成功部署至曙光智算AC.sugon.com平台&#xff0c;可为用户提供包括分子建模、第一性原理计算、数据可视化等在内的完备的超级计算云服务&#xff0c;让大家能够轻松上手具有完全自主知识产权的大尺度高性能材料计…