一、USB驱动层次
usb采用树形拓扑结构,可分为主机侧与设备侧,每一条USB总线上只有一个主机控制器,负责协调主机与设备之间的通讯,设备不能主动的向主机发送任何消息,如下图所示
如上图所示,从主机侧视角去看,在linux驱动中,usb驱动处于最上层,主要表现为usb主机侧的功能具体实现(比如U盘,鼠标,usb camer等),其下为usb核心层,主要完成usb驱动管理以及协议处理,再下为usb主机控制器,主要提供usb控制器的API,维护整个usb系统,完成设备的热插拔,总线传输控制等,最下为具体的USB硬件实现。
从从机侧看,linux的usb分为3个层次,udc,gadget api与gadget驱动,udc为直接访问硬件,控制usb与主机之间的底层通讯,上下层提供硬件相关的操作接口,gadget api主要完成对udc层的一些封装,适配于上层的gadget 驱动层的需求,最上层为gadget驱动层,主要完成设备的运用表现(网络打印,usb mass storage)等特性,gadget的存在主要是为了gadget将运用于底层的硬件操作进行隔离,是的gadget驱动层不需要关系udc层的具体实现。
二:usb的枚举过程
还是口头叙述吧,懒得画图了,挺麻烦的 ~~ 哈哈哈 ~~~~~~
当主机检测到usb设备插入后,
1、主机发起读设备参数的请求,设备也设备描述配置符的方式回应。
上庙的设备描述符中,我们主要关心vid,pid,配置数目这三个参数,vid与pid在usb的driver中会进行table的对比,当匹配当table中有相对应的VID与PID参数后,才会执行driver->probe进行参数的相关处理。
2、主机再次发起配置描述请求,从机以配置描述符的协议进行回应、
这里主要关心接口的个数,在同一个配置下,可能存在很多的接口设备,所有这里的配置根据其下的接口描述符的个数进行修改。
3、主机最后发起接口描述符请求,从机回应接口描述符的配置描述、
这里主要关心端点数,接口类型,一个接口下可能会有多个端点,不同的断电对应不同的类型(短点0主要用于usb控制,所以我们的功能定制需要从短点1开始,比如端点1用于hid设备的out端口,端点2用于hid的in端口,端点3用于mass storage的out 端点,断点4用于mass storage的in端点,。。。),
4、最后到具体断点描述符,从机会将当前设备的端点描述符返回给主机、
自此,usb的枚举以及完成,主机会将usb的信息存储到driver->private数据接口,获取到的数据都存储到struct usb_interface中,在probe函数中将相关的信息提出出来,进行usb的core相关即可完成usb的枚举过程。
三、usb主机侧驱动
usb主控制器有三种规格,OHCI(open host controller interface),UHCI(universal host controller interface)和EHCI(enhanced host controller interface)。
嵌入式中常用的是OHCI,在主机侧,用hub_hcd来描述主机相关的相关信息,
usb_hcd主要描述了主机控制器的相关信息,同时usb_driver为控制usb主机的钩子函数(回调函数)。
四、usb从机侧驱动
usb从机可大概分为如下几个设备类、
1、音频设备类
2、通讯设备类
3、HID设备类
4、mass storage类
5、集线器设备类
usb的从机侧使用usb_Driver来描述一个从机usb设备驱动,数据结构如下、
在编写usb从机侧驱动时候,主要完成probe与diconnect函数,
probe函数的被调用如上枚举所言,只要当枚举到的VID与PID与我们驱动中的id_table成员中的相同,才会被执行,usb设备拔出后disconnect函数在设备被拔出时才会被执行。
五、urb
urb(usb request block)为usb输出传输的载体,用于描述usb传输数据包的基本信息,结构体如下、
transfer_flags表示当前usb的状态,只有当该状态处于idle时,数据才能被submit到usb的ENDPOINT的queue中。
5.1 urb的处理流程
5.1.1、分配urb
使用struct urb *usb_alloc_urb(int iso_pockets,int men_flags)来动态的分配一个urb资源。
iso_pockets表示分配的urb个数,为0则表示分配的urb是非实时性的,
usb_free_urb(struct urb *urb) 表示将分配的资源进行释放。
5.1.2、初始化urb
使用void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,void *transfer_buf,int buffer_length,usb_complete complete,void *context,int interval)来初始化中断urb,
urb表示需要初始化的urb的指针,dev表示需要发送这个urb的usb设备,pipe表示urb需要发送到的usb指定的端口(一般情况下使用usb_sndintpipe()或者usb_rcvintpipe()创建),transfer_buf是指向发送数据或者接受数据的缓冲区,buffer_len表示数据长度,complete表示传输完成之后需要执行的处理回调函数,xontext字面上是上下文(咱不理解),interval表示这个urb被调度的间隔。
5.1.3、提交urb
使用int usb_submit_urb(struct urb *urb,int mem_flag)将urb的数据提交到usb的core层,注意,在complete没有没调用之前,不要再次submit urb
建议:关于usb的相关,内核中已经提供了一个很好的usb框架文件在drivers/usb/usb-skeleton.c
举个栗子:见下一篇 usb emulate camera device。
**~~~~~~~~~~~~~~~~~~~~~废话太多了,再见 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~**