一,前言
这一篇学习分析USB设备驱动程序,主要涉及到USB驱动基本概念,USB版本对比、USB主从结构、USB设备的传输类型、USB设备描述符关系、USB的数据传输对象;详细分析了USB总线驱动框架,USB Core、USB HCD、USB总线-设备-驱动模型;最后总结了USB驱动开发中的一般流程并模拟实现一个USB设备驱动程序。
二,USB子系统
2.1 USB驱动基础概念
2.1.1 USB版本
USB1.0版本,USB LS(Low Speed低速),速度1.5Mbps。
USB1.1版本,USB FS(Full Speed全速),速度12Mbps。
USB2.0版本,USB HS(High Speed高速),速度480Mbps。
USB3.0版本,USB SS(Super Speed超速),速度5Gbps。也称为 USB 3.1 Gen 1 或 USB 3.2 Gen 1
USB3.1版本,USB SS(Super Speed超速),速度10Gbps。也称为 USB 3.2 Gen 2
USB 3.2 是对 USB 3.0 和 USB 3.1 标准的重新命名
USB3.2 版本,USB SS(Super Speed超速),速度20Gbps。
USB 3.2 Gen 1:也称为 USB 3.0 或 USB 3.1 Gen 1,传输速度为 5Gbps。
USB 3.2 Gen 2:也称为 USB 3.1 Gen 2,传输速度为 10Gbps。
USB 3.2 Gen 2x2:这是最新的版本,传输速度可达 20Gbp
2.1.2 USB主从结构
USB分为USB 主机(USB Host)和USB device(USB设备)。比如USB鼠标插入到电脑,电脑就是USB Host,鼠标就是USB device。所有的USB传输,都是从USB主机这方发起的;USB设备没有"主动"通知USB主机的能力。
2.1.3 USB的传输类型
在 USB 通信中,控制传输(Control Transfer)是一个非常常见且重要的传输类型,主要用于配置设备、查询设备状态以及发送和接收命令。控制传输有三种主要形式:控制写传输(Control Write Transfer)、控制读传输(Control Read Transfer)和无数据控制传输(No Data Control Transfer)。这些传输类型在不同的场景下使用,具体如下:
-
控制写传输 (Control Write Transfer)
场景:当主机(Host)需要向设备(Device)发送配置信息或命令时使用控制写传输。例如:
向设备发送设置命令或配置参数,例如设置音频设备的音量。
向设备发送特定的请求来改变设备的状态,例如设置设备的工作模式。
示例:主机通过控制写传输向设备发送一组特定的数据,如设置 LED 灯的颜色或改变设备的通信速率。 -
控制读传输 (Control Read Transfer)
场景:当主机需要从设备获取信息时使用控制读传输。这通常用于查询设备状态或获取设备描述符等信息。例如:
主机请求设备返回设备描述符,以获取设备的基本信息(如厂商 ID、产品 ID 等)。
主机请求设备的状态或配置信息,例如查询设备当前的电池电量。
示例:主机通过控制读传输从设备读取某个传感器的当前读数或获取设备的固件版本信息。 -
无数据控制传输 (No Data Control Transfer)
场景:当主机仅发送一个控制请求,但不需要传输任何数据时使用无数据控制传输。这种情况常见于简单的命令或状态请求中。例如:
主机请求设备执行某个操作,但不需要传输额外的数据或接收设备的响应数据。
用于简单的请求,如设备复位、设备清除特定状态或发出一些简单的指令。
示例:主机发送一个复位请求给设备,然后设备复位后进入默认状态。
这三种控制传输类型通常用于 USB 设备的初始化、配置和状态管理,是确保 USB 设备和主机之间能够正确通信的基础。
2.1.4 USB设备描述符
我们第一个学习要点就是USB描述符,所谓描述符其实就是C语言里面的结构体或者数组,数组包含的信息说明当前的设备具有哪些特征。USB描述符有设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符,HID设备有HID描述符、报告描述符和物理描述符。我们先学会每个描述符的细节,我会写的非常详细且简洁,后面在学习USB枚举的时候会通过抓包和波形来学习每一个描述符在总线上的作用,并且会介绍每一个描述符是在什么时候以哪种方式在总线上进行传输的,今天主要是学习USB设备描述符的组成。
设备描述符是USB主机枚举USB设备申请的第1个描述符,每个设备有且仅有一个设备描述符,也就是大家以后看到的任何的USB设备都只有一个设备描述符数组,设备描述符的长度是18字节,下面我们来学习设备描述符数据结构,看看USB设备具有哪些特征,它的结构我们可以通过下面的表格来逐一了解。
2.1.4.1 设备描述符
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
__u8 bLength; //该结构体大小
__u8 bDescriptorType; //描述符类型 (此处应为0x01,即设备描述符)
__le16 bcdUSB; //usb版本号 200 -> USB2.0
__u8 bDeviceClass; //设备类
__u8 bDeviceSubClass; //设备类子类
__u8 bDeviceProtocol; //设备协议,以上三点都是USB官方定义
__u8 bMaxPacketSize0; //端点0最大包大小 (只能为8,16,32,64)
__le16 idVendor; //厂家id
__le16 idProduct; //产品id
__le16 bcdDevice; //设备出厂编号
__u8 iManufacturer; //描述厂商信息的字符串描述符的索引值
__u8 iProduct; //描述产品信息的字串描述符的索引值
__u8 iSerialNumber; //描述设备序列号信息的字串描述符的索引值
__u8 bNumConfigurations; //可能的配置描述符的数目
} __attribute__ ((packed));
举例,设备描述符代码实现如下:
#define USB_DEVICE_DESCRIPTOR_TYPE 0x01 // 设备描述符类别
#define USB_OTG_MAX_EP0_SIZE 64 // 端点0支持的最大包长
#define USBD_VID 0x0483 // 厂商ID
#define USBD_PID 0xDF11 // 产品ID
#define USBD_IDX_MFC_STR 0x01 // 厂商字符串的索引
#define USBD_IDX_PRODUCT_STR 0x02 // 产品字符串的索引
#define USBD_IDX_SERIAL_STR 0x03 // 产品序列号字符串的索引
#define USBD_CFG_MAX_NUM 1 // 设备的配置数
/* USB Standard Device Descriptor */
uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] =
{
0x12, /*bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
USB_OTG_MAX_EP0_SIZE, /*bMaxPacketSize*/
LOBYTE(USBD_VID), /*idVendor low*/
HIBYTE(USBD_VID), /*idVendor high*/
LOBYTE(USBD_PID), /*idVendor low*/
HIBYTE(USBD_PID), /*idVendor high*/
0x00, /*bcdDevice rel. 2.00*/
0x02,
USBD_IDX_MFC_STR, /*Index of manufacturer string*/
USBD_IDX_PRODUCT_STR, /*Index of product string*/
USBD_IDX_SERIAL_STR, /*Index of serial number string*/
USBD_CFG_MAX_NUM /*bNumConfigurations*/
} ; /* USB_DeviceDescriptor */
下面来介绍设备描述符每一个内容的详细含义(所有的描述符都是小端格式,先低后高)
(1)bLength
描述符长度(18字节,十六进制为0x12),就是标志描述符数据结构的长度。
(2)bDescriptorType
bDescriptorType代表了本描述符的类型,设备描述符为0x01。所有的描述符类型表示如下图,大家以后也可以速查:
(3)bcdUSB
USB协议版本,表示形式0xJJMN版本JJ.M.N(JJ - 主要版本号,M - 次要版本号,N - 次要版本)
例子:如果是USB2.0,写成:0200H;如果是USB1.1,写成:0110H 如果是USB3.11,写成:0311H
(4)bDeviceClass、bDeviceSubClass、bDeviceProtocol
bDeviceClass、bDeviceSubClass、bDeviceProtocol分别代表设备类型,子类型,设备使用的协议,USB-IF区分设备类分了三个等级(类-子类-协议码)其中,类包含人机交互类、图像类、无线类、音频类等等,子类比如音频类的音频控制、音频流等等,协议比如人机接口类中的鼠标、键盘、触摸屏等,为何会有这么多USB的Class分类,子分类,设备协议。我们要知道,USB协议设计的目的,就是为实现通用,用单一的USB接口取代之前种类繁多的各种其他接口。而为了取代其他各种接口,那意味着就要实现各种设备所对应的各种功能。如下图显示USB设备的各种类别,USB设备类信息更详细内容可进入https://www.usb.org/defined-class-codes查看。
(5)bMaxPackeSize0
就是端点一次最大传多少个字节。USB协议里有规定,端点0最低8字节,端点的最大传输大小和USB速度等级以及传输类型有关,控制传输一般使用端点0,低速最大8字节,全速和高速最大传输64字节,如下图:
(6)idVender
厂商ID,就是个2字节的编号,由USB协议分配,厂商申请时需要交费。自己随便写的话,仅限于学习、测试的情况下,不能用做产品。
(7)idProduct
产品ID,厂家自己随便定义。
(8)bcdDevice
产品版本号,厂家自己随便定义。
(9)iManufacturer
描述厂商的字符串的索引,为0则表示没有,后面详细讲解。
(10)iProduct
描述产品的字符串的索引,为0则表示没有,后面详细讲解。
(11)iSerialNumber
产品序列号字符串的索引,为0则表示没有,后面详细讲解。
(12)bNumConfigurations
指示设备由多少个配置,前面提到过,一个USB可能有多个配置,一般USB产品都是1个配置。
2.1.4.2 配置描述符
struct usb_config_descriptor {
__u8 bLength; //该结构体大小
__u8 bDescriptorType;//描述符类型(本结构体中固定为0x02)
__le16 wTotalLength; //该配置下,信息的总长度(包括配置,接口,端点和设备类及厂商定义的描述符)
__u8 bNumInterfaces; //接口的个数
__u8 bConfigurationValue; //Set_Configuration命令所需要的参数值,用来选定此配置
__u8 iConfiguration; //描述该配置的字符串描述的索引值
__u8 bmAttributes;//供电模式的选择
__u8 bMaxPower;//设备从总线提取的最大电流
} __attribute__ ((packed));
2.1.4.3 接口描述符
配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。
struct usb_interface_descriptor {
__u8 bLength; //该结构体大小
__u8 bDescriptorType;//接口描述符的类型编号(0x04)
__u8 bInterfaceNumber; //该接口的编号
__u8 bAlternateSetting; //备用的接口描述符编号
__u8 bNumEndpoints; //该接口使用的端点数,不包括端点0
__u8 bInterfaceClass; //接口类
__u8 bInterfaceSubClass; //子类
__u8 bInterfaceProtocol; //协议
__u8 iInterface;//描述此接口的字串描述表的索引值
} __attribute__ ((packed));
2.1.4.4 端点描述符
端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。
除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
__u8 bLength; //端点描述符字节数大小(7个字节)
__u8 bDescriptorType;//端点描述符类型编号(0x05)
__u8 bEndpointAddress; //此描述表所描述的端点的地址、方向 :
// bit3~bit0:端点号,bit6~bit4:保留,
// bit7:方向,如果是控制端点则忽略,0-输出端点(主机到设备)1-输入端点(设备到主机)
__u8 bmAttributes; // 端点特性,bit1~bit0 表示传输类型,其他位保留
// 00-控制传输 01-实时传输 10-批量传输 11-中断传输
__le16 wMaxPacketSize; //端点收、发的最大包大小
__u8 bInterval; // 中断传输模式中主机查询端点的时间间隔。
// 对于实时传输的端点此域必需为1,表示周期为1ms。对于中断传输的端点此域值的范围为1ms到255ms
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
2.1.4.5 字符串描述符
struct usb_string_descriptor {
__u8 bLength; // 此描述表的字节数(bString域的数值N+2)
__u8 bDescriptorType; // 字串描述表类型(此处应为0x03)
__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));
2.2 USB子系统框架
如下图所示,Linux内核中USB主机体系结构由五部分组成,分别为Application Software、USB Class Driver、USB Core((USB Driver)、USB Host Controller Driver、USB Host Controller。应用程序处于用户空间,通过系统调用访问Class Driver,从而间接的访问USB设备,如主机端的应用程序aplay、arecord可以访问USB音频设备。Class Driver是某一类设备驱动