2. Linux USB Gadget框架

Linux USB Gadget框架是嵌入式设备实现USB外设功能的核心软件层。它位于内核中,向上为驱动开发者提供统一的编程接口,向下通过UDC(USB Device Controller)驱动与硬件交互。理解其软件架构与核心数据结构,是进行Gadget驱动移植适配的基础。

2.1 软件架构分层

USB Gadget框架采用分层设计,从硬件到用户空间共分为四层,各层职责清晰,便于移植与扩展。

架构核心原则: 上层功能驱动(如Ethernet、Mass Storage)不直接操作硬件寄存器,而是通过Gadget API与UDC驱动交互,从而实现硬件无关性。
USB Gadget 软件架构分层图 USB Gadget 软件架构分层 用户空间 (User Space) 应用程序 / 配置工具 (如 configfs, gadgetfs) Gadget 功能驱动 (Function Drivers) Ethernet (g_ether) | Mass Storage (g_mass_storage) | Serial (g_serial) | ACM | RNDIS Gadget API 层 (usb_gadget_probe_driver) 核心数据结构: usb_gadget, usb_ep, usb_request UDC 驱动层 (UDC Driver) 硬件相关: 中断处理、DMA、寄存器操作 (如 dwc2, chipidea, musb) 硬件层 (USB Device Controller)

各层之间的交互流程如下:

  1. 用户空间通过 configfs 或 gadgetfs 配置Gadget功能。
  2. 功能驱动注册到Gadget API,绑定端点并处理数据传输。
  3. Gadget API 作为中间层,管理端点生命周期和请求队列。
  4. UDC驱动 实现具体的硬件操作,响应中断并完成数据传输。

2.2 核心数据结构

Gadget框架的核心抽象围绕三个数据结构展开:usb_gadget、usb_ep 和 usb_request。它们分别代表USB设备控制器、端点和数据传输请求。

2.2.1 usb_gadget:设备控制器抽象

usb_gadget 结构体代表一个USB设备控制器实例,是Gadget驱动与UDC硬件之间的桥梁。它包含了控制器的能力描述、状态信息和操作函数指针。

// include/linux/usb/gadget.h
struct usb_gadget {
    struct usb_gadget_driver  *driver;       // 当前绑定的功能驱动
    struct usb_ep             *ep0;          // 控制端点0
    struct list_head           ep_list;      // 所有可用端点的链表
    enum usb_device_speed      speed;        // 当前连接速度 (USB_SPEED_HIGH等)
    unsigned                   is_otg:1;     // 是否支持OTG
    unsigned                   is_a_peripheral:1; // 是否作为A设备
    unsigned                   b_hnp_enable:1;    // HNP使能
    unsigned                   a_hnp_support:1;   // A设备HNP支持
    unsigned                   a_alt_hnp_support:1;
    const struct usb_gadget_ops *ops;        // 硬件操作函数集
    struct device             *dev;          // 内核设备模型
    // ... 其他成员
};
移植要点: 在移植UDC驱动时,需要实现 struct usb_gadget_ops 中的回调函数,如 udc_start()、udc_stop()、pullup() 等。这些函数直接操作硬件寄存器。

2.2.2 usb_ep:端点抽象

usb_ep 结构体抽象了USB协议中的端点概念。每个端点都有方向、类型、最大包长等属性。Gadget功能驱动通过 usb_ep 来配置和传输数据。

// include/linux/usb/gadget.h
struct usb_ep {
    void                      *driver_data;  // 功能驱动的私有数据
    const char                *name;         // 端点名称 (如 "ep1in-bulk")
    const struct usb_ep_ops   *ops;          // 端点操作函数集
    struct list_head           ep_list;      // 链表节点
    unsigned                   maxpacket:16; // 最大包长
    unsigned                   max_streams:16;
    unsigned                   mult:2;
    unsigned                   maxburst:5;
    u8                         address;      // 端点地址 (方向+编号)
    struct usb_endpoint_descriptor *desc;    // 端点描述符
};
usb_ep 端点属性结构图 usb_ep 端点属性结构 usb_ep name: "ep1in-bulk" maxpacket: 512 address: 0x81 (IN) ops: usb_ep_ops desc: descriptor driver_data: priv 端点类型由 desc->bmAttributes 决定: 0x02=Bulk, 0x03=Interrupt, 0x01=Isochronous
常见移植陷阱: 某些UDC硬件不支持所有端点类型(如Isochronous)。在 ep_ops->enable() 回调中,必须检查端点描述符的 bmAttributes 字段,若不支持则返回 -EINVAL。

2.2.3 usb_request:数据传输请求

usb_request 是数据传输的基本单元。功能驱动通过分配并提交 usb_request 到端点,来发起一次数据收发操作。UDC驱动在完成传输后,会调用完成回调函数通知上层。

// include/linux/usb/gadget.h
struct usb_request {
    void                      *buf;          // 数据缓冲区
    unsigned                   length;       // 缓冲区长度
    dma_addr_t                 dma;          // DMA地址 (若使用DMA)
    unsigned                   no_interrupt:1; // 完成时不触发中断
    unsigned                   zero:1;       // 短包时发送ZLP
    unsigned                   short_not_ok:1; // 不允许短包
    void                      (*complete)(struct usb_ep *ep,
                                          struct usb_request *req); // 完成回调
    void                      *context;      // 回调上下文
    struct list_head           list;         // 链表节点
    int                        status;       // 传输状态 (0成功, 负值错误码)
    unsigned                   actual;       // 实际传输字节数
};

2.3 核心数据结构关系

三个核心数据结构在运行时形成明确的层级关系:一个 usb_gadget 包含多个 usb_ep,每个 usb_ep 可以挂载多个 usb_request 请求。

核心数据结构关系图 usb_gadget / usb_ep / usb_request 关系 usb_gadget ep0: 控制端点 ep_list: 端点链表 ep0 (控制端点) ep_list (端点链表) ep1 (Bulk IN) ep2 (Bulk OUT) ep3 (Interrupt) usb_request 队列 usb_request (完成回调) 每个端点维护一个请求队列,UDC驱动按顺序处理并调用 complete 回调

2.4 关键操作函数集

每个核心数据结构都关联一个操作函数集(ops),这些函数是驱动移植时需要重点实现的接口。

数据结构 操作函数集 关键回调 说明
usb_gadget usb_gadget_ops udc_start, udc_stop, pullup, get_frame 控制UDC的启动、停止、D+上拉和帧号获取
usb_ep usb_ep_ops enable, disable, alloc_request, free_request, queue, dequeue 管理端点的使能、禁用和请求队列
usb_request 无独立ops complete (回调函数指针) 传输完成后的通知机制,由功能驱动实现
移植优先级: 在适配新UDC硬件时,首先实现 usb_gadget_ops 中的 udc_start 和 pullup,然后实现 usb_ep_ops 中的 enable 和 queue。这两个函数是数据传输的基础。

2.5 数据传输流程

一次典型的USB数据传输流程如下:

  1. 端点使能: 功能驱动调用 usb_ep_ops->enable(),配置端点类型和最大包长。
  2. 请求分配: 调用 usb_ep_ops->alloc_request() 分配 usb_request 结构体。
  3. 请求提交: 填充 buf、length、complete 后,调用 usb_ep_ops->queue() 将请求加入硬件队列。
  4. 硬件传输: UDC硬件自动处理数据收发,完成后触发中断。
  5. 完成回调: UDC驱动在中断处理中调用 usb_request->complete(),通知功能驱动传输结束。
// 典型的数据发送流程 (伪代码)
static void my_function_send(struct usb_ep *ep, const char *data, int len)
{
    struct usb_request *req;

    // 1. 分配请求
    req = usb_ep_alloc_request(ep, GFP_ATOMIC);
    if (!req)
        return;

    // 2. 填充请求
    req->buf = kmemdup(data, len, GFP_ATOMIC);
    req->length = len;
    req->complete = my_complete_callback;
    req->context = my_priv_data;

    // 3. 提交请求到端点
    ret = usb_ep_queue(ep, req, GFP_ATOMIC);
    if (ret) {
        usb_ep_free_request(ep, req);
        kfree(req->buf);
    }
}

// 4. 完成回调
static void my_complete_callback(struct usb_ep *ep, struct usb_request *req)
{
    if (req->status == 0) {
        // 传输成功,处理 req->actual 实际字节数
        pr_info("Sent %d bytes successfully\n", req->actual);
    } else {
        // 传输失败,处理错误
        pr_err("Transfer failed: %d\n", req->status);
    }
    kfree(req->buf);
    usb_ep_free_request(ep, req);
}
性能优化提示: 在高速传输场景(如USB Video Class)中,建议使用DMA缓冲区。通过 usb_ep_alloc_request() 分配请求后,设置 req->dma 字段并调用 usb_gadget_map_request() 进行DMA映射,可显著降低CPU占用。

2.6 本章小结