找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

892

积分

0

好友

118

主题
发表于 5 天前 | 查看: 9| 回复: 0

USB(通用串行总线)是一种高度结构化与复杂的总线系统,相较于I2C、SPI等简单的串行协议,它具备独立的事务处理协议、电源管理、复杂的设备枚举机制以及四种不同的数据传输模式。在Linux系统中,USB驱动的实现基于一个庞大且高度抽象的子系统,严格遵循“主机-设备-驱动”的模型架构。

Linux内核中的USB驱动体系被清晰地划分为以下几个层次:

  • USB主机控制器驱动:位于最底层,直接操作SoC上的USB主机控制器硬件(如EHCI、OHCI、XHCI),负责最底层的信号控制和物理数据传输。这一层通常由内核官方提供。
  • USB核心:由内核实现,作为中间层,它负责处理USB设备的插拔事件、枚举流程、配置管理和电源管理等核心服务。
  • USB设备驱动:开发者针对特定USB设备(如自定义硬件、打印机、特定键盘)编写的驱动程序。该层无需关心如何发送SOF(帧起始)包等底层细节,只需向核心层声明“需要发送或接收这些数据”。
  • USB Class驱动:这是可选的通用驱动框架,针对某一类标准USB设备(如HID人机接口设备、Mass Storage大容量存储、CDC通信设备类)提供现成的接口。如果你的设备属于这些通用类别,可以直接使用这些框架,而无需从零开始开发。

深入理解USB驱动,必须掌握其面向对象的描述符结构,这是设备自我描述的“身份证”:

  • 设备描述符:描述整个USB设备的基本信息,如USB协议版本、厂商ID(VID)和产品ID(PID)。
  • 配置描述符:描述设备对电源的需求、支持的接口数量等信息。
  • 接口描述符:最重要的描述符!它定义设备提供的具体功能,例如声明“这是一个键盘接口”或“这是一个用于数据传输的接口”。
  • 端点描述符:描述数据传输的通道和方式。端点是USB设备上的数据缓冲区,决定了数据流向(IN或OUT)和传输类型(控制、中断、批量、等时)。

与I2C驱动类似,USB驱动同样通过VID/PID或设备类ID(Class ID)进行匹配,匹配成功后执行probe函数进行初始化。

驱动必须填充struct usb_driver结构体,这是USB驱动的“身份证明”:

struct usb_driver {
    const char *name;
    int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
    void (*disconnect) (struct usb_interface *intf);
    const struct usb_device_id *id_table; // 设备匹配表
    struct device_driver driver;
};

内核通过驱动提供的匹配表(id_table)来查找对应的驱动:

static const struct usb_device_id my_usb_id_table[] = {
    // 匹配特定设备的 VID/PID
    { USB_DEVICE(0x1234, 0x5678) },
    // 或者匹配某一类设备,例如所有HID设备
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
      .bInterfaceClass = USB_CLASS_HID },
    { /* 哨兵,结束标志 */ }
};
MODULE_DEVICE_TABLE(usb, my_usb_id_table);

probe函数是驱动开始工作的核心入口,其主要任务包括:

  1. 获取设备指针:从传入的struct usb_interface中提取struct usb_device指针。
  2. 查找端点:遍历接口的描述符,找到用于数据传输的端点(例如Bulk IN或OUT端点)。
  3. 创建URB:URB(USB请求块)是USB数据传输的核心结构。它封装了数据缓冲区以及传输描述信息(如目标端点、传输类型、回调函数)。你需要为每个传输任务分配并初始化一个URB。
  4. 提交URB:调用usb_submit_urb()将URB提交给USB核心层。当传输完成(无论成功或失败)时,URB中预设的回调函数将被调用,进行数据处理或错误处理

以下是一个简化的驱动probe函数示例,展示了如何分配和提交一个用于批量接收(Bulk IN)数据的URB:

#include <linux/usb.h>

/* URB传输完成回调函数 */
static void my_usb_complete(struct urb *urb)
{
    // 检查传输状态:urb->status == 0 表示成功
    if (urb->status) {
        // 处理传输错误
        return;
    }
    // 数据接收成功,处理 urb->transfer_buffer 中的数据
    // 处理完毕后,通常需要重新提交此URB以接收后续数据包
}

static int my_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct urb *my_urb;
    // 1. 假设我们已通过描述符解析得知批量输入端点地址为0x81
    __u8 bulk_in_endpoint = 0x81;

    // 2. 分配URB和数据缓冲区
    my_urb = usb_alloc_urb(0, GFP_KERNEL);
    char *transfer_buffer = kmalloc(512, GFP_KERNEL);

    // 3. 初始化URB(用于批量传输)
    usb_fill_bulk_urb(my_urb,              // URB指针
                      dev,                 // USB设备
                      usb_rcvbulkpipe(dev, bulk_in_endpoint), // 构建接收管道
                      transfer_buffer,     // 传输缓冲区
                      512,                 // 缓冲区长度
                      my_usb_complete,     // 完成回调函数
                      NULL);               // 传递给回调函数的上下文数据

    // 4. 提交URB,发起异步数据接收
    if (usb_submit_urb(my_urb, GFP_KERNEL)) {
        // 提交失败,进行错误处理
        kfree(transfer_buffer);
        usb_free_urb(my_urb);
        return -EIO;
    }

    // 可以将驱动私有数据存储到接口结构中,便于在disconnect等函数中获取
    // usb_set_intfdata(intf, my_private_data);
    return 0;
}

通过上述流程,USB设备驱动便与内核的USB子系统及网络协议栈协同工作,完成了复杂设备通信的抽象与实现。




上一篇:Java Web应用安全实战:防范SQL注入、XSS、CSRF与越权访问指南
下一篇:Vwifi-dkms无线驱动管理指南:在Kali Linux中解决内核兼容性与无线渗透测试
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 19:06 , Processed in 0.110688 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表