目录
一、设备类驱动实现
1.1 设备类概述:
USB设备类是根据设备功能和行为对USB设备进行分类的一种方式。USB标准定义了一系列设备类规范,为特定类型的设备提供了统一的接口定义和通信协议,使得操作系统和驱动程序可以按照标准的方式来识别和操作这些设备,无需针对每一种具体型号的设备编写独立的驱动程序。
常见的USB设备类包括:
-
Human Interface Devices (HID):涵盖各种人机交互设备,如键盘、鼠标、游戏手柄、触摸屏、传感器等。HID类设备通常用于输入控制或状态报告,数据传输量小且实时性要求较高。
-
Communication Device Class (CDC):用于通信设备,如调制解调器、ISDN适配器、虚拟串口(如FTDI芯片)、网络适配器等。CDC设备支持多种通信协议,如RS-232、AT命令集等,允许USB设备模拟传统的串行通信接口。
-
Mass Storage Class (MSC):适用于存储设备,如U盘、移动硬盘、读卡器等。MSC设备模拟SCSI命令集,使主机能够像访问本地磁盘一样访问USB存储设备,实现文件的读写操作。
-
USB Audio Class: 专为音频设备设计,包括麦克风、扬声器、声卡、耳机等。USB Audio Class定义了音频流的格式、同步机制、控制接口等,使得操作系统和应用程序可以无缝处理USB音频设备的输入输出。
-
Video Class: 支持摄像头、视频采集卡等视频设备,定义了视频流的格式、控制接口等,便于主机系统进行视频捕获和显示。
每个设备类的应用场景与其功能紧密相关。例如,HID类设备广泛应用于个人计算、工业控制、医疗设备等领域,提供用户与系统的交互手段;CDC类设备常用于设备间通信、嵌入式开发、物联网设备连接;MSC类设备是移动存储和数据交换的主流手段;USB Audio Class和Video Class则在多媒体应用、视频会议、直播等领域发挥重要作用。
1.2 设备描述符解析:
USB设备通过一系列描述符向主机报告其特性和能力。主要描述符包括:
设备描述符: 包含了设备的基本信息,如设备类别、设备供应商ID、产品ID、设备版本、厂商字符串、产品字符串、序列号等。这些信息有助于主机识别设备类型和制造商,并为用户提供友好的设备名称。
配置描述符: 定义了设备在某一特定工作模式下的整体配置。一个设备可能有多个配置,每个配置包含若干接口。配置描述符中包含了配置的总字节数、配置号、配置特性(是否支持远程唤醒等)、电源消耗等信息。
接口描述符: 描述了设备在某个配置下的功能单元。一个接口可能包含多个端点,共同实现某种特定功能(如HID接口、音频接口等)。接口描述符中包含接口编号、接口类别、子类、协议、接口特性、接口字符串索引等。
端点描述符: 描述了数据传输的具体通道。每个端点有唯一的地址,指定其方向(输入或输出)、传输类型(控制、中断、批量、同步)、最大包大小、传输间隔(对于周期性传输)等属性。端点描述符定义了设备如何与主机交换数据。
利用C语言解析这些描述符,通常会定义对应的结构体来匹配描述符的字节布局,然后通过字节流(从设备固件或配置描述符缓冲区中获取)逐个填充这些结构体。解析过程中,会遍历描述符链(通过描述符中的长度和类型字段指示下一个描述符的位置),提取所需信息。例如,对于设备描述符,可以定义如下结构体:
typedef struct {
uint8_t bLength; // 描述符长度
uint8_t bDescriptorType; // 描述符类型(设备描述符为0x01)
uint16_t bcdUSB; // USB版本号
uint8_t bDeviceClass; // 设备类别代码
uint8_t bDeviceSubClass; // 设备子类代码
uint8_t bDeviceProtocol; // 设备协议代码
uint8_t bMaxPacketSize0; // 控制端点的最大包大小
uint16_t idVendor; // 设备供应商ID
uint16_t idProduct; // 设备产品ID
uint16_t bcdDevice; // 设备版本号
uint8_t iManufacturer; // 制造商字符串索引
uint8_t iProduct; // 产品字符串索引
uint8_t iSerialNumber; // 序列号字符串索引
uint8_t bNumConfigurations; // 可配置数
} USB_Device_Descriptor;
然后使用类似如下代码进行解析:
void parse_device_descriptor(uint8_t *descriptor_buffer, USB_Device_Descriptor *device_desc) {
memcpy(device_desc, descriptor_buffer, sizeof(USB_Device_Descriptor));
}
类似地,可以为其他描述符定义结构体并编写解析函数。
1.3 驱动程序架构设计:
基于C语言的USB设备驱动程序通常包含以下几个核心模块:
驱动入口: 这是操作系统识别和加载驱动程序的起点,通常包括模块初始化函数、模块卸载函数等。在Linux环境下,可能是module_init()
和module_exit()
宏定义的函数。
设备初始化: 包括探测新设备、分配设备结构、注册设备到系统、解析设备描述符、设置设备配置等步骤。这部分代码通常在设备插入时被调用,确保驱动程序与设备正确关联并了解设备特性。
中断处理: 对于有中断端点的设备,需要注册中断处理函数。当设备产生中断请求时,该函数会被调用,处理中断事件,如数据到达通知、设备状态变化等。
数据传输管理: 实现控制传输、中断传输、批量传输、同步传输的请求发起、数据接收/发送、传输完成通知等功能。这包括设置传输参数、分配缓冲区、调用USB控制器接口进行数据传输、处理传输完成或错误事件等。
用户空间接口: 如果驱动程序需要直接与用户空间应用程序交互,可能需要提供系统调用接口、设备文件节点、ioctl接口等。用户空间应用程序通过这些接口与驱动程序通信,进行设备控制或数据交换。
1.4 实例分析:
以USB HID类设备驱动为例,展示其C语言驱动程序的实现过程:
与硬件交互: 首先,驱动程序需要与USB控制器硬件交互,通过寄存器操作或硬件抽象层(HAL)接口,设置中断处理函数、初始化控制器、配置端点等。例如,为HID设备的中断端点注册中断服务例程:
static irqreturn_t hid_interrupt(int irq, void *dev_id)
{
struct usb_hid_device *hid_dev = dev_id;
/* 处理中断,读取中断端点数据 */
read_interrupt_endpoint(hid_dev);
return IRQ_HANDLED;
}
/* 初始化阶段 */
int hid_device_init(struct usb_hid_device *hid_dev)
{
...
/* 注册中断处理函数 */
request_irq(hid_dev->irq_num, hid_interrupt, IRQF_SHARED, "hid_interrupt", hid_dev);
...
}
协议处理: HID类设备的数据交互基于HID报告描述符,驱动程序需要解析该描述符以了解设备报告格式。在接收到中断数据后,根据报告描述符解析报告内容,转换为应用程序可理解的数据结构:
void process_hid_report(struct usb_hid_device *hid_dev, uint8_t *report_data)
{
struct hid_report_descriptor *report_desc = hid_dev->report_desc;
...
/* 根据报告ID和类型找到对应的解析函数 */
parse_func = find_parse_function(report_desc, report_id, report_type);
if (parse_func) {
/* 调用解析函数处理报告数据 */
parse_func(hid_dev, report_data);
} else {
/* 未知报告类型,忽略或记录日志 */
}
}
用户空间接口: 为方便用户空间应用程序访问HID设备,可以创建字符设备节点,并实现read()
、write()
、ioctl()
等系统调用接口。例如,为用户提供读取按键事件的接口:
static ssize_t hid_char_device_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
struct usb_hid_device *hid_dev = filp->private_data;
struct input_event event;
int ret;
/* 获取一个按键事件 */
if (!get_next_input_event(hid_dev, &event)) {
/* 将事件复制到用户空间 */
if (copy_to_user(buf, &event, sizeof(event))) {
return -EFAULT;
}
ret = sizeof(event);
} else {
ret = 0; // 无事件可读,返回EOF
}
return ret;
}
类似地,对于其他设备类如USB CDC、USB MSC等,驱动程序