最近调查了MUSB的一个问题,将USB这块的框架简单梳理了一下,趁热这次做下记录,能提纲挈领即可
/*****************************************************************/
目录
0,背景
1,USB 子系统结构
2,USB 子系统实现
0,背景
以前刚接触到USB设备-u盘时,我们每次都得手动查找新硬件,然后驱动安装,才能访问,后来慢慢的U盘一插上都能用了,当时很懒,也不去多想其中原理,就觉得真是神奇啊......
以后工作中都和这一类的东西打交道,才发现不光USB,所有其他一些支持即插即拔功能的总线,能发挥这种功效,都是中断(可以算是硬件规范)和一些驱动编程规范起了作用。
硬件规范可以不管任何设备,只要满足一些很简单的接口设计要求,就可以应用在任何场合(剩下的交给驱动了),这也是计算机的loader(可以启动各种操作系统)的设计原理。
没有规矩,不成方圆!
没有规范,没有驱动!!
USB的规范手册就描述了一种总线,这条总线上可以连接很多种设备(mass storage,keyboards,mice,etc.),通过它多种设备可以稳定的和host进行通信。
基于这个规范,Linux内核提供了USB子系统驱动。
需要注意的事,Linux的USB子系统只提供属于预定义的类别的设备的驱动,对于那些例外,需要特定的设备驱动,这里只是点出。
1,USB 子系统结构
回想大学时候数据结构没有学好,当时觉得学这些东西不知道拿来做什么,不能和实际的东西扯上关系,兴趣也就江河日下......这是前话,暂且不表了
这里我们就从描述当一个USB设备插上主机开始,看USB子系统如何工作,来了解它的大致框架:
U盘插上电脑,根据USB总线的硬件规范,会产生一个中断给CPU,
CPU根据中断管理器送过来的中断号调用相关的中断函数,如果是一个共享号,这个中断函数里面会对共享它的相关硬件的寄存器进行check,看是否有中断到来,然后,就呼呼的去完成每个硬件设备要完成的工作了。
所有的硬件都遵循一些规范来设计,他们之间的协同工作就变的那么优美...
譬如人与人/社会之间,如果没有一些规范来约束,就会变得混乱不堪,
USB设备插入调用的中断函数会发出一个event,来交待在USB 总线上来了一个新人,这个event会发给用户空间的udevd(或mdevd,如果你使用busybox的话),udevd会根据event中的一些数据来插入相应的模块,挂载对应这个设备的驱动......
这个过程简单描述如下:
虽然,而且到目前为止,我也一直这样认为,USB总线和USB设备使用软件进行抽象描述起来是非常复杂的,一方面是协议使然,一方面也是因为它们使用太广泛了,抽象时考虑很太多情况。
但是,幸运的是,内核开发者们抽象出来的内核USB 子系统把很多复杂性都隐藏了(一群让我们又爱又恨的人啊 :))。
这样对整个过程趟了一遍,我们就自然而然的对在其中的软件实现思想和层次来兴趣了,这里我们先总览一下:
针对这幅图,为了理解什么是USB子系统,我们要做以下说明:
a) USB 驱动都是夸kernel子系统的,因为最终USB设备是要通过BLCOCK 或CHAR设备的方式呈现给我们的,所以我们从图中看到USB Driver之上还有一层。
b) USB driver利用USB Core提供的API来简单优雅的完成驱动工作,这里USB Core抽象了复杂的USB协议。
c) 主机控制器驱动位于USB软件的最下层,提供主机控制器硬件的抽象,隐藏硬件的细节,在主机控制器之下是物理的USB及所有与之连接的USB设备。主机控制器驱动只和USB Core进行关联,USB Core将用户的请求映射到相关的主机控制器驱动,从而使用户无需去访问主机控制器。
d) USB Core和USB主机控制器驱动就构成了我们的USB子系统,USB Core负责实现一些核心的功能,例如协议之类,提供一个用于访问和控制USB硬件的接口,使设备驱动不用去考虑系统当前使用哪种主机控制器。自从有了USB子系统,写USB驱动的时候,只需要调用USB Core export的接口,就几乎能完成所有工作。
2,USB 子系统实现
2.1 初始化
先罗嗦一下USB设备是如何抽象的:
a) 一个USB会由一个或多个configuration来构成
b) 一个configuration会由一个或多个Interface来构成
c) 一个interface会由一个或多个setting来构成
d) 一个interface会包含0或多个endpoint
USB设备和主机的通信channel被称为endpoint: Control,Interrupt, Bulk,Isochronous
interface其实就是通过endpoint来呈现的。
一个USB 驱动会跟一个USB设备的interface绑定
在USB子系统中,usb_device_id这个数据结构被用来匹配USB设备和相应的驱动。为了使用方便,USB Core会提供一些宏来协助建立这个数据结构。按照约定,这个表会在驱动中export出来(使用MODULE_DEVICE_TABLE宏),在build阶段的时候,这个表会被写入到模块的数据库,被UDEV以后使用。
任何驱动在一开始,都要去注册,在USB子系统中,driver被usb_register函数使用usb_drivr数据结构来进行注册。
当USB Core认为它已经为一个设备的interface发现相应的驱动时,probe函数就会被调用。
probe函数会完成许多重要的工作,例如:为interface的相关数据分配空间,根据interface的特性,注册其他kernel子系统(Block,Char,Net,TTY,etc)
2.2 通信
大部分情况下,USB设备的通信工作在软件层使用USB Request Blocks(URB)的方式来完成。
URB使用异步的方式在endpoint上来发送或接受数据,每个URB被初始化并赋值后会被提交到USB Core,由USB Core来完成后续的工作。
使用URB来进行通信会涉及到许多USB Core API的使用,有时候用户很希望简化使用方式,譬如我这种懒人,可爱的kernel开发者又给我们提供一些接口,我们可以很简单的完成USB endpoint的通信工作而不涉及到URB的操作(其实是将URB的操作封装了)