Linux USB Gadget 高速通信驱动开发实战
摘要
基于 Allwinner T113 平台,实现自定义 USB Gadget 驱动,通过架构重构和性能优化,实现了 10-30MB/s 的高速数据传输。本文详细介绍了驱动架构设计、并发请求管理、背压控制机制,以及 PC 端 libusb 应用开发,分享实战中的坑点与解决方案。
1. 项目背景
1.1 应用场景
在嵌入式设备与 PC 之间需要建立高速数据通道,典型场景包括: - 固件升级(百兆级别固件快速传输) - 数据采集(传感器数据实时上传) - 调试通信(大量日志输出)
1.2 技术选型
**方案对比**:
方案 | 速度 | 复杂度 | 稳定性 |
USB Serial (TTY) | 1-3MB/s | 低 | 高 |
USB Ethernet (RNDIS) | 5-10MB/s | 中 | 中 |
USB Mass Storage | 20-40MB/s | 高 | 低 |
自定义 Gadget | 10-30MB/s | 高 | 高 |
选择自定义 Gadget 的原因: 1. 完全控制数据流,无协议开销 2. 可根据业务特性优化 3. PC 端 libusb 直接控制,响应迅速
1.3 硬件平台
- **ARM 端**:Allwinner T113(ARM Cortex-A7) - **USB 控制器**:MUSB(Multi-USB)High-Speed Controller - **PC 端**:x86_64,libusb-1.0
2. 驱动架构设计
2.1 核心数据结构
统一的设备上下文是驱动稳定性的关键:
struct custom_gadget_context { /* 设备状态管理 */ enum gadget_state state; struct mutex context_lock; /* 字符设备接口 */ struct miscdevice misc_dev; struct kfifo read_fifo;/* ARM -> PC 数据 */ struct kfifo write_fifo;/* PC -> ARM 数据 */ /* USB 端点 */ struct usb_ep *bulk_in_ep;/* EP1 IN */ struct usb_ep *bulk_out_ep;/* EP2 OUT */ /* 并发请求管理 */ struct usb_request *in_reqs[MAX_CONCURRENT_REQS]; struct usb_request *out_reqs[MAX_CONCURRENT_REQS]; atomic_t active_in_reqs; atomic_t active_out_reqs; /* 背压控制 */ atomic_t out_paused;/* OUT 端点暂停 */ atomic_t in_starved;/* IN 端点饥饿 */ /* 工作队列 */ struct workqueue_struct *workqueue; struct work_struct read_work; struct work_struct write_work; /* 性能统计 */ atomic64_t bytes_read; atomic64_t bytes_written; };
2.2 性能优化参数配置
**激进性能配置**(基于实测调优):
#define FIFO_SIZE(4 * 1024 * 1024)/* 4MB FIFO */ #define REQ_BUFFER_SIZE(128 * 1024)/* 128KB/请求 */ #define MAX_CONCURRENT_REQS12/* 12 并发请求 */ /* FIFO 水位线 */ #define FIFO_HIGH_WATERMARK(FIFO_SIZE * 3 / 4)/* 75% 暂停 */ #define FIFO_LOW_WATERMARK(FIFO_SIZE / 4)/* 25% 恢复 */
**配置理由**: 1. **4MB FIFO**:应对应用层突发写入,避免阻塞 2. **128KB 请求**:减少 USB 传输次数,降低协议开销 3. **12 并发**:充分利用 USB 管道,保持端点忙碌 4. **水位线**:防止数据丢失,实现背压控制
3. 核心机制实现
3.1 并发请求管理
**IN 端点请求批量提交**:
static void gadget_start_in_requests(struct custom_gadget_context *ctx) { int i; struct usb_request *req; /* 批量提交 8 个 IN 请求 */ for (i = 0; i < IN_BATCH_SIZE; i++) { req = ctx->in_reqs[i]; if (!req) continue; /* 从 FIFO 读取数据 */ int actual = kfifo_out(&ctx->read_fifo, req->buf, REQ_BUFFER_SIZE); if (actual > 0) { req->length = actual; usb_ep_queue(ctx->bulk_in_ep, req, GFP_ATOMIC); atomic_inc(&ctx->active_in_reqs); } } }
**关键点**: - 批量提交减少中断频率 - `GFP_ATOMIC` 避免睡眠(中断上下文) - 主动填充避免端点饥饿
3.2 背压控制机制
**防止 FIFO 溢出**:
static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) { struct custom_gadget_context *ctx = req->context; int ret; /* 写入 FIFO */ ret = kfifo_in(&ctx->write_fifo, req->buf, req->actual); if (ret < req->actual) { /* FIFO 满了,暂停 OUT 请求 */ atomic_set(&ctx->out_paused, 1); pr_warn("FIFO full, OUT endpoint paused\n"); } /* 检查水位线,决定是否恢复 */ if (atomic_read(&ctx->out_paused) && kfifo_len(&ctx->write_fifo) < FIFO_LOW_WATERMARK) { atomic_set(&ctx->out_paused, 0); /* 重新提交请求 */ usb_ep_queue(ep, req, GFP_ATOMIC); } else if (!atomic_read(&ctx->out_paused)) { usb_ep_queue(ep, req, GFP_ATOMIC); } /* 唤醒读进程 */ wake_up_interruptible(&ctx->read_queue); }
3.3 工作队列优化
**高优先级工作队列**:
/* 创建高优先级工作队列 */ ctx->workqueue = alloc_workqueue("custom_gadget_wq", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
**写工作处理**:
static void write_work_handler(struct work_struct *work) { struct custom_gadget_context *ctx = container_of(work, ...); int actual; /* 从 FIFO 提取数据 */ while (kfifo_len(&ctx->write_fifo) > 0) { actual = kfifo_out(&ctx->write_fifo, buffer, size); /* 处理数据 */ } }
**高优先级的必要性**: - USB 回调在中断上下文,不能处理大量数据 - 工作队列确保及时处理,避免 FIFO 溢出
4. ARM 端应用开发
4.1 设备初始化
#define CUSTOM_GADGET_DEVICE "/dev/custom_gadget" int init_device(void) { int fd; fd = open(CUSTOM_GADGET_DEVICE, O_RDWR); if (fd < 0) { perror("Failed to open device"); return -1; } return fd; }
4.2 写入数据(ARM -> PC)
int write_data(int fd, const void *data, size_t size) { ssize_t written; written = write(fd, data, size); if (written < 0) { perror("Write failed"); return -1; } return written; }
4.3 性能测试代码
void speed_test_write(int level) { uint8_t *buffer; size_t size = level * 1024 * 1024; /* 10/20/30 MB */ uint64_t start, end; buffer = malloc(size); memset(buffer, 0xAA, size); start = get_time_ns(); write_data(fd, buffer, size); end = get_time_ns(); double speed = size / ((end - start) / 1e9); printf("Write speed: %.2f MB/s\n", speed / 1024 / 1024); }
5. PC 端 libusb 应用
5.1 设备打开
#define USB_VID 0x1d6b/* Linux Foundation */ #define USB_PID 0x0104/* Multifunction Composite Gadget */ #define USB_INTERFACE 0 int open_usb_device(void) { libusb_device_handle *handle; handle = libusb_open_device_with_vid_pid(NULL, USB_VID, USB_PID); if (!handle) { fprintf(stderr, "Device not found\n"); return -1; } /* 声明接口 */ libusb_claim_interface(handle, USB_INTERFACE); return (int)handle; }
5.2 Bulk 传输读取
int read_usb_data(libusb_device_handle *handle, uint8_t *buffer, int size) { int transferred; int ret; ret = libusb_bulk_transfer(handle, 0x81,/* EP1 IN 地址 */ buffer, size, &transferred, 5000); /* 5秒超时 */ if (ret == LIBUSB_ERROR_TIMEOUT) { fprintf(stderr, "Read timeout\n"); return -1; } return transferred; }
5.3 异步传输(高性能场景)
struct libusb_transfer *transfers[8]; /* 异步回调 */ void callback_fn(struct libusb_transfer *transfer) { /* 处理接收到的数据 */ process_data(transfer->buffer, transfer->actual_length); /* 重新提交 */ libusb_submit_transfer(transfer); } /* 提交异步传输 */ for (i = 0; i < 8; i++) { transfers[i] = libusb_alloc_transfer(0); libusb_fill_bulk_transfer(transfers[i], handle, 0x81, buffer, size, callback_fn, NULL, 0); libusb_submit_transfer(transfers[i]); } /* 事件处理 */ while (running) { libusb_handle_events_completed(NULL, NULL); }
6. 内核配置与调试
6.1 禁用冲突驱动
**内核配置修改**:
Device Drivers---> USB support---> [*] USB Gadget Support---> < >USB Gadget Drivers---> < >USB Functions Configfs < >Mass Storage < >Ethernet < >Serial
**为什么必须禁用**: - ConfigFS 会自动创建标准 gadget - 多个 gadget 同时存在会导致 UDC 绑定失败 - 资源冲突(端点编号、描述符)
6.2 手动绑定 UDC
# 加载驱动 insmod custom_gadget.ko # 绑定 UDC echo 4100000.udc-controller > /sys/class/udc/4100000.udc-controller/uevent # 或 echo 4100000.udc-controller > /sys/devices/platform/soc/1c19000.usb/udc/4100000.udc-controller/function
**检查是否绑定成功**:
# 查看 USB 状态 cat /sys/class/udc/4100000.udc-controller/state # 查看 gadget 状态 ls -la /sys/class/udc/4100000.udc-controller/device/gadget/
7. 性能调优实战
7.1 性能瓶颈分析
**初始性能**:3-5MB/s **瓶颈识别**: 1. 单请求模式(端点经常空闲) 2. FIFO 太小(1MB,频繁阻塞) 3. 缺少背压控制(数据丢失)
7.2 优化措施
措施 | 性能提升 | 说明 |
12 并发请求 | 5 -> 12 MB/s | 端点利用率提升 |
128KB 缓冲区 | 12 -> 18 MB/s | 减少传输次数 |
4MB FIFO | 18 -> 25 MB/s | 减少阻塞 |
水位线控制 | 稳定性提升 | 无数据丢失 |
工作队列 | 延迟降低 50% | 及时处理数据 |
7.3 最终性能
方向 | 目标速度 | 实测速度 | 稳定性 |
ARM -> PC | 30MB/s | 28MB/s | 稳定 |
PC -> ARM | 20MB/s | 19MB/s | 稳定 |
**未达到 30MB/s 的原因**: - USB 2.0 理论带宽 480Mbps = 60MB/s - 协议开销约 20%(SOF、握手、CRC) - 实际可用 ~40MB/s - 单方向达到 28MB/s 已接近极限
8. 踩坑与解决
8.1 坑 1:请求内存泄漏
**现象**:
kmemleak: 48 suspected memory leaks
**原因**: - USB 请求未正确释放 - 设备断开时未清理资源 **解决**:
static void free_all_requests(struct custom_gadget_context *ctx) { int i; for (i = 0; i < MAX_CONCURRENT_REQS; i++) { if (ctx->in_reqs[i]) { usb_ep_free_request(ctx->bulk_in_ep, ctx->in_reqs[i]); ctx->in_reqs[i] = NULL; } if (ctx->out_reqs[i]) { usb_ep_free_request(ctx->bulk_out_ep, ctx->out_reqs[i]); ctx->out_reqs[i] = NULL; } } }
8.2 坑 2:竞态条件
**现象**: - 随机崩溃(dmesg 有 Oops) - 偶发数据损坏 **原因**:
/* 错误:无锁保护 */ if (kfifo_len(&ctx->read_fifo) > 0) { /* 此时可能被中断修改 */ kfifo_out(&ctx->read_fifo, ...); }
**解决**:
/* 正确:使用上下文锁 */ mutex_lock(&ctx->context_lock); if (kfifo_len(&ctx->read_fifo) > 0) { kfifo_out(&ctx->read_fifo, ...); } mutex_unlock(&ctx->context_lock);
8.3 坑 3:端点饥饿
**现象**: - IN 端点经常无数据可发 - 性能波动大(5-20MB/s) **原因**: - 仅在收到新数据时提交请求 - 提交不及时导致端点空闲 **解决**:
/* 主动填充机制 */ static void keep_in_busy(struct usb_ep *ep) { if (list_empty(&ep->queue)) { /* 端点空闲,立即提交 */ gadget_start_in_requests(ctx); } }
9. 验证与测试
9.1 功能验证
# ARM 端 ./simple_arm write 1048576# 写入 1MB ./simple_arm read 65536# 读取 64KB # PC 端 ./simple_pc read 1048576# 读取 1MB ./simple_pc write 65536# 写入 64KB
9.2 性能测试
**ARM 写数据,PC 读取(测试 IN 端点)**:
# ARM 端 ./simple_arm speed_test 30mb write # PC 端(同时运行) ./simple_pc speed_test 30mb read
**预期结果**: - 速度稳定在 25-28MB/s - CPU 占用 < 30% - 无错误计数
9.3 压力测试
# 长时间运行(1小时) ./simple_arm speed_test 20mb write & ./simple_pc speed_test 20mb read & # 检查错误 cat /sys/class/misc/custom_gadget/errors cat /proc/meminfo | grep Slab
10. 总结与展望
10.1 关键收获
1. **并发请求**是高速传输的核心 2. **背压控制**确保数据不丢失 3. **工作队列**平衡实时性与性能 4. **资源清理**容易被忽视但至关重要
10.2 适用场景
- ✅ 需要 10MB/s 以上的高速传输 - ✅ 自定义协议,减少协议开销 - ✅ PC 端有 libusb 部署能力 - ❌ 仅需低速传输(建议 USB Serial) - ❌ 需要标准协议支持(建议 RNDIS/Ethernet)
10.3 下一步优化
1. 支持 USB 3.0(SuperSpeed,理论 5Gbps) 2. 零拷贝优化(DMA 直接传输) 3. 动态调整并发数(自适应负载)
__________________________________________________
**参考资料**: - Linux USB Gadget API:Documentation/usb/gadget.rst - MUSB 驱动文档:drivers/usb/musb/ - libusb 官方文档:https://libusb.info/ **完整代码**: - 驱动:custom_gadget_refactored.c - ARM 应用:simple_arm.c - PC 应用:simple_pc.c