☰
Linux USB Gadget 驱动开发:从 DTS 配置到 Function 注册的 5 个关键步骤
2026/10/26 10:00:39 网站建设 项目流程

Linux USB Gadget 驱动开发实战:从 DTS 配置到功能注册的完整指南

1. 嵌入式 USB 设备开发概述

在嵌入式系统开发中,USB Gadget 驱动扮演着关键角色,它允许设备作为 USB 从设备(如存储设备、串口设备等)与主机通信。与传统的 USB 主机驱动不同,Gadget 驱动需要开发者同时关注硬件控制器配置和功能实现两个层面。

现代 Linux 内核为 USB Gadget 开发提供了两种主要模式:

  • 传统模式(Legacy):需要重新编译内核来配置功能
  • ConfigFS 模式:通过文件系统动态配置,无需重新编译内核

对于嵌入式开发者而言,理解以下核心概念至关重要:

  • UDC (USB Device Controller):硬件控制器,如 DWC2
  • Gadget API:内核提供的端点分配和数据传输接口
  • Function 驱动:实现具体设备功能(如 ACM、MSC)
// 典型 USB Gadget 驱动模块初始化示例 #include <linux/module.h> #include <linux/usb/composite.h> static struct usb_composite_driver my_gadget_driver = { .name = "my_udc_gadget", .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, }; module_usb_composite_driver(my_gadget_driver);

2. 硬件准备与 DTS 配置

2.1 UDC 硬件选型

常见的嵌入式 UDC 控制器包括:

  • DWC2:Synopsys DesignWare 核心,广泛用于 SoC
  • MUSB:TI 的 Dual-Role 控制器
  • UDC 核心:如 fsl-usb2-udc

在选择硬件时需要考虑:

  • 支持的 USB 版本(1.1/2.0/3.0)
  • 端点数量和类型
  • DMA 支持情况
  • 供电模式(自供电/总线供电)

2.2 DTS 配置详解

完整的 DWC2 控制器配置示例:

usbd: usb@10200000 { compatible = "snps,dwc2"; reg = <0x10200000 0x1000>; interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_USBD>, <&ccu CLK_USB_PHY0>; clock-names = "otg"; resets = <&rst RESET_USBD>, <&rst RESET_USBPHY0>; reset-names = "dwc2", "dwc2-ecc"; /* FIFO 大小配置 */ g-rx-fifo-size = <380>; g-np-tx-fifo-size = <600>; g-tx-fifo-size = <8 8>; /* 设备模式配置 */ dr_mode = "peripheral"; status = "okay"; };

关键参数说明:

参数描述推荐值
g-rx-fifo-size接收 FIFO 大小根据端点数量调整
g-np-tx-fifo-size非周期传输 FIFO最大包大小的 2-3 倍
g-tx-fifo-size各端点 FIFO根据实际需求分配
dr_mode工作模式peripheral/otg/host

2.3 硬件初始化流程

UDC 驱动的 probe 函数主要完成以下工作:

  1. 时钟和电源管理初始化
  2. 寄存器映射和中断配置
  3. 端点资源分配
  4. 注册 Gadget 设备到 USB 核心
// 简化的 probe 函数流程 static int dwc2_driver_probe(struct platform_device *pdev) { struct dwc2_hsotg *hsotg; /* 1. 分配和初始化硬件结构体 */ hsotg = devm_kzalloc(&pdev->dev, sizeof(*hsotg), GFP_KERNEL); /* 2. 获取资源(寄存器、中断等) */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hsotg->regs = devm_ioremap_resource(&pdev->dev, res); /* 3. 初始化 Gadget 设备 */ hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.max_speed = USB_SPEED_HIGH; /* 4. 注册 UDC */ ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget); return 0; }

3. ConfigFS 动态配置实战

3.1 ConfigFS 基础配置

通过 ConfigFS 配置 USB Gadget 的典型流程:

# 挂载 configfs mount -t configfs none /sys/kernel/config # 创建 Gadget 实例 cd /sys/kernel/config/usb_gadget mkdir g1 cd g1 # 设置 VID/PID echo 0x1d6b > idVendor echo 0x0104 > idProduct # 配置字符串描述符 mkdir strings/0x409 echo "0123456789" > strings/0x409/serialnumber echo "My Company" > strings/0x409/manufacturer echo "Test Device" > strings/0x409/product

3.2 ACM Function 配置示例

创建 ACM (串口) 功能并绑定到配置:

# 创建 ACM 功能 mkdir functions/acm.usb0 # 创建配置 mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo "CDC ACM Config" > configs/c.1/strings/0x409/configuration # 绑定功能到配置 ln -s functions/acm.usb0 configs/c.1 # 激活 UDC ls /sys/class/udc/ > UDC

3.3 复合设备配置

创建包含多个功能的复合设备:

# 添加 Mass Storage 功能 mkdir functions/mass_storage.usb0 echo /dev/mmcblk0p1 > functions/mass_storage.usb0/lun.0/file # 添加 HID 功能 mkdir functions/hid.usb0 echo 1 > functions/hid.usb0/protocol echo 1 > functions/hid.usb0/subclass echo 8 > functions/hid.usb0/report_length echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc # 绑定所有功能 ln -s functions/mass_storage.usb0 configs/c.1 ln -s functions/hid.usb0 configs/c.1

4. 自定义 Function 驱动开发

4.1 Function 驱动框架

开发自定义 Function 驱动的基本结构:

#include <linux/usb/composite.h> struct myfunc_data { struct usb_function function; struct usb_ep *ep_in; struct usb_ep *ep_out; /* 私有数据 */ }; static struct usb_interface_descriptor myfunc_intf_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, }; static struct usb_endpoint_descriptor myfunc_fs_ep_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(64), }; /* 其他描述符... */ static int myfunc_bind(struct usb_configuration *c, struct usb_function *f) { struct myfunc_data *data = func_to_myfunc(f); struct usb_composite_dev *cdev = c->cdev; /* 分配端点 */ >static void myfunc_complete_in(struct usb_ep *ep, struct usb_request *req) { /* IN 端点传输完成处理 */ if (req->status != 0) printk(KERN_ERR "IN transfer error: %d\n", req->status); kfree(req->buf); usb_ep_free_request(ep, req); } static int myfunc_send_data(struct myfunc_data *data, void *buf, size_t len) { struct usb_request *req; int ret; req = usb_ep_alloc_request(data->ep_in, GFP_KERNEL); req->buf = kmemdup(buf, len, GFP_KERNEL); req->length = len; req->complete = myfunc_complete_in; ret = usb_ep_queue(data->ep_in, req, GFP_KERNEL); if (ret < 0) { kfree(req->buf); usb_ep_free_request(data->ep_in, req); return ret; } return 0; } static void myfunc_complete_out(struct usb_ep *ep, struct usb_request *req) { /* 处理接收到的数据 */ process_received_data(req->buf, req->actual); /* 重新提交请求以接收新数据 */ usb_ep_queue(ep, req, GFP_KERNEL); } static int myfunc_enable_out(struct myfunc_data *data) { struct usb_request *req; int i; /* 预分配多个 OUT 请求 */ for (i = 0; i < 5; i++) { req = usb_ep_alloc_request(data->ep_out, GFP_KERNEL); req->buf = kmalloc(512, GFP_KERNEL); req->length = 512; req->complete = myfunc_complete_out; if (usb_ep_queue(data->ep_out, req, GFP_KERNEL) < 0) { kfree(req->buf); usb_ep_free_request(data->ep_out, req); return -EIO; } } return 0; }

5. 调试与性能优化

5.1 常用调试工具

USB Gadget 开发中的关键调试命令:

命令用途示例
lsusb -v查看 USB 设备详情lsusb -v -d 1d6b:
dmesg查看内核日志dmesg | grep dwc2
cat /sys/kernel/debug/usb/devices查看 USB 设备树需要 root 权限
usbmonUSB 流量监控cat /sys/kernel/debug/usb/usbmon/0u

5.2 性能优化技巧

  1. FIFO 大小优化:

    • 根据实际数据传输需求调整 DTS 中的 FIFO 参数
    • 大块数据传输使用更大的 FIFO
    • 实时数据使用专用周期传输 FIFO
  2. DMA 配置:

    /* 在 probe 函数中启用 DMA */ hsotg->params.dma_enable = 1; hsotg->params.dma_desc_enable = 1;
  3. 端点分配策略:

    • 高带宽端点优先分配
    • 实时端点使用专用 FIFO
    • 批量传输端点共享 FIFO
  4. 电源管理:

    /* 启用远程唤醒 */ gadget->remote_wakeup = 1;

5.3 常见问题排查

问题1:设备无法被主机识别

  • 检查 DTS 配置是否正确
  • 验证 UDC 驱动是否成功加载
  • 使用示波器检查 USB 信号质量

问题2:数据传输不稳定

  • 检查 FIFO 大小是否足够
  • 验证 DMA 配置是否正确
  • 调整端点最大包大小

问题3:高负载下丢包

  • 增加预分配的 USB 请求数量
  • 优化中断处理延迟
  • 考虑使用 ISO 端点进行实时传输

在实际项目中,我们曾遇到一个案例:当同时启用 MSC 和 HID 功能时,HID 输入会出现延迟。通过分析发现是 MSC 的大块传输占用了过多带宽,解决方案是为 HID 端点分配专用 FIFO 并提高其优先级。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询