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

news2024/11/15 5:09:15

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全球机器传感器市场价值约为142亿美元,预计将以超过7.5%的增长率增长

机器传感器是一类用于检测、测量和感知机器环境中物理量、化学量或其他特定参数的设备。这些传感器将实际的物理现象转化为电信号或数字信号&#xff0c;以便计算机或控制系统进行处理、分析和控制。机器传感器在工业、自动化、物联网、机器人、汽车等领域有广泛应用。 根据阿…

部门来了个新同事,听说是00后,上来一顿操作给我看呆了...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&#x…

OD动态调试exe

之前一直卡&#xff0c;好不容易搞懂一点&#xff0c;记下来记下来 分析exe文件&#xff0c;用ida打开&#xff0c;找到主函数main&#xff0c;分析主函数可以发现&#xff0c;main在判断之后调用了l02等函数 因为判断部分的逻辑还是有点复杂&#xff0c;因此想让他直接打印函…

基于PaddleOCR2.7.0发布WebRest服务测试案例

基于PaddleOCR2.7.0发布WebRest服务测试案例 #WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. #警告&#xff1a;这是一个开发服务器。不要在生产部署中使用它。请改用生产WSGI服务器。 输出结果…

smartsofthelp 5.0 最专业的数据库优化工具,数据库配置优化,数据库高并发优化,SQL 语句优化...

下载地址:百度网盘 请输入提取码 SQL操作返回历史记录&#xff1a; 2023-08-21 20:42:08:220 输入&#xff1a;select version as 版本号 2023-08-21 20:42:08:223 输出&#xff1a;当前数据库实例版本号&#xff1a;Microsoft SQL Server 2012 - 11.0.2100.60 (X64) …

司徒理财:黄金美盘继续看跌,黄金原油最新走势分析及操作建议

黄金走势分析及策略      黄金上周继续回落收跌&#xff0c;周线连续第四周走低&#xff0c;而且还破了周的低点1892&#xff0c;空头力量仍占据优势&#xff0c;暗示后市仍有继续走低的动力和空间&#xff0c;日线方面&#xff0c;黄金一直是承压5日均线下行&#xff0c;是…

@Slf4j报错:Not generating field log: A field with same name already exists

错误出处&#xff1a; 错误原因&#xff1a; 同时使用了Slf4j注解以及LittlecLogger private static final LittlecLogger log LittlecLoggerFactory.getLogger(TimeTrackController.class); 修复方法&#xff1a; 将log改为LOG&#xff0c;便于区分&#xff0c;代码即用到了…

服务运营|斯坦福大学商学院Yue Hu :基于排队论的医院急诊部人员配置策略

作者&#xff1a;李舒湉&#xff0c; 陈盈鑫&#xff0c; XinW 编者按 本次解读的文章是“Prediction-Driven Surge Planning with Application in the Emergency Department”。论文第一作者是斯坦福大学商学院&#xff08;Stanford Graduate School of Business) 的Yue Hu&a…

浅谈煤矿井下电力监控系统的应用研究

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;主要介绍了应用煤矿井下电力监控系统的重要意义&#xff0c;并对煤矿井下电力监控系统的主要原理和基本构成做了简要介绍&#xff0c;并从隔爆监控单元设计、通信协议设计和组态监控系统设计等方面提出了电力监…

小白到运维工程师自学之路 第七十八集 (安装Jenkins)

一、环境概述 随着软件开发需求及复杂度的不断提高&#xff0c;团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins自动化部署可以解决集成、测试、部署等重复性的工作&#xff0c;工具集成的效率明显高于人工操作&#xf…

【考研数学】解决求解微分方程时,常数的结合问题以及是否能直接去掉绝对值的讨论

文章目录 引言例子 1例子 2总结 引言 在微分方程&#xff0c;尤其是一阶线性微分方程的求解过程中&#xff0c;经常出现求 ∫ 1 x d x \int\frac{1}{x}dx ∫x1​dx 等原函数需要加绝对值的不定积分。如果放在不定积分里&#xff0c;肯定毫不犹豫会加上绝对值&#xff0c;也就…

OpenCV 中的色彩空间 (C++ / Python)

在本教程中,我们将了解计算机视觉中使用的流行色彩空间,并将其用于基于颜色的分割。我们还将分享 C++ 和 Python 的演示代码。

在Flutter应用内部实现分屏功能

前言 这一次被要求实现屏幕上同时展示两个页面&#xff0c;并且两个页面的逻辑&#xff0c;功能互不影响&#xff0c;通俗一点讲就是在Flutter内部实现一个类似于分屏的功能&#xff0c;这可难不倒我。 方法 要在 Flutter 中实现一个屏幕的上半部分和下半部分展示不同的页面…

【C++】C 语言 和 C++ 语言中 const 关键字分析 ② ( const 常量分配内存时机 | const 常量在编译阶段分配内存 )

文章目录 一、const 常量内存分配时机二、使用如下代码验证 const 常量内存分配时机三、分析验证结果 - const 常量在编译阶段分配内存 一、const 常量内存分配时机 在上一篇博客中 , 讲到了获取 const 常量的地址 , 代码如下 : // 定义常量// 该常量定义在了 符号表 中// 符号…

通过cpolar在外远程查看家里内网监控

通过cpolar在外远程查看家里内网监控 文章目录 通过cpolar在外远程查看家里内网监控前言1. 在cpolar官网预留一个空白隧道2. 完成空白数据隧道&#xff0c;生成地址3. 设置空白隧道的出口4. 空白数据隧道的出口设置5. 获取公网地址6. 打开本地电脑“远程桌面”7. 打开Windows自…

赶紧看看!这才是对制造业最大的优化

​随着全球商业环境的不断变化&#xff0c;资产管理系统在帮助企业实现精细化管理、提高效率和降低风险方面发挥着关键作用。 在这个数字化时代&#xff0c;资产管理系统不仅是一种管理工具&#xff0c;更是推动企业创新和增长的关键因素之一。通过充分利用这些系统&#xff0c…

【前端vue升级】vue2+js+elementUI升级为vue3+ts+elementUI plus

一、工具的选择 近期想将vuejselementUI的项目升级为vue3tselementUI plus&#xff0c;以获得更好的开发体验&#xff0c;并且vue3也显著提高了性能&#xff0c;所以在此记录一下升级的过程对于一个正在使用的项目手工替换肯定不是个可实现的解决方案&#xff0c;更优方案是基于…

提升研发效能的开发工具

一、前言 随着企业对创新和效率的追求不断升级&#xff0c;研发效能成为了炙手可热的概念。各大公司纷纷成立专门的团队&#xff0c;以提升研发效能为重要目标。本文将从研发人员的视角出发&#xff0c;结合自身的深度思考&#xff0c;探讨研发效能的相关概念&#xff0c;共同寻…

【MySQL系列】MySQL内置函数的学习

「前言」文章内容大致是对MySQL内置函数的学习。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、MySQL的日期函数二、MySQL的字符串函数三、MySQL的数学函数四、 其它函数 一、MySQL的日期函数 常见的日期函数如下&#xff1a; 函数名称描述current…

Netty为什么高效,为什么这么受欢迎?

文章目录 前言Netty 解决的问题简化网络编程粘包和拆包 高性能的设计多线程调度零拷贝 总结 前言 上篇文章通过 Java NIO 的处理流程与 Netty 的总体流程比较&#xff0c;并结合 Netty 的源码&#xff0c;可以更加清晰地理解Netty。本文将结合源码详细解析Netty的高效和强大功…