1. Linux USB驱动开发概述
作为一名嵌入式Linux开发者,我经常需要处理各种外设驱动开发工作。USB作为现代嵌入式系统中最常用的外设接口之一,其驱动开发是每个Linux驱动工程师必须掌握的技能。在STM32MP1平台上进行USB Host驱动开发,不仅需要理解Linux USB子系统架构,还需要熟悉ARM架构下的硬件特性。
USB驱动开发之所以重要,是因为它涉及到:
- 设备枚举与识别
- 数据传输协议实现
- 电源管理
- 热插拔支持
在STM32MP1平台上,USB控制器分为三种类型:
- USB OTG控制器(支持主从切换)
- USB Host控制器(专用主机控制器)
- USB HS PHY控制器(物理层接口)
2. USB基础理论深入解析
2.1 USB物理层特性
USB 2.0采用4线制设计:
- VBUS(+5V电源)
- D+(数据正)
- D-(数据负)
- GND(地线)
对于USB 3.0及以上版本,新增了5条信号线用于超高速数据传输:
- SSRX+/SSRX-(超高速接收差分对)
- SSTX+/SSTX-(超高速发送差分对)
- 额外的地线
2.2 USB拓扑结构详解
USB采用严格的星型拓扑结构,具有以下特点:
- 单主机架构:一个USB系统中只能有一个主机控制器
- 层次化连接:最多支持7层(包括根集线器)
- 地址分配:最多支持127个设备(地址0保留用于枚举)
在实际应用中,拓扑结构表现为:
code复制[Host Controller]
|
+-- [Root Hub]
|
+-- [Device 1]
|
+-- [Hub 1]
|
+-- [Device 2]
|
+-- [Device 3]
2.3 USB传输模式对比
USB定义了四种传输类型,各有特点:
| 传输类型 | 数据量 | 实时性 | 可靠性 | 典型应用 |
|---|---|---|---|---|
| 控制传输 | 小 | 低 | 高 | 设备枚举、配置 |
| 中断传输 | 小 | 高 | 高 | 键盘、鼠标 |
| 批量传输 | 大 | 低 | 高 | U盘、打印机 |
| 同步传输 | 中 | 极高 | 低 | 摄像头、音频 |
3. STM32MP1 USB硬件架构
3.1 控制器类型与特点
STM32MP1集成了三种USB控制器:
-
USB OTG控制器:
- 支持OTG协议(ID引脚决定主从模式)
- 可配置为Host或Device模式
- 内置PHY,支持全速/高速
-
USB Host控制器:
- 专用主机模式控制器
- 由EHCI(高速)和OHCI(全速)组成
- 需要外接PHY
-
USB HS PHY控制器:
- 提供高速物理层接口
- 可分配给OTG或Host控制器使用
3.2 时钟与电源管理
USB控制器需要特定的时钟配置:
- 48MHz主时钟(来自PLL3)
- SOF时钟(1kHz)
- PHY参考时钟(60MHz for HS)
电源管理方面需要注意:
- VBUS供电控制
- 挂起/恢复机制
- 远程唤醒功能
4. Linux USB驱动框架解析
4.1 主机控制器驱动(HCD)
Linux USB子系统采用分层架构:
code复制[USB设备驱动] (如usb-storage)
|
v
[USB核心层] (usbcore)
|
v
[主机控制器驱动] (ehci-hcd, ohci-hcd等)
|
v
[硬件控制器]
关键数据结构:
struct usb_hcd:表示主机控制器struct urb:USB请求块struct usb_device:表示USB设备
4.2 设备驱动开发流程
开发一个USB设备驱动通常包括以下步骤:
- 定义设备ID表(
usb_device_id) - 实现驱动结构体(
usb_driver) - 注册probe/disconnect回调
- 实现文件操作接口(如需要)
- 处理URB请求
示例驱动框架:
c复制static struct usb_driver my_driver = {
.name = "my_usb_drv",
.probe = my_probe,
.disconnect = my_disconnect,
.id_table = my_id_table,
};
module_usb_driver(my_driver);
5. 设备树配置实战
5.1 USB Host控制器配置
STM32MP1的USB Host控制器设备树节点示例:
dts复制usbh_ohci: usbh-ohci@5800d000 {
compatible = "generic-ohci";
reg = <0x5800d000 0x1000>;
interrupts = <GIC_SPI 75 IRQ_TYPE_NONE>;
clocks = <&rcc USBH>;
resets = <&rcc USBH_R>;
phys = <&usbphyc_port1>;
phy-names = "usb";
status = "okay";
};
usbh_ehci: usbh-ehci@5800c000 {
compatible = "generic-ehci";
reg = <0x5800c000 0x1000>;
interrupts = <GIC_SPI 76 IRQ_TYPE_NONE>;
clocks = <&rcc USBH>;
resets = <&rcc USBH_R>;
phys = <&usbphyc_port1>;
phy-names = "usb";
companion = <&usbh_ohci>;
status = "okay";
};
5.2 PHY控制器配置
USB PHY的配置同样重要:
dts复制usbphyc: usbphyc@5a006000 {
compatible = "st,stm32mp1-usbphyc";
reg = <0x5a006000 0x1000>;
clocks = <&rcc USBPHY_K>;
resets = <&rcc USBPHY_R>;
#address-cells = <1>;
#size-cells = <0>;
usbphyc_port0: usb-phy@0 {
reg = <0>;
phy-supply = <&vdd_usb>;
#phy-cells = <0>;
};
usbphyc_port1: usb-phy@1 {
reg = <1>;
phy-supply = <&vdd_usb>;
#phy-cells = <0>;
};
};
6. USB枚举过程深度剖析
6.1 枚举步骤详解
USB设备枚举是一个标准化的过程:
- 检测连接:Hub检测到端口状态变化
- 端口复位:主机发送复位信号
- 获取描述符:
- 设备描述符
- 配置描述符
- 字符串描述符
- 设置地址:主机分配唯一地址
- 设置配置:激活设备功能
6.2 关键描述符解析
设备描述符示例(十六进制表示):
code复制12 01 00 02 00 00 00 40 83 04 23 05 00 01 01 02 00 01
各字段含义:
- bLength:0x12(18字节)
- bDescriptorType:0x01(设备描述符)
- bcdUSB:0x0200(USB 2.0)
- bDeviceClass:0x00(接口定义类)
- idVendor:0x0483(STMicroelectronics)
- idProduct:0x0523(STM32MP1)
7. USB数据传输实现
7.1 URB(USB Request Block)机制
URB是Linux USB驱动的核心数据结构,代表一个USB传输请求。URB的生命周期:
- 创建:
usb_alloc_urb() - 初始化:设置传输类型、端点等参数
- 提交:
usb_submit_urb() - 完成:回调函数处理结果
- 释放:
usb_free_urb()
7.2 批量传输示例
c复制struct urb *urb;
unsigned char *buf;
/* 分配URB */
urb = usb_alloc_urb(0, GFP_KERNEL);
buf = kmalloc(size, GFP_KERNEL);
/* 填充数据 */
memcpy(buf, data, size);
/* 初始化URB */
usb_fill_bulk_urb(urb, udev,
usb_sndbulkpipe(udev, ep_addr),
buf, size,
bulk_complete, NULL);
/* 提交URB */
ret = usb_submit_urb(urb, GFP_KERNEL);
8. 常见问题与调试技巧
8.1 典型问题排查
-
设备未被识别:
- 检查VBUS供电
- 验证D+/D-线路
- 查看内核日志(dmesg)
-
枚举失败:
- 检查描述符是否正确
- 验证电源配置
- 检查PHY初始化
-
传输错误:
- 检查端点类型匹配
- 验证URB状态
- 检查DMA配置
8.2 实用调试工具
-
lsusb:列出USB设备信息
bash复制
lsusb -v -
usbmon:捕获USB流量
bash复制modprobe usbmon cat /sys/kernel/debug/usb/usbmon/0u -
内核调试选项:
bash复制echo 8 > /proc/sys/kernel/printk dmesg -w
9. 实战案例:USB Host功能实现
9.1 USB鼠标驱动使能
-
配置内核:
bash复制
make menuconfig选择:
code复制Device Drivers -> HID support -> USB HID support -> USB HID transport layer -
设备树确认:
dts复制&usbh_ehci { status = "okay"; }; -
测试验证:
bash复制cat /dev/input/mouse0
9.2 U盘存储功能��现
-
内核配置:
code复制Device Drivers -> USB support -> USB Mass Storage support -
加载驱动:
bash复制
modprobe usb-storage -
挂载测试:
bash复制
mount /dev/sda1 /mnt/usb
10. 性能优化与高级特性
10.1 传输性能优化
-
批量传输优化:
- 增大URB缓冲区
- 使用散射/聚集IO
- 启用DMA
-
中断延迟优化:
- 调整轮询间隔
- 使用NAK限流
-
电源管理:
- 合理使用自动挂起
- 优化唤醒策略
10.2 OTG模式切换
STM32MP1支持动态主从切换:
-
硬件设计:
- 确保ID引脚正确连接
- 提供VBUS检测电路
-
软件配置:
c复制// 设置为Host模式 otg->gadget.ops->set_peripheral(NULL); otg->host->is_b_host = 0; -
模式检测:
c复制if (gpio_get_value(ID_PIN)) // Device模式 else // Host模式
在实际项目中,我发现STM32MP1的USB HS PHY对PCB布局非常敏感。有一次调试时遇到USB 3.0设备连接不稳定的问题,最终发现是PHY的差分线对长度匹配不够精确。通过重新设计PCB走线,将差分对长度差控制在5mil以内,问题得到完美解决。这个经验告诉我,高速USB设计必须严格遵循硬件设计规范。
