通用串行总线(USB)是主机和外围设备之间的一种连接。
最新的USB规范修订增加了理论上高达480Mbps的高速连接。
从拓扑上来看,一个USB子系统是一棵由几个点对点的连接构建而成的树。
这些连接是连接设备和集线器(hub)的四线电缆(底线、电源、两根信号线)。
USB主控制器(host controller)负责询问每一个USB设备是否有数据需要发送。
USB总线是一个单主方式的实现,即主机轮询各种不同的外围设备。
USB总线的特性:
*具有固定的数据传输带宽的能力
*只担当设备和主控制器之间通信通道的角色,对发送数据的结构没有要求
USB协议规范定义了一套任何特定类型的设备都可以遵循的标准。
如果一个设备遵循该标准,就不需要一个特殊的驱动程序。
这些不同的特定类型称为类(class),
包括存储设备、键盘、鼠标、游戏杆、网络设备和调制解调器。
对于不符合这些类的其他类型的设备,需要针对特定的设备编写一个特定的驱动程序。
如视频设备。
Linux内核支持两种类型的USB驱动程序:
宿主(host)系统上的驱动程序和设备(device)上的驱动程序。
宿主系统的USB驱动程序控制插入其中的USB设备,
而USB设备的驱动程序控制该设备如何作为一个USB设备和主机通信。
用 户
|
块设备层
|
USB设备驱动程序
|
USB核心
|
USB主控制器
|
硬 件
USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,
而不必考虑系统当前存在的各种不同类型的USB硬件控制器。
----------------------------------
USB设备基础
本章描述驱动程序和USB核心之间的接口。
USB驱动程序
|
端点 端点 端点
|
接 口
|
配 置
|
设 备
---------
端点
USB通信最基本的形式是通过一个名为端点(endpoint)的东西,
包括:
*输出端点
*输入端点
端点可以看作单向的管道。
USB端点有4种不同的类型,其传送数据的方式不同:
*控制
*中断
*批量
*等时
内核中使用struct usb_host_endpoint结构体来描述USB端点。
该结构体在另一个名为struct usb_endpoint_descriptor
的结构体中包含了真正的端点信息。
后一个结构体包含了所有的USB特定的数据,
这些数据的格式是由设备自己定义的。
该结构体中驱动程序需要关心的字段有:
bEndpointAddress
特定端点的USB地址。这个8位的值中还包含了端点的方向。
结合位掩码USB_DIR_OUT和USB_DIR_IN来使用,以确定端点的数据
是传向设备还是主机。
bmAttributes
端点的类型。该值结合位掩码USB_ENDPOINT_XFERTYPE_MASK来使用,
以确定此端点的类型是USB_ENDPOINT_XFER_ISOC、USB_ENDPOINT_XFER_BULK
还是USB_ENDPOINT_XFER_INT。分别表示等时、批量和中断端点。
wMaxPacketSize
该端点一次可以处理的最大字节数。
bInterval
如果端点是中断类型,该值是端点的间隔设置。
--------
接口
USB端点被捆绑为接口。
USB接口只处理一种USB逻辑连接。
一些USB设备具有多个接口,例如
USB扬声器可以包括两个接口:一个USB键盘接口和一个USB音频流接口。
一个USB接口代表了一个基本功能,每个USB驱动程序控制一个接口。
以扬声器为例,Linux需要两个不同的驱动程序来处理一个硬件设备。
内核使用struct usb_interface结构体来描述USB接口。
USB核心把该结构体传递给USB驱动程序,
之后由USB驱动程序来负责控制该结构体。
该结构体的重要字段有:
struct usb_host_interface *altsetting
一个接口结构体数组,包含了所有可能用于该接口的可选设置。
unsigned num_altsetting
altsetting指针所指的可选设置的数量。
struct usb_host_interface *cur_altsetting
指向altsetting数组内部的指针,表示该接口的当前活动设置。
int minor
如果捆绑到该接口的USB驱动程序使用USB主设备号,
这个变量包含USB核心分配给该接口的次设备号。
------------
配置
USB接口本身被捆绑为配置。
一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。
Linux使用struct usb_host_config结构体来描述USB配置,
使用struct usb_device结构体来描述整个USB设备。
USB设备的逻辑单元之间的关系描述如下:
* 设备通常具有一个或者更多的配置
* 配置经常具有一个或者更多的接口
* 接口通常具有一个或者更多的设置
* 接口没有或者具有一个以上的端点
-------------------
USB和Sysfs
USB sysfs设备命名方案为:
根集线器-集线器端口号:配置.接口
如:
2-1:1.0
如果有更多的USB集线器的使用,则设备名类似于:
根集线器-集线器端口号-集线器端口号:配置.接口
--------------------
USB urb
USB请求块
通过urb来和所有的USB设备通信。
这个请求块使用struct urb结构体来描述 <linux/usb.h>
设备中的每个端点都可以处理一个urb队列。
一个urb的典型生命周期:
* 由USB设备驱动程序创建
* 分配给一个特定USB设备的特定端点
* 由USB核心递交到特定设备的特定USB主控制器驱动程序
* 由USB主控制器驱动程序处理,它从设备进行USB传送
* 当urb结束之后,USB主控制器驱动程序通知USB设备驱动程序。
----------------------
struct urb
struct urb结构体中USB设备驱动程序关心的字段:
struct usb_device *dev
urb所发送的目标struct usb_device指针。
unsigned int pipe
urb所要发送的特定目标struct usb_device的端点信息。
有一系列函数可以用来设置端点,如usb_sndctrlpipe(),
可以把指定USB设备的指定端点号设置为一个控制OUT端点。
unsingned int transfer_flags
该变量可以被设置为许多不同的位值,
取决于USB驱动程序对urb的具体操作。
void *transfer_buffer
指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)
的缓冲区的指针。
dma_addr_t transfer_dma
用于以DMA方式传输数据到USB设备的缓冲区。
int transfer_buffer_length
transfer_buffer或者transfer_dma变量所指向的缓冲区的大小。
unsigned char *setup_packet
指向控制urb的设置数据包的指针。
dma_addr_t setup_dma
控制urb用于设置数据包的DMA缓冲区。
usb_complete_t complete
指向一个结束处理例程的指针。
void *context
指向一个可以被USB驱动程序设置的数据块。
...
-----------------
创建和销毁urb
struct urb *usb_alloc_urb(int iso_packets,int mem_flags);
void usb_free_urb(struct urb *urb);
----------------
中断urb
批量urb
控制urb
等时urb
----------------
提交urb
一旦urb被USB驱动程序正确地创建和初始化之后,
就可以提交到USB核心以发送到USB设备了。
这是通过调用usb_submit_urb函数来完成的。
int usb_submit_urb(struct urb *urb,int mem_flags);
----------------
结束urb:结束回调处理例程
----------------
取消urb
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
------------------------------------
编写USB驱动程序
驱动程序把驱动程序对象注册到USB子系统中,
稍后再使用制造商和设备标识来判断是否已经安装了硬件。