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

news2025/1/14 18:15:03

<kernel>kernel 6.4 USB-之-hub_event()分析
本文是基于linux kernel 6.4版本内核分析;源码下载路径:linux kernel
在这里插入图片描述

本文主要分析hub_event()函数的内容;hub_event()函数是Linux内核USB子系统中的一个函数,名为hub_event。这个函数的主要作用是处理USB集线器(Hub)的事件。这些事件可能包括USB设备的连接和断开,以及USB端口状态的改变等。
这个函数首先会对USB设备进行锁定,然后检查设备的状态。如果设备已经断开连接,或者设备的状态为未连接,那么它就会进行清理工作。
如果设备处于活动状态,那么它会尝试自动恢复USB接口,然后处理端口状态的改变。对于每个端口,如果它的状态发生了改变,那么就会调用port_event函数来处理端口事件。
最后,如果集线器的状态发生了改变,那么它会获取集线器的状态,然后根据状态的改变来进行相应的操作。例如,如果发生了电源改变,它会清除电源特性,并根据电源状态来设置limited_power标志。如果发生了过流改变,那么它会清除过流特性,然后尝试打开集线器的电源,并检查是否存在过流条件。
这个函数的最后,会解锁USB设备,并允许设备自动挂起。

下面我们来看下源码,

路径:drivers\usb\core\hub.c
static void hub_event(struct work_struct *work)
{
	struct usb_device *hdev;
	struct usb_interface *intf;
	struct usb_hub *hub;
	struct device *hub_dev;
	u16 hubstatus;
	u16 hubchange;
	int i, ret;

	hub = container_of(work, struct usb_hub, events);
	hdev = hub->hdev;
	hub_dev = hub->intfdev;
	intf = to_usb_interface(hub_dev);

	kcov_remote_start_usb((u64)hdev->bus->busnum);

	dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
			hdev->state, hdev->maxchild,
			/* NOTE: expects max 15 ports... */
			(u16) hub->change_bits[0],
			(u16) hub->event_bits[0]);

	/* Lock the device, then check to see if we were
	 * disconnected while waiting for the lock to succeed. */
	usb_lock_device(hdev);
	if (unlikely(hub->disconnected))
		goto out_hdev_lock;

	/* If the hub has died, clean up after it */
	if (hdev->state == USB_STATE_NOTATTACHED) {
		hub->error = -ENODEV;
		hub_quiesce(hub, HUB_DISCONNECT);
		goto out_hdev_lock;
	}

	/* Autoresume */
	ret = usb_autopm_get_interface(intf);
	if (ret) {
		dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
		goto out_hdev_lock;
	}

	/* If this is an inactive hub, do nothing */
	if (hub->quiescing)
		goto out_autopm;

	if (hub->error) {
		dev_dbg(hub_dev, "resetting for error %d\n", hub->error);

		ret = usb_reset_device(hdev);
		if (ret) {
			dev_dbg(hub_dev, "error resetting hub: %d\n", ret);
			goto out_autopm;
		}

		hub->nerrors = 0;
		hub->error = 0;
	}

	/* deal with port status changes */
	for (i = 1; i <= hdev->maxchild; i++) {
		struct usb_port *port_dev = hub->ports[i - 1];

		if (test_bit(i, hub->event_bits)
				|| test_bit(i, hub->change_bits)
				|| test_bit(i, hub->wakeup_bits)) {
			/*
			 * The get_noresume and barrier ensure that if
			 * the port was in the process of resuming, we
			 * flush that work and keep the port active for
			 * the duration of the port_event().  However,
			 * if the port is runtime pm suspended
			 * (powered-off), we leave it in that state, run
			 * an abbreviated port_event(), and move on.
			 */
			pm_runtime_get_noresume(&port_dev->dev);
			pm_runtime_barrier(&port_dev->dev);
			usb_lock_port(port_dev);
			port_event(hub, i);
			usb_unlock_port(port_dev);
			pm_runtime_put_sync(&port_dev->dev);
		}
	}

	/* deal with hub status changes */
	if (test_and_clear_bit(0, hub->event_bits) == 0)
		;	/* do nothing */
	else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
		dev_err(hub_dev, "get_hub_status failed\n");
	else {
		if (hubchange & HUB_CHANGE_LOCAL_POWER) {
			dev_dbg(hub_dev, "power change\n");
			clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
			if (hubstatus & HUB_STATUS_LOCAL_POWER)
				/* FIXME: Is this always true? */
				hub->limited_power = 1;
			else
				hub->limited_power = 0;
		}
		if (hubchange & HUB_CHANGE_OVERCURRENT) {
			u16 status = 0;
			u16 unused;

			dev_dbg(hub_dev, "over-current change\n");
			clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
			msleep(500);	/* Cool down */
			hub_power_on(hub, true);
			hub_hub_status(hub, &status, &unused);
			if (status & HUB_STATUS_OVERCURRENT)
				dev_err(hub_dev, "over-current condition\n");
		}
	}

out_autopm:
	/* Balance the usb_autopm_get_interface() above */
	usb_autopm_put_interface_no_suspend(intf);
out_hdev_lock:
	usb_unlock_device(hdev);

	/* Balance the stuff in kick_hub_wq() and allow autosuspend */
	usb_autopm_put_interface(intf);
	kref_put(&hub->kref, hub_release);

	kcov_remote_stop();
}

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

第12行:intf = to_usb_interface(hub_dev);container_of 是一个在Linux内核中广泛使用的宏,它用于获取包含特定成员的结构体的指针。container_of宏的作用是获取包含work成员的struct usb_hub类型的结构体的指针。这里的work是结构体struct usb_hub中的一个成员,而events是struct usb_hub中的另一个成员。这行代码的含义是,根据work成员的指针来获取包含这个成员的struct usb_hub结构体的指针。
一般来说,container_of宏用于在知道结构体中某个成员的指针的情况下,获取包含这个成员的整个结构体的指针。这在内核编程中非常有用,因为我们经常需要从结构体的一个成员出发,来操作整个结构体。

路径:include\linux\container_of.h
#define container_of(ptr, type, member) ({				\
	void *__mptr = (void *)(ptr);					\
	static_assert(__same_type(*(ptr), ((type *)0)->member) ||	\
		      __same_type(*(ptr), void),			\
		      "pointer type mismatch in container_of()");	\
	((type *)(__mptr - offsetof(type, member))); })

第15行:intf = to_usb_interface(hub_dev); to_usb_interface是一个宏,用于将一个类型为struct device *的指针转换为struct usb_interface *类型的指针。这个宏通常在需要操作USB接口时使用。 这里它的作用是将hub_dev转换为一个USB接口指针,并赋值给intf。

路径:include\linux\usb.h
#define to_usb_interface(__dev)	container_of_const(__dev, struct usb_interface, dev)
路径:include\linux\container_of.h
#define container_of_const(ptr, type, member)				\
	_Generic(ptr,							\
		const typeof(*(ptr)) *: ((const type *)container_of(ptr, type, member)),\
		default: ((type *)container_of(ptr, type, member))	\
	)

第17行:kcov_remote_start_usb((u64)hdev->bus->busnum); kcov_remote_start_usb函数是用于启动USB设备的代码覆盖率(Code Coverage)收集的。这个函数通常在USB设备的初始化过程中被调用,用于启动对USB设备的代码覆盖率收集。

第27行:usb_lock_device(hdev);usb_lock_device函数是用于锁定USB设备的,以防止在操作设备时发生并发问题。这个函数通常在需要对USB设备进行保护的操作之前调用,例如在修改设备状态或者读写设备数据等操作之前。

路径:include\linux\usb.h
#define usb_lock_device(udev)			device_lock(&(udev)->dev)
#define usb_unlock_device(udev)			device_unlock(&(udev)->dev)
路径:include\linux\device.h
static inline void device_lock(struct device *dev)
{
	mutex_lock(&dev->mutex);
}
static inline void device_unlock(struct device *dev)
{
	mutex_unlock(&dev->mutex);
}

第28行:unlikely(hub->disconnected);unlikely是Linux内核中的一个宏,用于向编译器提供代码执行频率的提示。unlikely(expr)表示表达式expr的结果预期为假(或者说,expr预期不经常发生)。unlikely宏用于告诉编译器,hub->disconnected这个条件预期不会经常为真。也就是说,我们预期USB集线器不会经常断开连接。
这个宏的主要作用是优化代码的执行效率。通过这个宏,编译器可以将不经常执行的代码放到程序的冷路径(Cold Path)中,从而优化CPU的指令缓存使用。unlikely宏的定义在Linux内核源码的include/linux/compiler.h文件中。具体的宏定义如下:

路径:tools\include\linux\compiler.h
#ifndef likely
# define likely(x)		__builtin_expect(!!(x), 1)
#endif

#ifndef unlikely
# define unlikely(x)		__builtin_expect(!!(x), 0)
#endif

第32-36行:这段代码是在处理USB集线器的事件时,检查集线器的状态,如果集线器已经断开连接(即设备状态为USB_STATE_NOTATTACHED),则进行一些清理工作。将集线器的错误状态设置为-ENODEV,表示设备不存在。这是一个标准的Linux错误码,通常用于表示设备已经断开连接或者设备不存在。hub_quiesce函数的作用是停止集线器的所有活动,并释放所有与集线器相关的资源。

第39-43行:调用usb_autopm_get_interface函数尝试自动恢复USB接口intf。这个函数的返回值保存在ret变量中。如果ret为0,表示自动恢复成功;否则表示自动恢复失败,ret的值为错误码。

第46-47行:检查USB集线器(hub)是否处于静默(quiescing)状态。如果集线器处于静默状态,那么它就不会进行任何操作,并立即跳转到标签out_autopm处执行后续的代码。

第49-60行:主要作用是检查USB集线器(hub)是否有错误,如果有错误则尝试重置集线器,并清除错误状态。
ret = usb_reset_device(hdev);:这行代码调用usb_reset_device函数尝试重置集线器。这个函数的返回值保存在ret变量中。如果ret为0,表示重置成功;否则表示重置失败,ret的值为错误码。

第63-85行:主要作用是处理USB集线器(Hub)上的端口状态变化。对于每一个端口,如果它的事件位、变化位或唤醒位被设置,那么就会对这个端口执行一次端口事件处理。
(1)for (i = 1; i <= hdev->maxchild; i++) {…}:这个for循环遍历集线器上的所有端口。hdev->maxchild表示集线器上的端口数量。
(2)struct usb_port *port_dev = hub->ports[i - 1];:这行代码获取第i个端口的struct usb_port结构体的指针。
(3)if (test_bit(i, hub->event_bits) || test_bit(i, hub->change_bits) || test_bit(i, hub->wakeup_bits)) {…}:这个if语句检查端口的事件位、变化位和唤醒位是否被设置。如果任一位被设置,那么就执行后面的代码块。
(4)pm_runtime_get_noresume(&port_dev->dev);和pm_runtime_barrier(&port_dev->dev);:这两行代码的作用是防止端口在处理事件期间被挂起。如果端口正在恢复过程中,这两个函数会保持端口的活动状态;如果端口处于运行时电源管理挂起状态,这两个函数会使端口保持在这个状态。
(5)usb_lock_port(port_dev);和usb_unlock_port(port_dev);:这两行代码使用互斥锁来保护端口,防止在处理事件期间发生并发问题。
(6)port_event(hub, i);:这行代码调用port_event函数来处理端口事件。
(7)pm_runtime_put_sync(&port_dev->dev);:这行代码减少端口的运行时PM引用计数,并可能触发端口的挂起。

第88-114行:主要作用是处理USB集线器(Hub)状态的变化。当集线器的状态发生变化时,它会获取集线器的状态,并根据状态的变化来进行相应的操作。
(1)if (test_and_clear_bit(0, hub->event_bits) == 0) {…}:这个if语句检查集线器的事件位是否被设置。如果事件位没有被设置,那么就不进行任何操作。
(2)else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) {…}:这个else if语句尝试获取集线器的状态。如果获取状态失败,那么就打印一条错误信息。
(3)if (hubchange & HUB_CHANGE_LOCAL_POWER) {…}:这个if语句检查集线器的本地电源状态是否发生变化。如果发生了变化,那么就清除电源特性,并根据电源状态来设置limited_power标志。
(4)if (hubchange & HUB_CHANGE_OVERCURRENT) {…}:这个if语句检查集线器是否发生了过流变化。如果发生了过流变化,那么就清除过流特性,然后尝试打开集线器的电源,并检查是否存在过流条件。
所以,这段代码的主要作用是处理USB集线器状态的变化。当集线器的状态发生变化时,它会获取集线器的状态,并根据状态的变化来进行相应的操作。

第116-126:这段代码主要负责在处理USB集线器事件结束后进行一些清理工作,包括释放获取的资源、解锁设备以及停止代码覆盖率(Code Coverage)收集。
代码的作用如下:
(1)usb_autopm_put_interface_no_suspend(intf);:这行代码减少intf的运行时PM引用计数,但不会触发设备的自动挂起。这个函数与之前的usb_autopm_get_interface函数对应,用于平衡引用计数。
(2)usb_unlock_device(hdev);:这行代码解锁USB设备hdev。这个函数与之前的usb_lock_device函数对应,用于释放之前获取的设备锁。
(3)usb_autopm_put_interface(intf);:这行代码减少intf的运行时PM引用计数,并可能触发设备的自动挂起。这个函数与之前的usb_autopm_get_interface函数对应,用于平衡引用计数。
(4)kref_put(&hub->kref, hub_release);:这行代码减少集线器hub的引用计数。如果引用计数变为0,那么就会调用hub_release函数来释放集线器。这个函数与之前的kref_get函数对应,用于平衡引用计数。
(5)kcov_remote_stop();:这行代码停止代码覆盖率(Code Coverage)收集。

所以,这段代码的主要作用是在处理USB集线器事件结束后进行一些清理工作,包括释放获取的资源、解锁设备以及停止代码覆盖率(Code Coverage)收集。

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

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

相关文章

面试之ReentrantLock

一&#xff0c;ReentrantLock 1.ReentrantLock是什么&#xff1f; ReentrantLock实现了Lock接口&#xff0c;是一个可重入且独占式的锁&#xff0c;和Synchronized关键字类似&#xff0c;不过ReentrantLock更灵活&#xff0c;更强大&#xff0c;增加了轮询、超时、中断、公平锁…

侯捷 C++ part2 兼谈对象模型笔记——7 reference、const、new/delete

7 reference、const、new/delete 7.1 reference x 是整数&#xff0c;占4字节&#xff1b;p 是指针占4字节&#xff08;32位&#xff09;&#xff1b;r 代表x&#xff0c;那么r也是整数&#xff0c;占4字节 int x 0; int* p &x; // 地址和指针是互通的 int& r x;…

windows电脑简单实时tts语音播报wsay;python pyttsx3语言实时播报text-to-speech;微软edge-tts 音色自然离线不实时

1、wsay 参考&#xff1a; https://github.com/p-groarke/wsay 下载安装&#xff1a; https://github.com/p-groarke/wsay/releases/tag/v1.5.0 下载exe文件&#xff0c;并把加入环境变量就可 使用 # Say something. wsay "Hello there."wsay "你好"…

图书馆管理系统、学生管理系统、交通管理系统(C语言、数据结构、java、Javaweb)

图书馆管理系统作为一个经典的项目&#xff0c;在国家、学校、等每个地方或者作为期末作品都用的非常广泛&#xff1a; C语言程序设计&#xff1a;图书馆管理系统含说明文档。 大一时C综合设计&#xff0c;当时得了96。代码纯原创&#xff0c;可直接运行&#xff0c;包含详细注…

springboot多数据源配置,看这一篇就够了

springboot下多数据源配置实现 不管是两个mysql&#xff0c;还是一个mysql一个oracle&#xff0c;都是一样的操作 目录 springboot下多数据源配置实现配置application.yml文件数据源配置类创建mapper接口创建mapper的xml配置文件 你可能会遇到的问题 配置application.yml文件 …

无涯教程-Perl - study函数

描述 此功能需要花费额外的时间来研究EXPR,以改善在EXPR上执行的正则表达式的性能。如果省略EXPR,则使用$_。实际的速度增益可能非常小,具体取决于您希望搜索字符串的次数。 您一次只能学习一种表达式或标量。 语法 以下是此函数的简单语法- study EXPRstudy返回值 此函数…

Scala 如何调试隐式转换--隐式转换代码的显示展示

方法1 在需要隐式转换的地方&#xff0c;把需要的参数显示的写出。 略方法2&#xff0c;查看编译代码 在terminal中 利用 scalac -Xprint:typer xxx.scala方法打印添加了隐式值的代码示例。 对于复杂的工程来说&#xff0c;直接跑到terminal执行 scalac -Xprint:typer xxx.…

薅羊毛!我用这款AI工具,免费拿下12张漫画头像

今天l1m0_将为大家分享一款AI生图工具&#xff0c;并介绍如何通过Pixso AI&#xff0c;用自己的照片&#xff0c;免费一键生成AI漫画头像&#xff0c;一起来看看吧。 这里我用Pixso资源社区的一组用户头像资源&#xff0c;为大家演示&#xff0c;如何快速生成AI漫画头像。 首先…

小程序开发:如何选择合适的开发工具和平台?

小程序是一种基于微信平台的轻量级应用程序&#xff0c;具有操作简便、体验流畅等优点。然而&#xff0c;对于许多中小企业来说&#xff0c;三五万的开发成本可能过高&#xff0c;让人感到犹豫。 首先&#xff0c;三五万的成本包括了开发人员的费用、服务器费用、推广费用等。对…

JS大纲简介

1 HTML中的JavaScript js引用文件可以放在两个位置&#xff0c;一种是html中的head中&#xff0c;一种是html中的body中&#xff1b;放置在这两个位置&#xff0c;有何区别呢&#xff1f; 1.1 使用<script>元素的方式 1.1.1 放置在 head 中 引用example.js文件&#…

微信公众号扫码实现网站登录-Django+Vue版本-超详细保姆级教程

实现网页端微信扫码登录有三种方式&#xff1a; PlanA&#xff1a;微信开放平台 — 需认证 — 300元PlanB&#xff1a;微信公众号 — 需服务号且已认证 — 300元PlanC&#xff1a;微信小程序 — 需已上线备案的小程序 — 0元 本教程为Djangovue举例的微信公众号扫码登录&#…

.Net程序调试时接受外部命令行参数方式

1.对项目右键&#xff0c;属性 2.在调试中打开常规&#xff0c;打开调试启动配置文件UI 3.输入需要的命令行参数

Vue3 setup中使用$refs

在 Vue 3 中的 Composition API 中&#xff0c;$refs 并不直接可用于 setup 函数。这是因为 $refs 是 Vue 2 的实例属性&#xff0c;而在 Vue 3 中&#xff0c;setup 函数是与模板实例分离的&#xff0c;不再使用实例属性。 实际工作中确实有需求&#xff0c;在setup 函数使用…

Vue实现动态遍历生成el-input

实现效果: el-input的label是measureName, el-input绑定的值是formDatat.measureCode 接口返回的数据格式如下 处理过的formData的格式如下

Java开发工具哪个好,哪些常见

Java开发工具是很重要的存在&#xff0c;选择合适的Java开发工具对于开发人员来说非常重要。一个好的开发工具可以提高开发效率、简化开发流程并提供丰富的功能和工具支持。 以下是动力节点推荐的一些常用且受欢迎的Java开发工具&#xff1a; Eclipse&#xff1a;Eclipse是一款…

在 OpenCV 中使用深度学习进行年龄检测-附源码

文末附完整源码和模型文件下载链接 在本教程中,我们将了解使用 OpenCV 创建年龄预测器和性别分类器项目的整个过程。 年龄检测 我们的目标是创建一个程序,使用图像来预测人的性别和年龄。但预测年龄可能并不像你想象的那么简单,为什么呢?您可能会认为年龄预测是一个回归问…

【idea】社区版idea运行Tomcat

使用 Smart Tomcat插件 配置运行&#xff1a;

JavaScript常见语法--菜鸟教程

文章目录 JavaScript 语法<body> 中的 JavaScript JavaScript 输出使用 window.alert()操作 HTML 元素写到 HTML 文档写到控制台 JavaScript 语法JavaScript 字面量JavaScript 变量JavaScript 操作符JavaScript 语句JavaScript 字符集JavaScript判断类型 JavaScript数据类…

冉冉升起的星火,再度升级迎来2.0时代!

文章目录 前言权威性评测结果 星火大模型多模态功能插件功能简历生成文档问答PPT生成 代码能力 福利 前言 前几天从技术群里看到大家都在谈论《人工智能大模型体验报告2.0》里边的内容&#xff0c;抱着好奇和学习的态度把报告看了一遍。看完之后瞬间被里边提到的科大讯飞的星火…

基于ACF,AMDF算法的语音编码matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .......................................................................... plotFlag …