Linux USB host driver 枚举前的源码分析

当我们插入一个USB设备,系统如何感知到USB设备的接入,后续发生了哪些细节?系统如何区分这些USB设备?主机侧如何和这些从机设备进行数据的交互?
这里参考Linux kernel 4.9.xx的代码,部分异常和次要代码在这里没有体现。

usb_hub_init

在 4.9 的 Linux 内核中,通过subsys_initcall,在系统启动时注册 USB 子系统的初始化函数,以确保在系统正常运行之前进行必要的 USB 初始化工作。
usb_init里,进行bus_register以及usb_hub_init。当hub_driver完成了注册,通过hub probe完成hub_event的初始化工作。当有设备插入时,感知到硬件上的电平变化,后续的工作将在hub_event里完成。

int usb_hub_init(void)
{if (usb_register(&hub_driver) < 0) {printk(KERN_ERR "%s: can't register hub driver\n",usbcore_name);return -1;}
}

hub_driver结构体如下:

static struct usb_driver hub_driver = {.name =		"hub",.probe =	hub_probe,.disconnect =	hub_disconnect,.suspend =	hub_suspend,.resume =	hub_resume,.reset_resume =	hub_reset_resume,.pre_reset =	hub_pre_reset,.post_reset =	hub_post_reset,.unlocked_ioctl = hub_ioctl,.id_table =	hub_id_table,.supports_autosuspend =	1,
};

hub_event

hub_event,通过检测event_bitschange_bitswakeup_bits是否置位。当以上三种情况有一个发生时,就会发起port_event。同时后续会对hub的状态进行状态管理。

static void hub_event(struct work_struct *work)
{...if (test_bit(i, hub->event_bits)|| test_bit(i, hub->change_bits)|| test_bit(i, hub->wakeup_bits)) {port_event(hub, i);}/* deal with hub status changes */...
}

port_event

static void port_event(struct usb_hub *hub, int port1)__must_hold(&port_dev->status_lock)
{if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))connect_change = 1;if (connect_change)hub_port_connect_change(hub, port1, portstatus, portchange);
}

hub_handle_remote_wakeup 和 hub_port_connect_change

当发生以下情况时调用hub_port_connect_change

  • 端口连接状态发生变化;
  • 端口使能状态发生变化(通常由电磁干扰引起);
  • usb_reset_and_verify_device() 遇到了变化的描述符(例如:固件下载)

调用此函数时,必须已经拿到hub的锁。

static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)__must_hold(&port_dev->status_lock)
{...hub_port_connect(hub, port1, portstatus, portchange);...
}

hub_port_connect

如果说前面都是检测“设备接入”状态的流程,那这里就是注册USB新的deivce的核心流程。当检测到新设备时,需要在总线上进行注册。注册前,需要填充udev结构,这个结构的内容分别由以下几个函数完成。

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,u16 portchange)
{...udev = usb_alloc_dev(hdev, hdev->bus, port1);//注册一个usb device,然后会放在usb总线上usb_set_device_state(udev, USB_STATE_POWERED);//设置注册的USB设备的状态标志choose_devnum(udev);//给新的设备分配一个地址编号status = hub_port_init(hub, udev, port1, i);//分配设备地址,获取详细的设备描述符//前面都在填充udev结构内容,这里才将udev结构注册到总线上status = usb_new_device(udev);//创建USB设备,与USB设备驱动连接...
}
usb_alloc_dev

这是USB 设备构造函数,这里设置device的成员,每当创建一个USB设备时,总线都会调用 .match匹配的函数,使得USB driver和device绑定。

struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1)
{dev = kzalloc(sizeof(*dev), GFP_KERNEL);//分配usb device设备结构体device_initialize(&dev->dev);//初始化usb devicedev->dev.bus = &usb_bus_type;//设置绑定usb device的成员dev->dev.type = &usb_device_type;dev->dev.groups = usb_device_groups;
}

usb_bus_type 的结构如下:

struct bus_type usb_bus_type = {.name =         "usb",            //总线名称,存在/sys/bus下.match = usb_device_match,    //匹配函数,匹配成功就会调用usb_driver驱动的probe函数成员.uevent =       usb_uevent,      //事件函数(可选).suspend =     usb_suspend,    //休眠函数(可选).resume =      usb_resume,     //唤醒函数(可选)
};
choose_devnum

这里主要是在devnum_next~128之间,寻找下一个非0(没有被设备占用)的编号。如果被占用则往后顺延。0地址是被用作初次接入未初始化USB设备所使用。

hub_port_init

hub_port_init的作用是:重置设备,分配地址,获取设备描述符。
此时设备连接必须稳定,成功返回 USB_STATE_ADDRESS 状态的设备,除非出现错误。
如果这是为一个已经存在的设备调用( 比如在usb_reset_and_verify_device 时调用),调用者必须拥有设备的锁和端口的锁。
这里通过获取设备的speed来决定ep0最大数据包大小。因为不同的speed,包的大小并不相同。对于无线 USB 设备,端点 0 的最大数据包大小始终为 512。
此外也可以直接读取前8个字节,获得USB设备描述符,因为这8个每个设备都有,后面再根据设备的数据,通过usb_get_device_descriptor再重新读一次目标设备的描述结构。

static int
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
{...retval = hub_set_address(udev, devnum);//设置地址,告诉USB新的地址号retval = usb_get_device_descriptor(udev, 8);//获得USB设备描述符的前8个字节//还不确定对方支持的包容量,所以先读8个,这8个每个设备都有,后面再根据设备的数据,通过//usb_get_device_descriptor再重新读一次目标设备的描述结构。 /*struct usb_device_descriptor {__u8  bLength;                          //本描述符的size__u8  bDescriptorType;                //描述符的类型,这里是设备描述符DEVICE__u16 bcdUSB;                           //指明usb的版本,比如usb2.0__u8  bDeviceClass;                    //类__u8  bDeviceSubClass;                 //子类__u8  bDeviceProtocol;                  //指定协议__u8  bMaxPacketSize0;                 //端点0对应的最大包大小__u16 idVendor;                         //厂家ID__u16 idProduct;                        //产品ID__u16 bcdDevice;                        //设备的发布号__u8  iManufacturer;                    //字符串描述符中厂家ID的索引__u8  iProduct;                         //字符串描述符中产品ID的索引__u8  iSerialNumber;                   //字符串描述符中设备序列号的索引__u8  bNumConfigurations;               //可能的配置的数目} __attribute__ ((packed));*/retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);//重新获取设备描述符信息...
}

为什么要以这种方式交错 GET_DESCRIPTORSET_ADDRESS
因为设备的硬件和固件在这个领域有时会有缺陷,而这是 Linux 多年来一直采用的方法。

usb_new_device

此函数用于检测但尚未完全枚举的设备。获取设备描述符信息,打印device的相关信息,然后将device添加到usb bus的链表中。

此调用是同步的,不能在中断上下文使用。返回值为设备是否被正确配置。如果接口已在驱动程序核心注册,则返回0。

int usb_new_device(struct usb_device *udev)
{err = usb_enumerate_device(udev);	/* Read descriptors */ //获取描述符信息/* Tell the world! */announce_device(udev);err = device_add(&udev->dev);//注册device。将device放入bus的dev链表中,并寻找对应的设备驱动}

总结

当我们插上USB设备,系统会根据电平信号的变化,检测到新设备的接入,接着获取USB设备、配置、接口、端点的数据,并创建新设备。

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

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

相关文章

c++算法学习笔记 (20) 哈希表

1.模拟散列表 // 拉链法 #include <bits/stdc.h> using namespace std; const int N 100003; int h[N]; int e[N], ne[N], idx; // 存链void insert(int x) {int k (x % N N) % N; // 让负数的余数变成正数(若直接加N,则可能溢出)e[idx] x;ne[idx] h[k];h[k] idx;…

Spring自定义事件处理完全解析!2024美团春招面试题大全,超详细解答,必备收藏!

在2024年的技术招聘季中&#xff0c;随着Spring框架在企业开发中的广泛应用&#xff0c;对Spring框架深入理解和应用能力的需求日益增长。美团作为中国领先的生活服务电子商务平台&#xff0c;对技术人才的要求尤为严格&#xff0c;特别是在Spring框架的应用上。Spring自定义事…

如何确保正向代理处理的安全性?

确保正向代理的安全性是至关重要的&#xff0c;因为代理服务器作为中介&#xff0c;处理客户端和目标服务器之间的通信。以下是一些关键步骤和最佳实践&#xff0c;以确保正向代理的安全性&#xff1a; 1、访问控制&#xff1a; 1、使用访问控制列表&#xff08;ACL&#xff…

传动设计选型

一. 齿轮选型 1齿轮传动概述 齿轮传动首先自然是一对对的&#xff0c;故也叫齿轮副。原理其实大家基本都知道&#xff0c;很多玩具中都会有齿轮传动的身影&#xff0c;比如小时候玩的四驱车。就是两齿轮互相啮合互相推动&#xff0c;然后输出旋转动力或者改变传递动力的方向&…

浏览器插件自动化插件 Tampermonkey autojs 区别,脱离手动操作,代替人工操作

Tampermonkey autojs 区别 修改复制 Tampermonkey 和 AutoJS 都是浏览器扩展程序&#xff0c;用于在浏览器上运行 JavaScript 脚本。它们的主要区别在于&#xff1a; Tampermonkey 是一个通用的脚本管理器&#xff0c;支持多种浏览器&#xff0c;包括 Chrome、Firefox、Safar…

基于SpringBoot Vue学生信息管理

一、&#x1f4dd;功能介绍 基于SpringBoot Vue学生信息管理 角色&#xff1a;管理员、学生、教师 管理员&#xff1a;管理员进入主页面&#xff0c;主要功能包括对系统首页、个人中心、学生管理、教师管理、公告通知管理、课程类型管理、课程信息管理、选课信息管理、课程成…

输出各位数字之和能被15整除的所有数

输出各位数字之和能被15整除的所有数 输出100到1000之间的各位数字之和能被15整除的所有数&#xff0c;输出时每5个一行&#xff0c;每个数输出占4列列宽。 public class Day5 {public static void main(String[] args) {int t,flag0;for(int i100;i<1000;i){t (i/100)(i…

使用STM32实现 蓝牙插座

硬件介绍 蓝牙模块HC-01 蓝牙模块&#xff0c;又叫做蓝牙串口模块 串口透传技术&#xff1a;透传即透明传送&#xff0c;是指在数据的传输过程中&#xff0c;通过无线的方式这组数据不发生任何形式的改变&#xff0c;仿佛传输过程是透明的一样&#xff0c;同时保证传输的质量&…

农业地物反演采用卫星影像技术方法

一、引言 随着空间技术的快速发展&#xff0c;卫星影像已经成为一种重要的工具&#xff0c;用于提供全球尺度的数据收集和分析。在农业领域&#xff0c;卫星影像被广泛应用于植物反演&#xff0c;以获取关于作物生长和健康的实时信息。本文旨在全面概述卫星影像在农业植物反演中…

【排序,找规律,站队型】

茵茵所在的合唱队共有N个人&#xff08;N为奇数&#xff09;。为了准备一次演出&#xff0c;老师开始为她们安排合唱队形了。大家都知道&#xff0c;合唱队形通常是中间高两端低的。老师是这样安排他们的队形的&#xff1a;先让所有的同学按高个儿在前的顺序排成一队。然后&…

baseline SE SP YI是什么?

SE、SP和YI是评估分类模型性能时常用的几个统计指标&#xff0c;特别是在医学影像处理、疾病诊断等领域&#xff0c;这些指标帮助了解模型对于正负类样本的识别能力。 SE (Sensitivity)&#xff0c;也称为真正率&#xff08;True Positive Rate, TPR&#xff09;或召回率&#…

鸿蒙ArkUI实例:【自定义组件】

组件是 OpenHarmony 页面最小显示单元&#xff0c;一个页面可由多个组件组合而成&#xff0c;也可只由一个组件组合而成&#xff0c;这些组件可以是ArkUI开发框架自带系统组件&#xff0c;比如 Text 、 Button 等&#xff0c;也可以是自定义组件&#xff0c;本节笔者简单介绍一…

《LeetCode力扣练习》代码随想录——二叉树(二叉搜索树的最小绝对差---Java)

《LeetCode力扣练习》代码随想录——二叉树&#xff08;二叉搜索树的最小绝对差—Java&#xff09; 刷题思路来源于 代码随想录 530. 二叉搜索树的最小绝对差 二叉树-中序遍历 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* T…

hive词频统计---文件始终上传不来

目录 准备工作&#xff1a; 文件内容&#xff1a; 创建数据库及表 将文件上传到&#xff1a;上传到/user/hive/warehouse/db1.db/t_word目录下 hive里面查询&#xff0c;始终报错&#xff1a;&#xff08;直接查询也是不行&#xff09; 解决方案&#xff1a; 准备工作&am…

OPC UA遇见chatGPT

最近opc 基金会将召开一个会议&#xff0c;主题是”OPC UA meets IT“。由此可见&#xff0c;工业自动化行业也开始研究和评估chatGPT带来的影响了。 本文谈谈本人对OPC UA 与chatGPT结合的初步实验和思考。 构建OPC UA 信息模型 chatGPT 的确非常强大了&#xff0c;使用自然…

Stable Diffusion文生图技术详解:从零基础到掌握CLIP模型、Unet训练和采样器迭代

文章目录 概要Stable Diffusion 底层结构与原理文本编码器&#xff08;Text Encoder&#xff09;图片生成器&#xff08;Image Generator&#xff09; 那扩散过程发生了什么&#xff1f;stable diffusion 总体架构主要模块分析Unet 网络采样器迭代CLIP 模型 小结 概要 Stable …

C++设计模式:装饰器模式(四)

1、定义与动机 装饰器模式定义&#xff1a;动态&#xff08;组合&#xff09;地给一个对象增加一些额外的职责。就增加功能而言&#xff0c;Decorator模式比生成子类&#xff08;继承&#xff09;更为灵活&#xff08;消除重复代码 & 减少子类个数&#xff09;。 在某些情…

如何理解CDN?说说实现原理?

文章目录 一、是什么二、原理分析负载均衡系统缓存代理 三、总结参考文献 一、是什么 CDN (全称 Content Delivery Network)&#xff0c;即内容分发网络 构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内…

【开启自启动】软件设置开机自启动的相关问题

。 。 。 。 。 。 。 。 1、这个是设置开启自动启动的代码 //这个是设置开启自动启动的代码 #define REG_RUN_MANAGE "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run" //#define REG_RUN_MANAGE "HKEY_LOCAL_MACHINE\…

Wooden Toy Festival 题解

Wooden Toy Festival题解 题目在这 题目在这 题目在这 思路 &#xff1a; &#xff1a; &#xff1a; 二分&#xff0c;二分距离&#xff0c;首先肯定要排序&#xff0c;然后这题还得去一下重(因为题目说了"雕刻师们都是非常熟练的人&#xff0c;可以同时为不同的人完成…