1、USB基础
USB(Universal Serial Bus),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯。
USB主要优点:
1)支持热插拔
2)携带方便
3)标准统一
4)可以连接多个设备
每条USB总线上最多可以接127个设备
USB主控制器:
负责处理主机与设备之间的电器和协议层的互联,常见的USB主控制器规格有:
1)OHCI:主要是非PC系统上的USB芯片,如ARM
2)UHCI:大多是Intel和Via主板上的USB控制器芯片,都是由USB1.1规格的
3)EHCI:由Intel等几个厂商研发,兼容OHCI,UHCI,遵循USB2.0规范
每个USB Host控制器都会自带一个USB Hub,称为根(Root)Hub。当USB设备插入到USB Hub或从上面拔出时,都会发出电信号通知系统
USB设备逻辑结构:
在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。设备通常有一个或多个配置,配置通常有一个或多个接口,接口有零或多个端点。
1)配置代表功能的集合(功能组合)
2)接口代表一个基本的功能,是USB设备驱动程序控制的对象,(一个接口需要一个USB驱动程序)一个功能复杂的USB设备可以具有多个接口
例如:
一个USB播放器带有音频、视频功能,还有旋钮和按钮
配置1:音频+旋钮
配置2:音频+视频+按钮
配置3:视频+旋钮
音频接口、视频接口、按钮接口和旋钮接口均需要一个驱动程序
USB设备中唯一可寻址部分是设备端点,它位于USB设备或主机上的一个数据缓冲区,用来存放和发送USB的各种数据。主机和涉笔的通信最终作用于设备上的哥哥端点,它是主机与设备间通信流的一个逻辑终端。
每个USB设备有一个唯一的地址,这个地址是在设备连上主机时,由主机分配的,而设备中的每个端点在设备内存有唯一的端点号,这个端点号时在设计设备时给定的。
每个端点都是一个简单的连接点,或者支持数据流进设备,或者支持数据流出设备,两者不可兼得
基于PnP机制,设备被枚举时,它必须 向主机报告各个端点的特性,包括端点号、通信方向、端点支持的最大包大小、带宽要求等(其中端点支持的最大包大小叫做数据有效负载)。
每个设备必须有端点0,它用于设备枚举和对设备进行一些基本的控制功能。除了端点0,其他端点在设备配置之前不能与主机通信,只有向主机报告这些端点的特性并被确认后才能被激活
USB描述符:
在每一个USB设备内部,都包含一组固定格式的数据,通过这些数据,USB主机就可以获取USB设备的类型、生产厂商等信息,称为USB描述符
标准的USB设备有5种USB描述符:设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符
一个设备只有一个设备描述符,一个设备描述符可以包含多个配置描述符,一个配置描述符可以包含多个接口描述符,一个接口描述符使用几个端点就有几个端点描述符
1)设备描述符----Standard Device Descriptor,长度为18字节
bLength:描述符长度,固定为0x12
bDescriptorType:设备描述符类型,固定为0x01
bcdUSB:USB规范发布号,表示了本设备能适用于哪种协议,如2.0=0200
bDeviceClass:类型代码
bDeviceSubClass:子类型代码
bDeviceProtocol:协议代码
bMaxPacketSize0:端点0最大分组大小
idVendor:供应商ID
idProduct:产品ID(由厂商分配)
bcdDevice:设备出产编码,由厂家自行设置
iManufacturer:厂商描述符字符串索引,索引到对应的字符串描述符。为0则表示没有
iProduct:产品描述符字符串索引
iSerialNumber:设备序列号字符串索引
bNumConfigurations:可能的配置数
2)配置描述符----Standard Configuration Descriptor,长度为8字节
bLength:描述符长度,固定为0x09
bDescriptorType:设备描述符类型,固定为0x02
wTotalLength:返回整个数据的长度,指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小
bNumInterface:配置所支持的接口数,指该配置配备的接口数量,也表示该配置下接口描述符数量
bConfigurationValue:作为Set Configuration的一个参数选择配置值
iConfiguration:用于描述该配置字符串描述符的索引
bmAttribute:供电模式选择,Bit4-0保留,D7:总线供电,D6:自供电, D5:远程唤醒
MaxPower:总线供电的USB设备的最大消耗电流,以2mA为单位
3)接口描述符-----Standard Interface Descriptor,长度为8字节
bLength:描述符长度,固定为0x09
bDescriptorType:接口描述符类型:固定为0x04
bInterfaceNumber:该接口的编号
bAlterfaceSetting:用于为上一次字段选择可供替换的设置
bNumEndpoint:使用的端点数目,端点0除外
bInterfaceClass:类型代码(由USB组织分配)
bInterfaceSubClass:子类型代码(由USB组织分配)
iInterface:字符串描述符的索引
4)端点描述符-----Standard Endpoint Descriptor,长度为7字节
USB传输:
1)等时传输
用来连接对数据的正确性要求不高而对时间极为敏感的外部设备,如麦克风、音箱及电话等。
等时传输以固定的传输速率,连续不断地在主机与USB设备之间传输数据,在传输数据发生错误时,USB并不处理这些错误,而是继续传送新的数据
2)中断传输
传输的数据量小,但是这些数据需要及时处理以达到实时效果,此方式主要用在键盘、鼠标以及游戏手柄等外部设备上。当USB宿主要求设备传输数据时,中断端点会以一个固定的速率传输数据。由CPU主动发起
3)控制传输
用于传输设备控制指令、设备状态查询及确认命令
4)批量传输
该方式用来传输要求正确无误的数据,通常打印机,扫描仪和数码相机以这种方式与主机连接
事务:
一次传输由一个或多个事务(transaction)构成,事务可分为In事务、Out事务,Setup事务
一个事务由一个或多个包(packet)构成,包可分为令牌包(setup)、数据包(data)、握手包(ACK)和特殊包
一个包由多个域构成,域可分为同步域(SYNC)、标识域(PID)、地址域(ADDR)、端点域(ENDP)、帧号域(FRAM)、数据域(DATA)以及校验域(CRC).
设备枚举:
枚举是让HOST认得这个USB设备,并且为该设备准备资源,建立好主机和设备之间的数据传递机制
1)获取设备描述符
2)复位
3)设置地址
4)再次获取设备描述符
5)获取配置描述符
6)获取接口、端点描述符
7)获取字符串描述符
8)选择设备配置
2、Linux usb驱动
主控制器驱动、设备驱动、UDC驱动(设备控制器)、gadget驱动(小模块)
在linux内核中,使用struct usb_driver结构来描述一个USB驱动
struct usb_driver
{
const char *name;
/* 当USB核心发现该驱动能够处理的USB接口时,调用该函数 */
int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
/* 当相应的USB接口被移除时,调用该函数 */
void (*distconnect) (struct usb_interface *intf);
/* USB驱动能够处理的设备列表 */
const struct usb_device_id *id_table;
};
Linux内核提供了宏USB_DEVICE来定义一种USB设备的USB_DEVICE(Vend,prod); //Vend: USB Vendor ID ,prod: USB Product ID
//注册USB驱动
usb_register(struct usb_driver *driver);
Linux内核使用struct usb_device 来描述一个USB设备
struct usb_device
{
int devnum;
enum usb_device_state; //设备状态
enum usb_device_speed speed; //高速,全速,低速
......
struct usb_device_descriptor descriptor; //USB设备描述符,和协议一致
.......
char *product;
....
};
Linux内核使用struct usb_host_config来描述一个USB配置
struct usb_host_config
{
struct usb_config_descriptor desc; //配置描述符
.......
};
一个配置包含多个接口,一个接口包含一个或多个设置
Linux内核使用struct usb_host_endpoint来描述USB端点:
struct usb_host_endpoint
{
struct usb_endpoint_descriptor desc; //端点描述符
struct list_head *usr_list ;
};
3、URB
USB请求块(USB request block,URB),USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构。
URB处理流程:
1)USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交给USB Core
2)USB Core提交该urb到USB主控制器驱动程序
3)USB主控制器驱动程序根据该URB描述的信息来访问USB设备
4)当设备访问结束后,USB主控制器驱动程序通知USB设备驱动程序
//创建URB
struct urb *usb_alloc_urb(int iso_packets,gfp_t mem_flags);
iso_packets:urb所包含的等时数据包的个数
mem_flags: 内存分配标识
//初始化URB
对于中断URB,使用usb_fill_int_urb函数来初始化
对于批量URB,使用usb_fill_bulk_urb函数来初始化
对于控制URB,使用usb_fill_control_urb函数来初始化
等时urb,只能手动初始化urb
//处理URB
urb被提交到USB核心后,USB核心指定USB主控制器驱动程序来处理该urb。在3种情况下,urb会被认为为处理完成。
1)urb被成功发送到设备,并且设备返回正确的确认,urb->status = 0,表示成功
2)发送数据到设备或从设备接受数据时发生错误时,urb->status将记录错误值
3)urb被“取消”,发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb虽已提交,而USB设备被拔出的情况下
Linux 内核使用struct usb_interface 来藐视一个USB接口
struct usb_interface
{
struct usb_host_interface *alsetting; //接口设置数组
struct usb_host_interface * cur_altsetting; //当前设置
unsigned num_altsetting; //设置数
};