usb博大精深不是一篇文章就能够解释清楚的。想要深入研究usb的话,usb协议(外加host和otg协议)是必要的知识,另外,国内有本也写的很好很详细(点击阅读原文,21ic嵌入式论坛有下载),唯一美中不足的就是写得太详细了反而感觉思路架构不是很清晰了。今天我们来简单地把usb在linux里的结构框架大致整理下,其中重点解析下usb core和hub。
0. 预备理论
说实话,读usb协议还是蛮痛苦的,它仅仅是一个协议,一个在usb世界里制定的游戏规则,就像法律条文一样,它并不是为了学习者而写的,可读性很差。这里总结以下几个重点基本点。
0.1 拓扑结构 (ch4.1.1)
·之所以要规定这个树形拓扑结构是为了避免环形连接。
·一条usb总线有且只有一个usb host,对应一个roothub
·usb设备分为两类,hub和functions,hub通过端口port连接更多usb设备,functions即usb外接从设备。
·层次最多7层,且第7层不能有hub,只能有functions。
·compound device - 一个hub上接多个设备组成一个小设备。
·composite device - 一个usb外接设备具有多个复用功能。
0.2 机械性能 (ch5)
·连接件connector,就是设备上的那个连接口。
·插头plug,就是usb电缆线两头的插口。
·mini-ab, micro-ab指的是支持a和b两类插头的连接件。
0.3 电气性能 (ch6)
·vbus - +5v电源供电。
·d+ d- - 用于数据传输的电缆线。
·低速 low-speed 10-100kb/s 应用于鼠标和键盘等
·全速 full-speed 500kb-10mb/s应用于音频和麦克等
·高速high-speed 25-400mb/s 应用于存储和视频等 (usb3.0比之块10倍)
0.4 四大描述符 (ch9.5)
协议规定了usb的四个描述符descriptor - 设备device,配置configure,接口interface,端点endpoint。
终端下输入命令 # ls /sys/bus/usb/devices
usb1 1-0:1.0 usb2 2-0:1.0 // usb总线(roothub) no.2,usb port端口号no.0,配置号no.1,接口号no.0。
·区别port和endpoint,port之于hub,endpoint是每个usb设备用于数据传输所必需的端点。
·设备device>配置configure>接口interface>设置setting>端点endpoint。
·设备可以有多个配置,配置可以有一个或多个接口,接口可以有一个或多个设置。
·一个接口对应一个驱动,接口是端点的集合。
0.5 启动流程 (ch9.1,9.2)
·attached->powered->default->address->configured
·启动流程与其他设备比如sd卡相比,最大的不同在于hub,主机host通过hub状态的变化判断usb外接设备的有无。
·usb外接设备插入和拔出整个实现过程称为总线枚举bus enumeration。
0.6 数据流传输 (ch5)
·endpoint分零端点和非零端点,零端点作为默认的控制方法用于初始化和操控usb逻辑设备。
·数据流传输分 control/bulk/interrupt/isochronous data transfer。
0.7 数据包 (ch8)
·数据包分token, data, handshake, special,四种包有自己的数据组织方式。
·token令牌包只能由主机传送给设备,分in, out, sof和setup。
·setup包实现主机向设备发出的请求request,也要满足特定的格式。(ch9.3,9.4)
1. usb core
先啰嗦几句,回答一个困扰我很久的问题,读linux源码究竟要读到什么程度?这是个永恒的话题,每个同道中人都有自己的看法。以吾辈之见,如何阅读源码主要取决于自己的职业定位,是研发还是开发,是为linux社区作贡献还是用已有的方案开发?我想大多数驱动工程师属于后者,那么,面对已经很完善的核心层源码,还有必要看吗,或者有必要去深入研究吗?我认为既然我们已经站在了巨人的肩膀上,至少要知道这宽阔的肩膀是如何炼成的,它所存在的价值以及如何去使用它。
既然如此,那usb核心层到底是什么,它都默默地做了些什么,我们要如何使用它?这里主要有两个重点,usb总线和urb。
1.1 usb子系统结构
协议里说,hcd提供主控制器驱动的硬件抽象,它只对usb core一个负责,usb core将用户的请求映射到相关的hcd,用户不能直接访问hcd。换句话说,usb core就是hcd与usb设备唯一的桥梁。
1.2 usb子系统的初始化
usb core源码位于./drivers/usb/core,其中的makefile摘要如下,
usbcore这个模块代表的不是某一个设备,而是所有usb设备赖以生存的模块,它就是usb子系统。
./drivers/usb/core/usb.c里实现了初始化,伪代码如下,
usbcore注册了usb总线,usb文件系统,usb hub以及usb的设备驱动usb generic driver等。
1.3 usb总线
注册usb总线通过bus_register(&usb_bus_type);
struct bus_type usb_bus_type = {.name =usb,.match =usb_device_match, // 这是个很重要的函数,用来匹配usb设备和驱动。.uevent =usb_uevent,.pm =&usb_bus_pm_ops,};下面总结下usb设备和驱动匹配的全过程,
-> step 1 - usb device driver
usb子系统初始化的时候就会注册usb_generic_driver, 它的结构体类型是usb_device_driver,它是usb世界里唯一的一个usb设备驱动,区别于struct usb_driver usb驱动。
·usb设备驱动(usb device driver)就只有一个,即usb_generice_driver这个对象,所有usb设备都要绑定到usb_generic_driver上,它的使命可以概括为:为usb设备选择一个合适的配置,让设备进入configured状态。
·usb驱动(usb driver)就是usb设备的接口驱动程序,比如adb驱动程序,u盘驱动程序,鼠标驱动程序等等。
-> step 2 - usb driver
linux启动时注册usb驱动,在xxx_init()里通过usb_register()将usb驱动提交个设备模型,添加到usb总线的驱动链表里。
-> step3 - usb device
usb设备连接在hub上,hub检测到有设备连接进来,为设备分配一个struct usb_device结构体对象,并将设备添加到usb总线的设备列表里。
-> step4 - usb interface
usb设备各个配置的详细信息在usb core里的漫漫旅途中已经被获取并存放在相关的几个成员里。
usb_generic_driver得到了usb设备的详细信息,然后把准备好的接口送给设备模型,linux设备模型将接口添加到设备链表里,然后去轮询usb总线另外一条驱动链表,针对每个找到的驱动去调用usb总线的match函数,完成匹配。
1.4 usb request block (urb)
usb主机与设备间的通信以数据包(packet)的形式传递,linux的思想就是把这些遵循协议的数据都封装成数据块(block)作统一调度,usb的数据块就是urb,结构体struct urb,定义在,其中的成员unsigned char *setup_packet指针指向setup数据包。下面总结下使用urb完成一次完整的usb通信需要经历的过程,
-> step 1 - usb_alloc_urb()
创建urb,并指定usb设备的目的端点。
-> step 2 - usb_control_msg()
将urb提交给usb core, usb core将它交给hcd主机控制器驱动。
-> step3 - usb_parse_configuration()
hcd解析urb,拿到数据与usb设备通信。
-> step 4
hcd把urb的所有权交还给驱动程序。
协议层里最重要的函数就是usb_control/bulk/interrupt_msg(),这里就简单地理一条线索,
usb_control_msg() => usb_internal_control_msg() => usb_start_wait_urb() => usb_submit_urb() => usb_hcd_submit_urb => hcd->driver->urb_enqueue() hcd主控制器驱动根据具体平台实现usb数据通信。
2. usb hub
hub集线器用来连接更多usb设备,硬件上实现了usb设备的总线枚举过程,软件上实现了usb设备与接口在usb总线上的匹配。
下面总结下usb hub在linux usb核心层里的实现机制,
usb子系统初始化时,usb_hub_init()开启一个名为khubd的内核线程,
内核线程khubd从linux启动后就自始至终为usb hub服务,没有hub事件时khubd进入睡眠,有usb hub事件触发时将会经由hud_irq() => hub_activate() => kick_khubd() 最终唤醒khubd,将事件加入hub_event_list列表,并执行hub_events()。hub_events()会不停地轮询hub_events_list列表去完成hub触发的事件,直到这个列表为空时退出结束,回到wait_event_xxx继续等待。
处理hub事件的全过程大致可分为两步,
·第一步 判断端口状态的变化
通过hub_port_status()得到hub端口的状态。
源码里类似像hub_port_status(), hub_hub_status()等功能函数,都调用了核心层的usb_control_msg()去实现主控制器与usb设备间的通信。
·第二步处理端口的变化
hub_port_connect_change()是核心函数,以端口发现有新的usb设备插入为例,usb hub为usb设备做了以下几步重要的工作,注意这里所谓的usb设备是指插入usb hub的外接usb设备(包括hub和functions),接下来hub都在为usb设备服务。
1) usb_alloc_dev() 为usb设备申请一个sturct usb_device结构。
2) usb_set_device_state() 设置usb设备状态为上电状态。(硬件上设备已进入powered状态)。
3) choose_address() 为usb设备选择一个地址,利用一个轮询算法为设备从0-127里选择一个地址号。
4)hub_port_init() 端口初始化,实质就是获取设备描述符device descriptor。
5) usb_get_status() 这个有点特殊,它是专门给hub又外接hub而准备的。
6)usb_new_device() 这时usb设备已经进入了configured状态,调用device_add()在usb总线上寻找驱动,若匹配成功,则加载对应的驱动程序。
3. usb otg
引入otg的概念是为了让设备可以充当主从两个角色,主设备即hcd,从设备即udc,也就是gadget。这里就简单梳理下协议和源码。
3.1 协议
1) protocol
otg的传输协议有三类 - adp,srp,hnp。
·adp(attach detection protocol)当usb总线上没有供电时,adp允许otg设备或usb设备决定连接状态。
·srp(session request protocol) 允许从设备也可以控制主设备。
·hnp(host negotiation protocol)允许两个设备互换主从角色。
2) device role
协议定义两种角色,otg a-device和otg b-device,a-device为电源提供者,b-device为电源消费者,默认配置下,a-device作为主设备,b-device作为从设备,之后可以通过hnp互换。
3) otg micro plug
协议上说an otg product must have a single micro-ab receptacle and no other usb receptacles.这句话有点问题。。。应该还包括mini-ab receptacle,以下所有micro都可以是mini。
otg电缆一端为micro-a plug,另一端为micro-b plug。
otg加了第5个pin脚,名为id-pin,micro-a plug的id-pin接地,micro-b plug的id-pin悬空。
otg设备被接上micro-a plug后被称为micro-a device,被接上micro-b plug后被称为micro-b device。
3.2 源码浅析
otg控制器集成在cpu内,linux下的源码驱动由各家开发平台提供,位于./drivers/usb/otg/下。
以freescale平台为例,主要的思路就是,当有otg线插入otg设备时产生中断,中断处理函数上半部通过读取otg控制器寄存器相应值判断otg设备属于host(hcd)还是gadget(udc),下半部通过工作队列由回调函数类似host->resume()或gadget->resume()重启host或gadget控制器,resume()具体的实现过程在hcd或udc相关驱动里实现。
4. usb host
usb主控制器(hcd)同样集成在cpu内,由开发平台厂商提供驱动,源码位于./drivers/usb/host/下。
主控制器主要有四类:ehci, fhci, ohci, uhci, 它们各自的寄存器接口协议不同,嵌入式设备多为ehci。
该驱动的结构体类型为struct hc_driver,其中的成员(*urb_enqueue)最为重要,它是主控制器hcd将数据包urb传向usb设备的核心实现函数,之前已经提到,协议层里最主要的函数usb_control_msg()最终就会回调主控制器的(*urb_enqueue)。
usb_control_msg() => usb_internal_control_msg() => usb_start_wait_urb() => usb_submit_urb() => usb_hcd_submit_urb => hcd->driver->urb_enqueue()
5. usb gadget
gadget源码位于./drivers/usb/gadget/下,涉及的驱动程序和数据结构相对较多。
驱动主要有,
·平台相关的gadget控制器驱动
·平台无关的复用设备驱动composite.c
·android平台的复用设备驱动android.c
·adb驱动f_adb.c,u盘驱动f_mass_storage.c等一些复用的usb驱动
数据结构主要有,
·struct usb_gadget 里面主要有(*ops)和struct usb_ep *ep0。
·struct usb_gadget_driver 其中的(*bind)绑定复用设备驱动,(*setup)完成usb枚举操作。
·struct usb_compostie_driver 其中的(*bind)绑定比如android复用设备驱动。
·struct usb_request usb数据请求包,类似urb。
·struct usb_configuration 就是这个gadget设备具有的配置,其中的struct usb_function *interface[]数组记录着它所拥有的usb接口/功能/驱动。
·struct usb_function 其中的(*bind)绑定相关的usb接口,(*setup)完成usb枚举操作。
整体框架可概括为,(mv_gadget为gadget控制器的数据)
6. usb mass storage
全世界只有一个linux u盘驱动,位于./drivers/usb/storage/usb.c,伪代码如下,这里需要注意的是,在进行u盘驱动的初始化probe之前,usb core和hub已经对这个u盘做了两大工作,即
1) 完成了usb设备的枚举,此时u盘已经进入configured状态,u盘数据存放在struct usb_interface。
2) 完成了usb总线上设备和驱动的匹配,这时总线上已经找到了接口对应的驱动即u盘驱动。
·土黄色部分由scsi子系统封装实现最终的u盘驱动注册。
·usb_stor_scan_thread 扫描u盘的线程,等待5秒,如果5秒内不拔出就由scsi进行全盘扫描,
·usb_stor_contro_thread 一个核心的线程,具体参看《usb那些事》...
Linux中的declare/typeset命令功能简介
5G的短板是什么,它将面临着什么问题
红米 Note 4X评测:性能强大、漂亮、时尚、与众不同
浙江联想电脑维修网点及联系电话
动力电池热失控的原因是什么?动力电池4类热失控原因详细分析
USB在Linux里的结构框架是什么样的?USB Core和Hub是什么?
汽车大灯智能化趋势,国产车规芯片打入汽车供应链的“敲门砖”
从头到尾讲述开关电源软开关技术
硕橙科技完成数千万A轮融资
提升车载镜头感知能力 虹软VisDrive加速汽车智能化进程
高压功率放大器在高校实验室的实际应用领域介绍
最新研发的装置可提高锂金属电池的性能
VR领域的四大瓶颈是什么,有哪些困难需要解决
如何选择优秀的车用功放?
印度财政部长曾因手机4G信号差爬上树打电话
TI推出最新马达驱动器评估平台 可支援有刷直流与步进马达
在配置外部接口的过程当中,需要考虑的因素?
绕线型异步电动机的原理
智利法院已下令国有银行和智利银行重新为交易所开立账户
干货总结:室内定位技术的3大常用算法