LDD-USB

USB基本概念

USB是主机(host)用来和外围设备进行连接的总线。主机的USB控制器(controller)负责寻味USB设备是否需要发送数据;因此,USB设备在控制器询问之前,无法主动发送数据——USB很容易实现PnP。
下图是USB驱动结构图,在USB控制器之上,还有一个USB core负责接收驱动发送的请求,向控制器发送相应的命令,简化驱动的工作。
下图是USB设备的系统结构图,主要由Configuration,Interface,Endpoint三部分组成。
终端(Endpoint)是进行USB传输最基本的形式,内核数据结构:struct usb_host_endpoint,struct usb_endpoint_descriptor。一个终端只能进行单方向的数据传输——host->device | device->host。终端有四种类型:
  1. CONTROL:用来配置设备,endpoint 0
  2. INTERRUPT:以固定的速率传输少量数据,键盘和鼠标;USB协议要保证传输的带宽
  3. BULK:传输大量的数据,USB保证数据不会丢失,但是不保证带宽
  4. ISOCHRONOUS:传输大量的数据,数据可能丢失,视频或音频设备
接口(Interface)是终端的集合,一个接口处理一种类型的USB连接,例如鼠标,键盘,视频流。每个USB驱动控制一个接口,一个设备可能有多个接口,每个接口有多个设置选项,第一个设置编号0。内核数据机构:struct usb_interface,由USB core传给驱动控制。
配置(Configuration)是接口的集合,一个设备可能有多个配置,通过切换配置来改变设备的状态,每次只能激活一个配置。内核数据结构:struct usb_host_config,USB设备struct usb_device。interface_to_usbdev可以将struct usb_interface转化为struct usb_device。
 
USB和sysfs

LDD中说struct usb_device在路径/sys/devices/pci0000:00/0000:00:09.0/usb2/2-1,鼠标的接口在/sys/devices/pci0000:00/0000:00:09.0/usb2/2-1/2-1:1.0下,我在自己的centos7-1803下没有找到(/sys/devices/pci0000:00/00000:00:14.0/usb3/3-0:1.0),最后的文件夹命名方法如下:root_hub-hub_port:config.interface
LDD还说可以在usbfs——/proc/bus/usb/devices下找到sysfs下的所有信息,还有系统中USB设备的可选配置以及终端的信息,我还是没有找到,但是在/pro/bus/input/devices下可以看到相关的输入设备的信息,包括鼠标。
 
 
USB urbs(USB request block)

内核中的USB代码通过urb和USB设备进行通信,数据结构:struct urb。urb通过异步的方式和USB终端进行通信,和文件系统的异步IO中的kiocb结构体和网络中的struct skbuff类似。一个终端可能有多个urb,一个urb可能被不同的终端使用。urb的使用方法如下:
  1. 由USB设备驱动创建
  2. 分配给一个设备的某个终端
  3. 由设备驱动提交给USB core
  4. 由USB core提交给某个USB host controller
  5. USB主机控制器处理后,进行一次 USB传输
  6. urb完成后,USB主机控制器通知设备驱动
提交urb的驱动可以在任何时候取消urb,或者在设备被移除后,由USB core取消。urb动态创建,内部包含一个引用计数,在最后一个使用者释放后自动销毁。
 
struct urb包含该urb所属的USB设备和终端信息:和四种终端类型相对应,每个urb可以通过对应的函数调用设为8种类型中的一个(每种终端类型包括收发两种类型);传送的标志位;传送的缓冲区;dma缓冲区;完成后调用的函数;当前的状态;实际传送的数据大小。
struct urb必须通过usb_alloc_urb进行动态创建,保证USB core能够正确处理urb的引用计数;释放通过usb_free_urb。
urb创建后需要通过相应的初始化函数进行初始化,isochronous urb需要手动初始化。
初始化完成后,调用usb_submit_urb提交;提交后,无法访问urb结构体的数据,直到complete函数被调用。
urb可能以一下三种方式完成,并且调用complete函数:
  1. urb成功发送到设备,设备正确完成请求:成功向设备发送数据,设备成功返回请求的数据
  2. 传送数据时设备发生错误
  3. urb从USB core移除:驱动通过usb_unlink_urb、usb_kill_urb取消已经提交的urb,或者设备从系统中移除。
两种取消urb的方法种,kill会销毁urb,通常作为设备移除的回调函数;unlink不会等待urb完全停止才返回, This is useful for stopping the urb while in an interrupt handler or when a spinlock is held, as waiting for a urb to fully stop requires the ability for the USB core to put the calling process to sleep。LDD中的原话,不是很理解。调用unlink函数时,urb中的URB_ASYNC_UNLINK标志必须设置。
 
 
struct usb_driver

USB驱动的主要数据结构,包括驱动的拥有者(owner)、名称(name)、可以接受的设备id(id_table)、回调函数proble、disconnect,以及一些不常用的回调函数ioctl、suspend、resume。
之后(init)调用usb_register将struct usb_driver注册到USB core;卸载(exit)驱动时调用usb_deregister将驱动从内核卸载。
回调函数:设备安装后,如果USB core认为该设备可以通过此驱动处理,就会调用probe函数;probe需要根据传递的设备信息判断该设备是否真的适合自己。驱动不能再控制设备时,调用disconnect函数,并进行清理工作。
两个回调函数都处于USB hub kernel thread,所以可以sleep。为了使probe时间最短,推荐在用户打开设备时完成主要工作——USB core通过一个线程进行USB设备的添加和移除。
probe函数需要保存自己所需设备信息到局部变量,作为返回值返回。
如果USB驱动没有用户和设备进行交互的子系统,可以通过USB的major number来使用传统的字符驱动的接口。这样的话,驱动必须在probe函数调用usb_register_dev,在disconnect函数调用usb_deregister_dev。
驱动收到数据时(通常在write函数),使用urb传输数据,可以利用dma进行加速。设置完毕urb将其提交给USB core。core完成urb后,调用complete函数。complete函数在中断上下文中执行,不能进行内存分配(使用GFP_ATOMIC来分配内存)、持有信号量等可能导致程序休眠的操作。
有时候,我们只想通过USB传输很简单的数据,不想一步步的创建、初始化urb,可以通过以下两个函数:
  1. usb_bulk_msg:创建一个USB bulk urb,然后发送给制定的设备,等待其完成后返回
  2. usb_control_msg:创建一个USB control urb,发送给指定设备
两个函数都不能在终端上下文或者持有自选锁时调用,而且调用后无法取消;对应的disconnect函数必须在等待其返回后才能进行卸载操作。
 
一些获取USB数据结构的函数usb_get_decriptor、usb_get_string、usb_string的代码实现位于drivers/usb/core/message.c。
 
 
posted @ 2018-10-31 15:17  glob  阅读(298)  评论(0编辑  收藏  举报