API GUIDE / USB_PHY / MIDDLEWARE
USB PHY Middleware 接口
1. 文档范围
本文档定义 G32S10 SDK USB middleware(MID)层面向应用、BSP 和示例代码的正式接口。本文只描述 code/middleware/usb/ 及其公开的 code/include/usb/ 接口,覆盖:
- USB 控制器初始化、退出、挂起/恢复、主机/设备角色查询和 VBUS 状态更新
- USB Device(Gadget)的 CDC ACM、HID、Bulk、WinUSB、Printer、Display、Mass Storage、RNDIS、NCM、UAC1 和 UVC 设备类
- UVC+HID、UVC+CDC、UVC+UAC1、多路 UVC、Serial+HID、Display+UAC1+HID 等复合设备
- USB Host 的 CDC ACM、CDC NCM、CH341、HID 键盘/鼠标/Vendor、UVC 和 Mass Storage 驱动
- 应用需要提供的描述符、配置、缓冲区和回调类型
USB PHY、CPM 侧 USB 时钟/复位/IDDIG 控制、PHY 寄存器映射和底层 LL 接口不属于 USB MID,统一参见 USB PHY HAL/LL 正式接口文档。
本文档以当前 SDK 源码为准,主要核对文件如下:
| 类型 | 路径 |
|---|---|
| 核心公开接口 | code/include/usb/usb.h |
| 平台公开类型 | code/include/usb/platform.h |
| OTG 公开类型 | code/include/usb/otg.h |
| USB 标准类型 | code/include/usb/ch9.h、code/include/usb/cdc.h、code/include/usb/uac.h、code/include/usb/uvc.h |
| Gadget 公开接口 | code/include/usb/gadget_*.h |
| Host 公开接口 | code/include/usb/host_*.h |
| MID 构建配置 | code/middleware/usb/Kconfig |
| MID 实现 | code/middleware/usb/ |
| MID 示例 | code/middleware/usb/example/ |
补充说明:
- 本文的“正式接口”指
code/include/usb/中对应用公开的接口。code/middleware/usb/core.h、hcd.h、gadget.h、usb_internal.h等头文件包含 DWC2、HCD、UDC 和 Linux 风格 USB Core 的内部接口,应用通常不应直接调用。 - 当前平台公开一个 USB 控制器实例 USB0。USB MID 管理 DWC2 控制器、USB IRQ、工作队列和 VBUS 策略,并通过平台 glue 调用独立的 USB PHY 驱动。
- USB MID 通过
code/include/usb/platform.h的struct usb_phy_ops调用 USB PHY;PHY 内部时序、寄存器、CPM 辅助接口和 LL 接口由独立文档定义。 - USB Device 和 USB Host 的类接口受不同 Kconfig 选项保护;未启用对应选项时,头文件中可能没有函数声明,相关源文件也不会参与构建。
- 同一固件只能选择一个 Gadget 类或复合 Gadget。Host 类驱动没有同样的互斥限制,可以同时启用多个。
- 本文描述当前源码的实际行为。头文件注释、参数注释与实现不一致时,以本文列出的实现语义为准。
2. 分层关系与依赖
Application / BSP / Example
|
+-------------------+--------------------------+
| | |
v v v
Gadget class API Host class API USB core API
serial/hid/uvc/... cdc/ch341/uvc/msc/... usb_core_*
| | |
v v |
Composite / UDC API USB Host Core / HCD |
+-------------------+--------------------------+
|
v
DWC2 USB controller
|
v
USB PHY / CPM clock / GPIO / IRQ
USB MID 的职责:
- 根据
USB_PERIPHERAL、USB_HOST或USB_DUAL_ROLE配置初始化 DWC2 的 Device、Host 或 OTG 角色。 - 通过
struct usb_phy_ops管理 USB PHY 生命周期,并管理控制器时钟、USB IRQ、工作队列、Host 根集线器和 Gadget UDC 的注册。 - 为应用提供连接状态、枚举状态、供电类型、设备插拔和类协议回调。
- 为 RNDIS/NCM Gadget 和 CDC NCM Host 注册 lwIP Ethernet netif,并完成 USB Ethernet 帧与 lwIP
pbuf之间的转换。 - 在 Host 侧完成 USB 枚举、驱动匹配、拓扑维护和类驱动绑定。
- 在 Device 侧完成配置描述符、端点匹配、标准请求分发和复合功能管理。
- 通过各 Gadget/Host 类接口隐藏 DWC2 端点、URB、USB request 和描述符细节。
USB MID 不负责:
- 应用协议、图像采集、音频采样和存储文件系统由对应 MID 或业务模块负责;USB MID 只提供 USB 类协议与数据通道。
- TCP/IP 协议栈本身;RNDIS/NCM 依赖外部初始化的 lwIP,MID 只负责 USB 网络链路和 netif 适配。
- 在 USB 回调中替应用执行长时间业务逻辑。
- 自动管理
uvc_buffer、Mass Storage回调缓冲区或应用读写缓冲区。 - 在所有配置下自动完成 VBUS 检测;
USB_DISABLE_VBUS场景需要外部 VBUS 事件驱动。
主要依赖:
| 依赖 | 用途 |
|---|---|
DRIVER_USB_PHY |
MID 编译前置;提供 struct usb_phy_ops 所需的 PHY 初始化、复位、挂起/恢复和供电类型检测实现。内部行为见 USB PHY HAL/LL 文档。 |
DRIVER_HRTIMER |
USB 工作队列的延时工作 |
OS_API |
线程、互斥量、信号量、条件变量、等待器和临界区 |
IRQ/PLIC |
USB 控制器中断注册、使能和释放 |
GPIO |
Host VBUS 电源使能、OTG 模式相关 GPIO |
MIDDLEWARE_NET_LWIP |
RNDIS/NCM Gadget 和 CDC NCM Host 的编译与运行前置;提供 lwIP、netifapi_*、pbuf、DHCP 和 IPv6 能力。lwIP 当前要求 OS_BACKEND_FREERTOS。 |
LIB_RING_MEM |
UAC1 及其复合 Gadget 的音频环形缓冲区 |
Storage/DFS |
USB_HOST_MASS_STORAGE_AUTO_MOUNT 自动挂载,插入U盘外设后自动挂载到文件系统 |
3. 构建配置与文件组织
3.1 Kconfig
USB MID 的 Kconfig 位于 code/middleware/usb/Kconfig。USB PHY 配置不在 MID 范围内,见 USB PHY HAL/LL 文档。
3.1.1 基础选项
| 选项 | 默认值 | 说明 |
|---|---|---|
USB_DRIVER |
n |
构建 DWC2 USB MID 与平台 glue;依赖 DRIVER_USB_PHY,并自动选择 DRIVER_HRTIMER。 |
USB0 |
y |
启用 USB0 OTG 控制器。 |
USB_PERIPHERAL |
角色选择默认值 | 固定为 Device 角色,选择 USB_IS_DEVICE。 |
USB_HOST |
角色选择默认值 | 固定为 Host 角色,选择 USB_IS_HOST,依赖 DRIVER_GPIO。 |
USB_DUAL_ROLE |
角色选择默认值 | OTG/双角色模式,同时选择 USB_IS_DEVICE 和 USB_IS_HOST,依赖 DRIVER_GPIO。 |
USB_IS_DEVICE |
自动 | 内部派生选项,表示固件包含 Device 路径。 |
USB_IS_HOST |
自动 | 内部派生选项,表示固件包含 Host 路径。 |
USB_PERIPHERAL、USB_HOST、USB_DUAL_ROLE 是互斥 choice,只能选择其中一个。
DRIVER_USB_PHY 属于 USB PHY 驱动配置;MID 仅要求 DRIVER_USB_PHY=y 作为编译前置,PHY 专用选项见 USB PHY HAL/LL 文档。
3.1.2 Gadget 选项
下列选项属于同一个 choice,只能选择一个:
| 选项 | 说明 | 附加依赖 |
|---|---|---|
USB_GADGET_SERIAL |
CDC ACM 串口 | 无 |
USB_GADGET_HID |
HID,可同时提供两个 HID function | 无 |
USB_GADGET_HID_WAKEUP |
带 suspend/resume 和 remote wakeup 的 HID | 无 |
USB_GADGET_SERIAL_HID |
CDC ACM + HID 复合设备 | 无 |
USB_GADGET_UVC |
单路 UVC 摄像头 | 无 |
USB_GADGET_UVC_N |
多路 UVC,最多 4 路 | 无 |
USB_GADGET_UVC_HID |
UVC + HID | 无 |
USB_GADGET_UVC_CDC |
UVC + CDC ACM | 无 |
USB_GADGET_BULK |
通用 bulk 双向通道 | 无 |
USB_GADGET_UAC1 |
UAC1 音频 | 自动选择 LIB_RING_MEM |
USB_GADGET_UVC_UAC1 |
UVC + UAC1 | 自动选择 LIB_RING_MEM |
USB_GADGET_UVC_N_UAC1 |
多路 UVC + UAC1 | 自动选择 LIB_RING_MEM |
USB_GADGET_MASS_STORAGE |
Mass Storage | 无 |
USB_GADGET_PRINTER |
Printer | 无 |
USB_GADGET_DISPLAY |
Display | 无 |
USB_GADGET_DISPLAY_AUDIO_HID |
Display + UAC1 + HID | 自动选择 LIB_RING_MEM |
USB_GADGET_WINUSB |
WinUSB/OS descriptor 设备 | 无 |
USB_GADGET_RNDIS |
RNDIS 网络设备 | 依赖 MIDDLEWARE_NET_LWIP |
USB_GADGET_NCM |
CDC NCM 网络设备 | 依赖 MIDDLEWARE_NET_LWIP |
3.1.3 Host 选项
Host 类驱动可独立组合:
| 选项 | 说明 |
|---|---|
USB_HOST_FAST_ENUM |
缩短 Host 上电、端口复位、恢复和防抖等待,并启用 usb_host_fast_disconnect_all_devices()。 |
USB_HOST_HID_MOUSE |
HID Boot Mouse 主机驱动。 |
USB_HOST_HID_KEYBOARD |
HID Boot Keyboard 主机驱动。 |
USB_HOST_HID_VENDOR |
HID Vendor 中断端点主机驱动。 |
USB_HOST_CDC_ACM |
CDC ACM 主机驱动。 |
USB_HOST_CDC_NCM |
CDC NCM 主机驱动;依赖 USB_IS_HOST 和 MIDDLEWARE_NET_LWIP。 |
USB_HOST_CH341 |
CH341 USB 串口主机驱动。 |
USB_HOST_UVC |
UVC 摄像头主机驱动。 |
USB_HOST_MASS_STORAGE |
USB Mass Storage 主机驱动。 |
USB_HOST_MASS_STORAGE_AUTO_MOUNT |
自动注册块设备并发布分区;依赖 DFS_ELMFAT,自动选择 STORAGE_PARTITION。 |
3.1.4 USB 电气选项
| 选项 | 默认值 | 说明 |
|---|---|---|
USB_DISABLE_VBUS |
n |
忽略硬件 VBUS 检测;Device 模式需要应用调用 usb_device_vbus_status_update()。 |
USB_GADGET_VBUS_DRAW |
200 |
Device 声明并从总线申请的电流,单位 mA,范围 0 到 500。 |
USB_FORCE_FULL_SPEED |
n |
强制 Full-Speed。 |
USB_FIFO_1808 |
n |
1808 个 32-bit word(约 7232 字节)的 FIFO 布局。 |
USB_HOST_VBUS_POWER_GPIO_{NONE,A..E} |
NONE |
Host/OTG 模式下 VBUS 电源使能 GPIO 端口组。 |
USB_HOST_VBUS_POWER_PIN |
-1 |
VBUS 电源使能 GPIO 引脚;-1 表示未连接。 |
USB_HOST_VBUS_POWER_ACTIVE_LOW |
n |
VBUS 电源使能信号低有效。 |
USB_GADGET_STORAGE_NUM_BUFFERS |
2 |
Mass Storage Gadget 缓冲数量,范围 2 到 8。 |
3.2 配置到头文件的映射
| Kconfig | 应用应包含的头文件 | 主要入口 |
|---|---|---|
任意 USB_DRIVER |
<usb/usb.h> |
usb_core_*、Device 状态回调 |
USB_GADGET_SERIAL |
<usb/gadget_serial.h> |
gadget_serial_* |
USB_GADGET_HID |
<usb/gadget_hid.h> |
gadget_hid_* |
USB_GADGET_HID_WAKEUP |
<usb/gadget_hid_wakeup.h> |
gadget_hid_wakeup_*、gadget_hid_* |
USB_GADGET_SERIAL_HID |
<usb/gadget_serial_hid.h> |
gadget_serial_hid_* 及共用类接口 |
USB_GADGET_BULK |
<usb/gadget_bulk.h> |
gadget_bulk_* |
USB_GADGET_WINUSB |
<usb/gadget_winusb.h> |
gadget_winusb_* |
USB_GADGET_PRINTER |
<usb/gadget_printer.h> |
gadget_printer_* |
USB_GADGET_DISPLAY |
<usb/gadget_display.h> |
gadget_display_* |
USB_GADGET_DISPLAY_AUDIO_HID |
<usb/gadget_display_uac1_hid.h> |
gadget_display_audio_hid_* 及共用类接口 |
USB_GADGET_MASS_STORAGE |
<usb/gadget_mass_storage.h> |
gadget_mass_storage_* |
USB_GADGET_RNDIS |
<usb/gadget_rndis.h> |
gadget_rndis_* |
USB_GADGET_NCM |
<usb/gadget_ncm.h> |
gadget_ncm_* |
USB_GADGET_UAC1 |
<usb/gadget_uac1.h> |
gadget_uac1_* |
USB_GADGET_UVC |
<usb/gadget_uvc.h> |
gadget_uvc_* |
USB_GADGET_UVC_N |
<usb/gadget_uvc_n.h> |
gadget_uvc_n_* |
USB_GADGET_UVC_HID |
<usb/gadget_uvc_hid.h> |
gadget_uvc_* 与 gadget_hid_* |
USB_GADGET_UVC_CDC |
<usb/gadget_uvc_serial.h> |
gadget_uvc_serial_*、gadget_uvc_*、gadget_serial_* |
USB_GADGET_UVC_UAC1 |
<usb/gadget_uvc_uac1.h> |
gadget_uvc_uac1_*、gadget_uvc_*、gadget_uac1_* |
USB_GADGET_UVC_N_UAC1 |
<usb/gadget_uvc_n_uac1.h> |
gadget_uvc_n_uac1_*、gadget_uvc_n_*、gadget_uac1_* |
USB_HOST_CDC_ACM |
<usb/host_cdc_acm.h> |
usb_host_cdc_acm_* |
USB_HOST_CDC_NCM |
<usb/host_cdc_ncm.h> |
usb_host_cdc_ncm_* |
USB_HOST_CH341 |
<usb/host_ch341.h> |
usb_host_ch341_* |
USB_HOST_HID_KEYBOARD |
<usb/host_kbd.h> |
usb_kbd_register_callback() |
USB_HOST_HID_MOUSE |
<usb/host_mouse.h> |
usb_mouse_register_callback() |
USB_HOST_HID_VENDOR |
<usb/host_hid_vendor.h> |
usb_host_hid_vendor_* |
USB_HOST_UVC |
<usb/host_uvc.h> |
usb_host_uvc_* |
USB_HOST_MASS_STORAGE |
<usb/host_mass_storage.h> |
usb_host_mass_storage_* |
注意:
USB_GADGET_UVC_CDC对应的公开头文件是gadget_uvc_serial.h,入口函数是gadget_uvc_serial_init()。USB_GADGET_DISPLAY_AUDIO_HID的入口函数是gadget_display_audio_hid_init()。- 多个复合 Gadget 复用
gadget_hid_*、gadget_serial_*、gadget_uvc_*、gadget_uac1_*等相同符号名,因此只能按当前选中的复合配置包含对应头文件。
3.3 最小配置
CDC ACM Device:
CONFIG_DRIVER_USB_PHY=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_PERIPHERAL=y
CONFIG_USB_GADGET_SERIAL=y
CDC ACM Host:
CONFIG_DRIVER_USB_PHY=y
CONFIG_DRIVER_GPIO=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_HOST=y
CONFIG_USB_HOST_CDC_ACM=y
UVC Camera Device:
CONFIG_DRIVER_USB_PHY=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_PERIPHERAL=y
CONFIG_USB_GADGET_UVC=y
UAC1、UVC+UAC1、多路 UVC+UAC1 以及 Display+UAC1+HID 会通过 Kconfig 自动选择 LIB_RING_MEM。
NCM Device:
CONFIG_OS=y
CONFIG_OS_BACKEND_FREERTOS=y
CONFIG_OS_API=y
CONFIG_MIDDLEWARE_NET_LWIP=y
CONFIG_MIDDLEWARE_NET_LWIP_IPV6=y # 使用 IPv6 时启用
CONFIG_DRIVER_USB_PHY=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_PERIPHERAL=y
CONFIG_USB_GADGET_NCM=y
NCM Host:
CONFIG_OS=y
CONFIG_OS_BACKEND_FREERTOS=y
CONFIG_OS_API=y
CONFIG_MIDDLEWARE_NET_LWIP=y
CONFIG_MIDDLEWARE_NET_LWIP_IPV6=y # 使用 IPv6/RA 时启用
CONFIG_DRIVER_USB_PHY=y
CONFIG_DRIVER_GPIO=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_HOST=y
CONFIG_USB_HOST_CDC_NCM=y
MIDDLEWARE_NET_LWIP 当前要求 FreeRTOS 后端。NCM 示例使用 usb_gadget_ncm_defconfig 和 usb_host_ncm_defconfig,两者都启用 IPv6。
3.4 文件职责
| 文件或目录 | 说明 |
|---|---|
code/include/usb/ |
USB 应用公开头文件、标准协议定义和错误码兼容定义 |
code/middleware/usb/core.c |
DWC2 控制器初始化、角色切换和底层公共逻辑 |
code/middleware/usb/platform.c |
平台实例、PHY、时钟、IRQ、usb_core_* 和 VBUS glue |
code/middleware/usb/gadget.c / core_intr.c |
Device 控制器、端点和中断实现 |
code/middleware/usb/gadget/ |
Gadget function 和复合设备实现 |
code/middleware/usb/gadget/ncm/ |
CDC NCM Gadget function、Ethernet 适配和 lwIP netif 实现 |
code/middleware/usb/hcd.c / hcd_queue.c / hcd_intr.c |
DWC2 Host Controller Driver |
code/middleware/usb/host/ |
USB Host Core、Hub、URB 和 Host 类驱动 |
code/middleware/usb/host/cdc-ncm/ |
CDC NCM Host 驱动、NTB 编解码、RX/TX 工作队列、netif、DHCP/RA 和诊断实现 |
code/middleware/usb/example/ |
可选择的 Device/Host 示例与 defconfig |
4. 资源模型与公开类型
4.1 控制器、角色与实例
| 项目 | 当前实现 |
|---|---|
| 控制器 | 公开 USB0 一个 DWC2 OTG 控制器实例 |
| Device 实例 | USB0 Gadget/UDC |
| Host 实例 | USB0 根集线器 |
| Host 地址空间 | 根集线器加下游 Hub/设备,由 Host Core 自动枚举 |
| Host 类设备索引 | 每类最多 32 个,使用 u32 bitmap 表示,索引范围 0 到 31 |
| USB 速度 | High-Speed 为主;可通过 USB_FORCE_FULL_SPEED 强制 Full-Speed |
角色由编译期配置决定,并提供运行时查询:
| 配置 | 控制器行为 | 可用接口 |
|---|---|---|
USB_PERIPHERAL |
固定 Device | usb_core_is_device_mode()、Gadget 类 |
USB_HOST |
固定 Host | usb_core_is_host_mode()、Host 类 |
USB_DUAL_ROLE |
OTG 双角色 | 两个模式查询接口以及 Device/Host 类 |
公开 API 只提供当前模式查询,不提供应用级“强制切换 Host/Device”的通用接口。双角色切换取决于 OTG 硬件状态机、VBUS/ID 信号和控制器实现;应用不能用 usb_core_is_* 代替切换动作。
4.2 生命周期
Device 推荐顺序:
可选:usb_device_set_state_callback()
可选:usb_device_set_power_callback()
|
v
usb_core_init()
|
v
gadget_<class>_init()
|
v
连接回调 / wait_connect / read / write / wait_stream ...
|
v
gadget_<class>_cleanup()
|
v
usb_core_exit()
Host 推荐顺序:
usb_host_init()
|
v
usb_host_<class>_register_callback()
|
v
usb_core_init()
|
v
设备插拔 bitmap 回调 / open / I/O / get_buffer ...
|
v
close / 释放类资源
|
v
usb_core_exit()
NCM/RNDIS Device 的额外顺序要求:
usb_core_init()
|
v
初始化 lwIP(推荐 im_net_lwip_core_init())
|
v
gadget_<class>_init()
|
v
可配置 IP / 等待 USB 数据接口连接
|
v
gadget_<class>_cleanup()
|
v
usb_core_exit()
NCM Host 的额外顺序要求:
初始化 lwIP(推荐 im_net_lwip_core_init())
|
v
usb_host_init()
|
v
usb_host_cdc_ncm_register_callback()
|
v
usb_core_init()
|
v
bit 0 置位后配置静态 IP / DHCP / IPv6 / RA
|
v
bit 0 清零时停止使用 usb_host_cdc_ncm_get_stats() 返回值
约束:
usb_core_init()成功后必须且只能初始化一个 Gadget 类,或使用一组受当前 Gadget 配置支持的复合接口。- RNDIS/NCM 初始化前必须确保 lwIP TCP/IP 线程已经启动;
netifapi_*、tcpip_input()和pbuf分配不能在 lwIP 初始化前使用。 - NCM Host 没有应用级 open/close;USB 枚举成功时驱动自动创建 netif,设备移除时自动停止 RX/TX、移除 netif 并调用 bitmap 回调。
- Device 状态/供电回调应在
usb_core_init()之前注册;实现也允许初始化后替换回调。 - Host 类插拔回调应在
usb_core_init()之前注册,以免漏掉首次枚举事件。 - 调用 cleanup/close 前应停止应用线程对对应类接口的并发调用,并释放仍由应用持有的 UVC/Display 缓冲区。
usb_core_exit()要求先注销 Gadget 或关闭 Host 类资源,不能在有活动 I/O 时调用。
4.3 返回值、超时与回调上下文
4.3.1 返回值
| 返回类型 | 约定 |
|---|---|
0 |
初始化、配置、等待、flush 或 buffer 操作成功 |
| 正数 | 实际读/写字节数;部分查询接口返回数量、大小或 bitmap |
| 负数 | Linux 风格负 errno,常见值见 11. 错误码 |
void |
无返回值;参数或内部初始化错误可能通过 assert()/panic() 暴露 |
bool |
查询/判断结果;未初始化时通常返回 false |
NCM 的静态 IP、DHCP 和 RA 接口会在部分路径直接返回 lwIP err_t。这类错误通常也是负值,但取值来自 lwIP(如 ERR_MEM、ERR_CONN、ERR_USE),不一定等于 Linux errno;调用方应同时检查“是否小于 0”和 ERR_OK,不要混用 -errno 语义。
带 block 的 Gadget I/O:
block != 0:条件不满足时等待,直到数据/空间可用、超时或断开。block == 0:立即返回;无数据或无发送空间时通常返回-EAGAIN。
Gadget I/O 的 timeout_ms 类型为 uint32_t:
0表示非阻塞路径的立即返回值。THREAD_COND_TIMEOUT_NO_LIMIT_MS或UINT32_MAX表示永久等待。
Host 串口/HID 类 I/O 的 timeout 类型为 int:
timeout > 0:等待指定毫秒数。timeout <= 0:当前实现按永久等待处理。
Host UVC 的 usb_host_uvc_get_buffer() 使用 uint32_t timeout_ms:
0:立即检查,无 ready buffer 时返回-ENODATA。UINT32_MAX:永久等待。
4.3.2 回调上下文
USB 回调可能运行在 HCD/UDC 中断、URB completion、USB 工作队列或 Hub 工作线程中。回调应满足:
- 仅做状态更新、事件置位、等待者唤醒或短事务。
- 不在 callback 内执行无界阻塞、长时间内存分配、文件系统操作或复杂业务。
- 不在 callback 内调用会等待自身资源释放的 close/cleanup。
- 对共享状态使用 OS 临界区、原子变量或队列,不在 callback 中假设自己是普通应用线程。
Host HID 键盘/鼠标回调由 URB completion 路径调用,必须按 ISR 安全代码处理。Gadget Connect/Format/Stream/Param 回调通常在控制传输或功能启停路径中执行,也只应做短操作。NCM Host 的 device bitmap 回调在 USB Hub 枚举/移除工作线程中执行,回调应立即返回,把 IP 配置和业务处理交给普通线程。
4.3.3 连接状态
所有 connect_callback_t 统一使用:
| 值 | 含义 |
|---|---|
1 |
类功能已配置并连接 |
0 |
类功能断开、端点禁用或设备 reset |
gadget_*_get_connect_status() 返回:
| 返回值 | 含义 |
|---|---|
1 |
已连接;UVC 在 connected 或 streaming 状态也返回 1 |
0 |
已初始化但尚未连接 |
| 负 errno | 未初始化、实例非法或设备已移除 |
4.4 公共类型
4.4.1 struct gadget_id
struct gadget_id {
uint16_t vendor_id;
uint16_t product_id;
};
用于设置 USB Device 描述符中的 idVendor 和 idProduct。所有 Gadget 初始化接口都要求传入有效指针。
4.4.2 公共回调
typedef void (*connect_callback_t)(int connect);
typedef void (*suspend_callback_t)(int suspend);
typedef void (*usb_state_callback_t)(enum usb_device_state state);
typedef void (*usb_power_callback_t)(enum usb_device_power type,
unsigned mA);
suspend_callback_t 的参数为 1 表示进入 suspend,0 表示 resume。
4.4.3 enum usb_device_state
| 值 | 说明 |
|---|---|
USB_STATE_NOTATTACHED |
未连接/已断开 |
USB_STATE_ATTACHED |
已检测到连接 |
USB_STATE_POWERED |
已上电 |
USB_STATE_RECONNECTING |
无线 USB 认证相关状态 |
USB_STATE_UNAUTHENTICATED |
无线 USB 认证相关状态 |
USB_STATE_DEFAULT |
USB Default 状态 |
USB_STATE_ADDRESS |
已分配 USB 地址 |
USB_STATE_CONFIGURED |
已选择配置,类功能可用 |
USB_STATE_SUSPENDED |
总线挂起 |
可用 usb_state_string() 转换为只读诊断字符串。
4.4.4 enum usb_device_power
| 值 | 说明 |
|---|---|
USB_POWER_UNKNOWN |
未知或未连接 |
USB_POWER_SDP |
Standard Downstream Port |
USB_POWER_DCP |
Dedicated Charging Port |
USB_POWER_CDP |
Charging Downstream Port |
可用 usb_power_string() 转换为只读诊断字符串。
4.4.5 CDC 串口参数
struct usb_cdc_serial_param {
u32 dwDTERate;
u8 bCharFormat;
u8 bParityType;
u8 bDataBits;
};
typedef void (*serial_param_callback_t)(
struct usb_cdc_serial_param *param);
支持的公开常量为:
| 类别 | 常量 |
|---|---|
| 停止位 | USB_CDC_1_STOP_BITS、USB_CDC_1_5_STOP_BITS、USB_CDC_2_STOP_BITS |
| 校验 | USB_CDC_NO_PARITY、USB_CDC_ODD_PARITY、USB_CDC_EVEN_PARITY、USB_CDC_MARK_PARITY、USB_CDC_SPACE_PARITY |
gadget_serial_init() 的 param == NULL 时使用 115200 bps、1 停止位、无校验、8 数据位。
4.4.6 HID 描述符与回调
struct hid_report_descriptor {
uint8_t subclass;
uint8_t protocol;
unsigned short report_length;
unsigned short report_desc_length;
const uint8_t *report_desc;
};
typedef int (*hid_request_callback_t)(
void *data, u16 *length, int request);
struct hid_callback {
connect_callback_t connect_cb;
hid_request_callback_t request_cb;
};
request_cb(data, length, request) 用于处理 HID Class-Specific Request:
- 输入/输出
data指向协议数据。 length是可用长度指针;回调接受请求时,应通过它给出实际响应长度。request是 HID request code。- 非负返回值表示接受请求,负 errno 表示 stall/不支持。
- 描述符和回调结构体必须在 Gadget 生命周期内保持有效。
4.5 Gadget 配置类型
4.5.1 MSC
struct fsg_config / struct fsg_lun_config
typedef int (*fsg_read_callback_t)(void *buf, int count, u64 pos);
typedef int (*fsg_write_callback_t)(const void *buf, int count, u64 pos);
typedef int (*fsg_sync_callback_t)(void);
struct fsg_lun_config {
fsg_read_callback_t read_callback;
fsg_write_callback_t write_callback;
fsg_sync_callback_t sync_callback;
char ro;
char removable;
char cdrom;
unsigned long long num_sectors;
unsigned int block_size;
const char *vendor_name;
const char *product_name;
unsigned int product_version;
};
struct fsg_config {
const char *serialnumber;
unsigned nluns;
struct fsg_lun_config *luns;
connect_callback_t connect_cb;
};
回调语义:
| 回调 | 说明 |
|---|---|
read_callback |
从应用介质读取 count 字节到 buf,返回实际读取字节数;失败返回负 errno。 |
write_callback |
把 buf 中 count 字节写入应用介质,返回实际写入字节数;失败返回负 errno。 |
sync_callback |
可选;用于 flush 写缓存。只读 LUN 或未提供时按成功处理。 |
fsg_lun_config 要求:
block_size和num_sectors非零。- 至少提供
read_callback。 - 非只读 LUN 必须提供
write_callback。 - CD-ROM LUN 的
block_size必须为 2048,扇区数必须大于等于 300 且小于256 * 60 * 75。 - 描述字段
vendor_name、product_name应分别不超过 8 和 16 个字符。
fsg_config 的 nluns 为 0、luns 为空或回调不满足要求时,当前实现通过 panic() 终止,而不是返回参数错误。
4.5.2 RNDIS
struct usb_rndis_param
struct usb_rndis_param {
const char *ipaddr;
const char *netmask;
const char *gw;
int mtu;
unsigned char dev_mac[6];
unsigned char host_mac[6];
};
ipaddr、netmask、gw 必须指向 RNDIS Gadget 生命周期内有效的 IPv4 字符串;初始化只复制结构体,不复制字符串内容。mtu == 0 时使用 1500。非单播、全零、全 ff 或两端相同的 MAC 会由驱动重新生成/修正。
4.5.3 WinUSB 描述符
struct winusb_ext_prop {
u8 type;
const char *name;
const void *data;
int data_len;
};
struct winusb_descriptor {
u8 qw_sign[14];
u8 vendor_code;
char ext_compat_id[16];
u32 ext_prop_count;
struct winusb_ext_prop *ext_prop;
};
ext_prop 及其 name/data 必须保持有效。winusb_descriptor 同时决定 OS String 的兼容 ID、vendor code 和扩展属性。
4.5.4 Display 类型
struct display_edid {
const u8 *buf;
u8 size;
};
struct display_specific_des {
const u8 *buf;
u8 size;
};
struct display_buffer {
const u8 *buf;
u32 length;
u32 actual;
void *private_data;
};
edid 和厂商自定义描述符不能为空。display_buffer 由驱动填写,private_data 是内部 request 句柄,应用不得修改。
状态位:
| 常量 | 值 | 说明 |
|---|---|---|
DISPLAY_NOT_ERROR |
0x08 |
无错误 |
DISPLAY_SELECTED |
0x10 |
已选中 |
4.5.5 NCM Gadget 参数
struct usb_ncm_param {
const char *ipaddr;
const char *netmask;
const char *gw;
int mtu;
unsigned char dev_mac[6];
unsigned char host_mac[6];
};
| 字段 | 说明 |
|---|---|
ipaddr / netmask / gw |
gadget netif 的初始 IPv4 地址、掩码和网关。当前实现会在 bind 时调用 lwIP 的 IPv4 解析函数,因此三个指针必须非空,并指向有效 IPv4 字符串。 |
mtu |
netif MTU;0 时由 gadget_ncm_init() 改为 1500。驱动不检查调用方给出的上限。 |
dev_mac |
Device 侧 netif MAC,也是 Host 发包的目标 MAC。 |
host_mac |
Host 侧 MAC,通过 CDC Ethernet iMACAddress 字符串导出给 Host。 |
gadget_ncm_init() 只复制 struct usb_ncm_param,不复制三个 IP 字符串的内容;字符串存储必须在 gadget_ncm_cleanup() 前保持有效且不再修改。MAC 数组会按值复制。
MAC 校验行为:
dev_mac或host_mac为全零时,驱动分别替换为02:12:34:56:78:9a和02:12:34:56:78:9b。- 非零 MAC 不再由驱动校验;应用应保证两端都是唯一的单播地址,并建议使用本地管理地址。
- 驱动不检查两端 MAC 是否相同;应用必须保证
dev_mac != host_mac,否则可能造成 ARP/IPv6 邻居发现问题。
param == NULL 虽可进入初始化函数,但当前 NCM bind 仍会使用内部参数中的 IP 字符串。由于未提供可用默认 IP,当前实现不把 NULL 视为安全的“全默认配置”;应用应传入非空 param 和有效的 IPv4 字符串。
4.6 UVC 公共类型
4.6.1 struct uvc_buffer
struct uvc_buffer {
struct list_head queue;
enum uvc_buffer_state state;
unsigned int error;
void *mem;
uint32_t length;
uint32_t bytesused;
void (*complete)(struct uvc_buffer *buf);
void *private_data;
unsigned int timestamp; /* us */
};
| 状态 | 说明 |
|---|---|
UVC_BUF_STATE_IDLE |
空闲 |
UVC_BUF_STATE_QUEUED |
已提交给驱动,等待传输 |
UVC_BUF_STATE_ACTIVE |
正在传输 |
UVC_BUF_STATE_READY |
数据已就绪,Host 侧使用 |
UVC_BUF_STATE_DONE |
Gadget 已发送完成 |
UVC_BUF_STATE_ERROR |
传输错误或断开 |
Gadget 应用在调用 gadget_uvc_write() 前设置 mem、length 和可选 complete,提交后不得修改/复用,直到 complete 回调或状态变为 DONE/ERROR。
Host UVC 驱动分配的 buffer 由 Host UVC API 管理;应用只读取 state、bytesused、error 等字段,并在处理完后调用 usb_host_uvc_put_buffer() 归还。
4.6.2 struct uvc_video_format
struct uvc_video_format {
u32 fcc;
unsigned int width;
unsigned int height;
unsigned int bpp;
unsigned int fps;
};
fcc 使用 V4L2 pixel format 四字符码,bpp 为每像素位数,fps 为帧率。uvc_buffer.timestamp 的公开定义单位为微秒;Gadget 发送路径会在其非 0 时用于生成 UVC PTS,当前 Host UVC 接收路径没有写入该字段,应用不应把 Host buffer 的 timestamp 当作有效采集时间。
4.6.3 UVC 帧和格式配置(Gadget)
struct uvc_frame_config {
unsigned int width;
unsigned int height;
unsigned int fps_num;
const unsigned int *frame_fps;
};
struct uvc_format_config {
unsigned int fcc;
unsigned int bpp;
unsigned int frames_num;
const struct uvc_frame_config *frames;
};
struct uvc_device_config {
unsigned int format_num;
unsigned int camera_feature_config;
unsigned int camera_param_config;
const struct uvc_format_config *formats;
};
UVCN 配置在其后额外包含 const struct uvc_callback *callback 字段,callback 按 UVC 实例提供。
配置数组的维度要求如下:
frames指向frames_num个 frame 配置。frame_fps指向fps_num个帧率。formats指向format_num个像素格式。- 示例约定
frame_fps从大到小、frame 尺寸从小到大排序,但驱动不替应用校验该顺序。
camera_feature_config 和 camera_param_config 使用 uvc.h 中的 UVC_FEATURE_*、UVC_PARAM_* 位掩码。
4.6.4 UVC 控制请求
struct uvc_control_request {
void *buf;
unsigned int buf_actual;
unsigned char request;
unsigned int request_len;
unsigned char entity_id;
unsigned char control_selector;
unsigned char event_out;
};
param_cb 根据 request 处理 UVC_SET_CUR、UVC_GET_CUR、UVC_GET_MIN、UVC_GET_MAX 等请求:
buf是可读写协议数据区。request_len是请求最大长度。buf_actual是实际收到的字节数。- 返回实际应发送的字节数;不支持返回
-EOPNOTSUPP。 - 回调返回后驱动使用该长度完成控制传输。
4.6.5 UVC request、status 和 payload 常量
| 类别 | 常量 |
|---|---|
| Control request | UVC_SET_CUR、UVC_GET_CUR、UVC_GET_MIN、UVC_GET_MAX、UVC_GET_RES、UVC_GET_LEN、UVC_GET_INFO、UVC_GET_DEF |
| Status type | UVC_STATUS_TYPE_CONTROL、UVC_STATUS_TYPE_STREAMING |
| Payload header bits | UVC_STREAM_EOH、UVC_STREAM_ERR、UVC_STREAM_STI、UVC_STREAM_RES、UVC_STREAM_SCR、UVC_STREAM_PTS、UVC_STREAM_EOF、UVC_STREAM_FID |
| Control capability | UVC_CONTROL_CAP_GET、UVC_CONTROL_CAP_SET、UVC_CONTROL_CAP_DISABLED、UVC_CONTROL_CAP_AUTOUPDATE、UVC_CONTROL_CAP_ASYNCHRONOUS |
4.6.6 UVC feature/param 位掩码
| 类别 | 公开位掩码 |
|---|---|
| Camera terminal feature | UVC_FEATURE_SCANNING_MODE、UVC_FEATURE_AUTO_EXPOSURE_MODE、UVC_FEATURE_AUTO_EXPOSURE_PRIORITY、UVC_FEATURE_EXPOSURE_TIME_ABSOLUTE、UVC_FEATURE_EXPOSURE_TIME_RELATIVE、UVC_FEATURE_FOCUS_ABSOLUTE、UVC_FEATURE_FOCUS_RELATIVE、UVC_FEATURE_IRIS_ABSOLUTE、UVC_FEATURE_IRIS_RELATIVE、UVC_FEATURE_ZOOM_ABSOLUTE、UVC_FEATURE_ZOOM_RELATIVE、UVC_FEATURE_PAN_TILT_ABSOLUTE、UVC_FEATURE_PAN_TILT_RELATIVE、UVC_FEATURE_ROLL_ABSOLUTE、UVC_FEATURE_ROLL_RELATIVE、UVC_FEATURE_FOUCES_AUTO、UVC_FEATURE_PRIVACY、UVC_FEATURE_FOCUS_SIMPLE、UVC_FEATURE_WINDOW、UVC_FEATURE_REGION_OF_INTEREST |
| Processing unit param | UVC_PARAM_BRIGHTNESS、UVC_PARAM_CONTRAST、UVC_PARAM_HUE、UVC_PARAM_SATURATION、UVC_PARAM_SHARPNESS、UVC_PARAM_GAMMA、UVC_PARAM_WHITE_BALANCE_TEMPERATURE、UVC_PARAM_WHITE_BALANCE_COMPONENT、UVC_PARAM_WHITE_BACKLIGHT_COMPENSATION、UVC_PARAM_GAIN、UVC_PARAM_POWER_LINE_FREQUEBCY、UVC_PARAM_HUE_AUTO、UVC_PARAM_WHITE_BALANCE_TEMPERATURE_AUTO、UVC_PARAM_WHITE_BALANCE_COMPONENT_AUTO、UVC_PARAM_DIGITAL_MULTIPLIER、UVC_PARAM_DIGITAL_MULTIPLIER_LIMIT、UVC_PARAM_ANALOG_VIDEO_STANDARD、UVC_PARAM_ANALOG_VIDEO_LOCK_STATUS、UVC_PARAM_CONTRAST_AUTO |
UVC_PARAM_POWER_LINE_FREQUEBCY 和 UVC_FEATURE_FOUCES_AUTO 的拼写来自当前公开头文件,调用方应按源码拼写使用。
4.7 UAC1 公共类型
typedef int (*uac1_feature_callback_t)(u8 type, u8 request,
void *buf, u16 len);
typedef int (*uac1_start_callback_t)(u32 rate);
typedef void (*uac1_stop_callback_t)(void);
struct uac1_params {
/* playback */
u32 p_chmask;
u32 p_ssize;
u32 p_srate_num;
u32 *p_srates;
u32 p_srate;
u16 p_feature;
uac1_feature_callback_t p_feature_callback;
uac1_start_callback_t start_playback_callback;
uac1_stop_callback_t stop_playback_callback;
/* capture */
u32 c_chmask;
u32 c_ssize;
u32 c_srate_num;
u32 *c_srates;
u32 c_srate;
u16 c_feature;
uac1_feature_callback_t c_feature_callback;
uac1_start_callback_t start_capture_callback;
uac1_stop_callback_t stop_capture_callback;
u32 buffer_size_ms;
connect_callback_t connect_cb;
};
struct uac1_params 包含 playback 和 capture 两组参数:
| 字段组 | 说明 |
|---|---|
p_chmask / c_chmask |
通道布局,使用 UAC_CH_LAYOUT_*;0 表示方向禁用 |
p_ssize / c_ssize |
每样本字节数,必须与驱动端点格式一致 |
*_srate_num / *_srates |
支持的采样率数组 |
*_srate |
默认采样率 |
*_feature / *_feature_callback |
Feature Unit 控制位和回调 |
*_start_callback / *_stop_callback |
流启动/停止回调 |
buffer_size_ms |
PCM ring 缓冲时长,单位 ms |
connect_cb |
UAC1 连接状态回调 |
回调参数:
feature_callback(type, request, buf, len)的type是UAC_FU_MUTE、UAC_FU_VOLUME等 control selector,request是UAC_SET_CUR、UAC_GET_CUR等请求。start_callback(rate)用于通知流启动;当前实现忽略其返回值。stop_callback()表示 Host 停止流。
gadget_uac1_playback_read() 从“Host 发给 Device、由应用播放/消费”的 ring 中读取数据;gadget_uac1_capture_write() 将“Device 采集、发给 Host”的数据写入 ring。
4.8 Host 公共类型
4.8.1 CDC ACM 和 CH341 行参数
struct cdc_acm_line_coding {
u32 dwDTERate;
u8 bCharFormat;
u8 bParityType;
u8 bDataBits;
};
struct ch341_line_coding {
u32 dwDTERate;
u8 bCharFormat;
u8 bParityType;
u8 bDataBits;
u8 hardflow;
};
CDC ACM 控制线常量:
| 方向 | 常量 |
|---|---|
| 输出 | ACM_CTRL_DTR、ACM_CTRL_RTS |
| 输入状态 | ACM_CTRL_DCD、ACM_CTRL_DSR、ACM_CTRL_BRK、ACM_CTRL_RI、ACM_CTRL_FRAMING、ACM_CTRL_PARITY、ACM_CTRL_OVERRUN |
CH341 控制线常量以 <usb/host_ch341.h> 中的 CH341_CTO_*、CH341_CTI_* 为准。
4.8.2 Host HID 回调
typedef void (*kbd_notify_callback_t)(unsigned char *report,
unsigned int report_len,
char *data,
unsigned int data_len);
typedef void (*mouse_notify_callback_t)(unsigned char btn_state,
signed char rel_x,
signed char rel_y,
signed char rel_wheel);
键盘回调在 HID URB completion 上下文执行,report 和 data 只在回调期间有效。鼠标回调只在报文长度至少 4 时触发。
4.8.3 Host UVC 类型
enum uvc_stream_type {
UVC_STREAM_TYPE_VIDEO_CAPTURE = 1,
UVC_STREAM_TYPE_VIDEO_OUTPUT = 2,
};
struct uvc_host_frame {
u8 bFrameIndex;
u8 bmCapabilities;
u16 wWidth;
u16 wHeight;
u32 dwMinBitRate;
u32 dwMaxBitRate;
u32 dwMaxVideoFrameBufferSize;
u8 bFrameIntervalType;
u32 dwDefaultFrameInterval;
const u32 *dwFrameInterval;
};
struct uvc_host_format {
u8 type;
u8 index;
u8 bpp;
u32 fcc;
u32 flags;
unsigned int nframes;
const struct uvc_host_frame *frames;
};
struct uvc_host_video {
void *stream;
enum uvc_stream_type type;
unsigned int nformats;
const struct uvc_host_format *formats;
};
struct uvc_host_video 由 usb_host_uvc_get_video() 填充,stream、formats 和 frames 指向驱动内部对象,应用只读且不得释放。在设备保持打开期间,该句柄用于后续格式和流操作。
uvc_status 是 Host UVC 状态通知结构:
struct uvc_status_streaming {
u8 button;
} __packed;
struct uvc_status_control {
u8 bSelector;
u8 bAttribute;
u8 bValue[11];
} __packed;
struct uvc_status {
u8 bStatusType;
u8 bOriginator;
u8 bEvent;
union {
struct uvc_status_control control;
struct uvc_status_streaming streaming;
};
} __packed;
常用类型值:UVC_STATUS_TYPE_CONTROL、UVC_STATUS_TYPE_STREAMING。
4.8.4 Host Mass Storage 类型
struct usb_storage_device {
void *user_data;
unsigned int block_size;
unsigned long long block_count;
char vendor[9];
char product[17];
char version[5];
/* 内部字段 */
u8 index;
void *private_data;
u32 bind_flag;
thread_waiter_t unbind_wait;
};
user_data、block_size、block_count 以及 INQUIRY 字符串由 Host 驱动填充,应用只读。只有 bind_flag == USB_STORAGE_BIND_FLAG 时才能调用 read/write。
4.8.5 NCM Host 统计与回调
typedef void (*cdc_ncm_device_callback_t)(u32 devices_bit);
devices_bit 的 bit 0 表示存在一个已成功 probe 的 CDC NCM 设备。当前 Host 驱动只维护一个活动设备,不使用 bit 1 到 bit 31。
struct usb_host_cdc_ncm_stats {
u32 probe_ok;
u32 probe_fail;
u32 disconnect;
u32 ctrl_get_ntb_params_ok;
u32 ctrl_get_ntb_params_fail;
u32 ctrl_set_ntb_format_ok;
u32 ctrl_set_ntb_format_fail;
u32 ctrl_set_ntb_format_16;
u32 ctrl_set_ntb_format_32;
u32 ctrl_set_ntb_format_fallback_16;
u32 ctrl_set_ntb_input_size_ok;
u32 ctrl_set_ntb_input_size_fail;
u32 ctrl_set_packet_filter_ok;
u32 ctrl_set_packet_filter_fail;
u32 ctrl_set_packet_filter_ignored;
u32 ctrl_set_crc_mode_ok;
u32 ctrl_set_crc_mode_fail;
u32 ctrl_set_crc_mode_on;
u32 ctrl_set_crc_mode_off;
u32 ctrl_get_max_datagram_ok;
u32 ctrl_get_max_datagram_fail;
u32 desc_no_union_fallback;
u32 desc_no_union_fail;
u32 rx_ntb;
u32 rx_ntb16;
u32 rx_ntb32;
u32 rx_frame;
u32 rx_bad_nth;
u32 rx_bad_ndp;
u32 rx_bad_dpe;
u32 rx_frame_oversize;
u32 rx_alloc_fail;
u32 rx_input_fail;
u32 rx_bulk_error;
u32 rx_urb_submit;
u32 rx_urb_submit_fail;
u32 rx_urb_complete;
u32 rx_urb_kill;
u32 rx_urb_status_error;
u32 rx_done_queue_max;
u32 rx_clear_halt;
u32 rx_zero_length;
u32 rx_resubmit_fail;
u32 rx_short_ntb;
u32 rx_bad_nth_sig;
u32 rx_bad_nth_len;
u32 rx_bad_nth_block;
u32 rx_bad_nth_ndp_index;
u32 rx_seq_mismatch;
u32 rx_bad_ndp_sig;
u32 rx_bad_ndp_len;
u32 rx_bad_ndp_next;
u32 rx_ndp_chain_too_deep;
u32 rx_crc_ndp;
u32 rx_crc_frame;
u32 rx_crc_ok;
u32 rx_crc_fail;
u32 rx_crc_strip;
u32 rx_bad_dpe_index;
u32 rx_bad_dpe_len;
u32 rx_dpe_zero_len;
u32 rx_dpe_no_term;
u32 rx_dpe_too_many;
u32 tx_ntb;
u32 tx_ntb16;
u32 tx_ntb32;
u32 tx_frame;
u32 tx_drop;
u32 tx_timeout;
u32 tx_stall;
u32 tx_bulk_error;
u32 tx_queue;
u32 tx_queue_full;
u32 tx_queue_max;
u32 tx_urb_submit;
u32 tx_urb_submit_fail;
u32 tx_urb_complete;
u32 tx_urb_kill;
u32 tx_urb_status_error;
u32 tx_short_write;
u32 tx_clear_halt;
u32 tx_agg_ntb;
u32 tx_agg_frame;
u32 tx_agg_max;
u32 tx_agg_requeue;
u32 tx_agg_timer_flush;
u32 tx_agg_full_flush;
u32 tx_agg_fallback;
u32 tx_crc_frame;
u32 tx_frame_queue;
u32 tx_frame_queue_full;
u32 tx_frame_queue_max;
u32 tx_abort_free_pbuf;
};
统计字段按以下类别使用:
| 前缀/字段 | 含义 |
|---|---|
probe_* / disconnect |
NCM probe、失败阶段和断开次数。 |
ctrl_* |
CDC NCM class request 的成功、失败、NTB16/32 选择、CRC 和 packet filter 结果。 |
desc_* |
CDC Union/interface descriptor 兼容回退结果。 |
rx_* |
NTB/NTH/NDP/DPE 解析、CRC、pbuf 提交和 bulk RX URB 计数。 |
tx_* |
Ethernet 帧入队、NTB 打包、TX URB、聚合、短写和错误计数。 |
tx_agg_* |
TX 聚合批次、定时 flush、满批 flush、重新入队和自动降级计数。 |
usb_host_cdc_ncm_get_stats() 返回驱动内部的只读统计对象,不是快照。设备断开后返回 NULL,此前取得的指针随对象释放而失效;统计字段由 RX/TX worker 或 URB completion 并发更新,普通线程读取可能观察到非原子的混合时刻值,只应用于诊断。
5. 核心接口
统一包含:
#include <usb/usb.h>
5.1 控制器与角色
int usb_core_init(void);
void usb_core_exit(void);
void usb_core_suspend(void);
void usb_core_resume(void);
bool usb_core_is_host_mode(void);
bool usb_core_is_device_mode(void);
#ifdef CONFIG_USB_IS_HOST
void usb_host_init(void);
#endif
#ifdef CONFIG_USB_HOST_FAST_ENUM
void usb_host_fast_disconnect_all_devices(void);
#endif
5.1.1 usb_core_init
功能说明: 按当前 Kconfig 初始化 USB0 控制器、IRQ、DWC2 Host/Gadget 路径和工作队列,并通过平台 glue 调用 USB PHY 初始化与复位。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
| 无 | - | - | 使用编译期平台配置 |
返回值表:
| 返回值 | 说明 |
|---|---|
0 |
初始化成功 |
-EBUSY |
USB Core 已经初始化 |
补充说明:
- 只允许在线程上下文调用。
- 当前实现对平台实例、内存和 probe 失败使用
assert();配置不满足或底层资源初始化失败时可能直接终止,而不是稳定返回负 errno。 - Host 模式下应先调用一次
usb_host_init(),再调用usb_core_init()。 - Device 模式下若使用状态/供电回调,应在
usb_core_init()前注册。 - 计数器、列表和内部单例状态均在首次初始化时建立,不要在未退出旧实例时重复初始化。
5.1.2 usb_core_exit
功能说明: 移除 DWC2 Host/Gadget 实例,停止工作队列,释放 IRQ、PHY 时钟和 Core 内存。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
| 无 | - | - | 释放当前 USB0 Core |
返回值表:
| 返回值 | 说明 |
|---|---|
| 无 | 接口不返回状态 |
补充说明:
- 只允许在线程上下文调用。
- 调用前必须停止应用 I/O,并完成 Gadget cleanup 或 Host 类资源 close。
- 未初始化时调用会触发
assert()。 - 退出后内部状态/供电回调指针被清空,再次初始化前需要重新注册。
5.1.3 usb_core_suspend / usb_core_resume
功能说明: 在系统低功耗流程中挂起或恢复 USB PHY/控制器。
参数表:无。
返回值表:无。
补充说明:
- 仅在线程上下文调用。
usb_core_suspend()会按 Device remote wakeup 状态配置 PHY。usb_core_resume()恢复 PHY;Host 模式且端口没有已连接设备时会重新使能 VBUS。- 这两个接口处理 USB 控制器生命周期,并通过平台 glue 调用 PHY suspend/resume;PHY 内部寄存器和 CPM 行为见
USB PHY HAL/LL 文档。 - 这两个接口不替代系统电源管理或应用业务状态保存。
5.1.4 usb_core_is_host_mode / usb_core_is_device_mode
功能说明: 查询当前 DWC2 控制器实际模式。
参数表:无。
返回值表:
| 返回值 | 说明 |
|---|---|
true |
当前处于对应模式 |
false |
Core 未初始化或当前不是对应模式 |
补充说明:
- OTG 模式下结果可能在运行时变化,应在需要时重新查询。
- 固定角色模式下结果通常与 Kconfig 一致。
5.1.5 usb_host_init
功能说明: 初始化 Host 根 Hub,并注册当前启用的通用、HID、CDC ACM/NCM、CH341、UVC 和 Mass Storage Host 驱动。
参数表:无。
返回值表:无。
补充说明:
- 只应在 Host 或 Dual-Role 构建中调用。
- 必须在
usb_core_init()之前调用;示例中的顺序是usb_host_init()→ 注册应用回调 →usb_core_init()。 - 接口没有重复调用保护,应由系统初始化代码保证只调用一次。
- Host 驱动注册目前没有公开的反注册接口;应用生命周期结束时应先关闭所有类设备,再调用
usb_core_exit()。
5.1.6 usb_host_fast_disconnect_all_devices
功能说明: 断开根 Hub 下全部下游设备,供快速重新枚举或拓扑复位使用。
参数表:无。
返回值表:无。
补充说明:
- 只允许在线程上下文调用。
- 内部持有
usb_bus_list_lock,并可能调用睡眠的usb_disconnect();禁止在中断或 USB callback 中调用。 - 函数仅在
CONFIG_USB_HOST_FAST_ENUM=y时声明和构建。
5.2 Device 状态与 VBUS
统一包含:
#include <usb/usb.h>
void usb_device_set_state_callback(usb_state_callback_t state_cb);
void usb_device_set_power_callback(usb_power_callback_t power_cb);
const char *usb_state_string(enum usb_device_state state);
const char *usb_power_string(enum usb_device_power type);
void usb_device_vbus_status_update(int status);
这些接口仅在 CONFIG_USB_IS_DEVICE=y 时可用。
5.2.1 usb_device_set_state_callback
功能说明: 注册 USB Device 枚举状态变化回调。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
state_cb |
usb_state_callback_t |
输入 | 回调;传 NULL 可注销 |
返回值表:无。
补充说明:
- 应在
usb_core_init()前注册,初始化后注册也可立即生效并替换已有回调。 - 回调可能在 UDC/HCD 的中断或工作路径中执行,不应阻塞。
- 状态从
USB_STATE_CONFIGURED变为USB_STATE_NOTATTACHED表示设备不再可用。
5.2.2 usb_device_set_power_callback
功能说明: 注册 Device 供电类型/电流变化回调。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
power_cb |
usb_power_callback_t |
输入 | 接收 enum usb_device_power 和 mA 的回调;NULL 注销 |
返回值表:无。
补充说明:
mA是当前 VBUS draw 值,不代表应用一定能持续消耗该电流。- 断开时供电类型回到
USB_POWER_UNKNOWN。
5.2.3 usb_state_string / usb_power_string
功能说明: 将枚举值转换为静态只读字符串。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
state / type |
枚举值 | 输入 | 待转换状态 |
返回值表:
| 返回值 | 说明 |
|---|---|
| 非空字符串 | 对应名称 |
"UNKNOWN" |
状态值越界 |
返回指针由静态表持有,调用方不得释放或修改。
5.2.4 usb_device_vbus_status_update
功能说明: 向 UDC 注入 VBUS 有效/无效状态。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
status |
int |
输入 | 0 表示 VBUS 无效,非 0 表示有效 |
返回值表:无。
补充说明:
- 主要用于
CONFIG_USB_DISABLE_VBUS=y或外部 VBUS 检测方案。 - Core 未初始化或当前不是 Device 模式时直接返回,不改变任何状态。
- 应用应把中断中的边沿事件转化为一次短调用;函数可能进入 UDC connect/disconnect 状态机。
6. Gadget 接口
所有 Gadget 类接口都要求:
- 先成功调用
usb_core_init()。 - 只初始化当前 Kconfig 选中的一个 Gadget 类/复合类。
- 初始化配置、描述符和回调函数指针在 Gadget 生命周期内有效。
- 读、写、wait 等阻塞接口在线程上下文调用。
6.1 CDC ACM Serial
包含:
#include <usb/gadget_serial.h>
int gadget_serial_init(const struct gadget_id *id,
const struct usb_cdc_serial_param *param,
connect_callback_t connect_cb,
serial_param_callback_t serial_cb);
void gadget_serial_cleanup(void);
int gadget_serial_read(uint8_t *buf, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_serial_write(const uint8_t *buf, uint32_t count,
uint32_t block, uint32_t timeout_ms);
int gadget_serial_break_ctl(int duration);
int gadget_serial_flush_chars(uint32_t timeout_ms);
int gadget_serial_clean_write(void);
int gadget_serial_get_connect_status(void);
int gadget_serial_wait_connect(uint32_t timeout_ms);
int gadget_serial_get_param(struct usb_cdc_serial_param *cdc_param);
6.1.1 gadget_serial_init
参数:
| 参数 | 说明 |
|---|---|
id |
必填;VID/PID |
param |
可选;NULL 时使用 115200/1 stop/no parity/8 data |
connect_cb |
可选;连接/断开回调 |
serial_cb |
可选;Host 修改 line coding 后回调 |
返回:
| 返回值 | 说明 |
|---|---|
0 |
复合驱动注册成功 |
| 负 errno | composite probe 或绑定失败 |
| 不返回 | id == NULL 时当前实现 panic() |
6.1.2 gadget_serial_read
从 Host 发往 Device 的 CDC 数据队列读取。
| 返回值 | 说明 |
|---|---|
| 正数 | 实际读取字节数 |
0 |
count == 0 或 buf == NULL |
-ENODEV |
Gadget 未初始化或正在 cleanup |
-ENOLINK |
未连接 |
-EAGAIN |
block == 0 且无数据 |
-ETIMEDOUT |
阻塞等待超时 |
不足一个完整包的数据可能被保留,下一次调用继续读取。
6.1.3 gadget_serial_write
向 Host 发送 CDC 数据。
| 返回值 | 说明 |
|---|---|
| 正数 | 已复制并提交到发送队列的字节数 |
0 |
count == 0 或 buf == NULL |
-ENODEV / -ENOLINK |
未初始化/已断开 |
-EAGAIN |
block == 0 且发送队列满 |
-ETIMEDOUT |
等待发送空间超时 |
返回正数表示已接受的数据长度;应用可用 gadget_serial_flush_chars() 等待实际发送完成。
6.1.4 控制与状态接口
| 接口 | 说明 | 返回值 |
|---|---|---|
gadget_serial_break_ctl(duration) |
发送 CDC break,duration 单位由底层接入;0 通常结束 break |
0 或负 errno |
gadget_serial_flush_chars(timeout_ms) |
等待发送请求队列清空 | 0、-ETIMEDOUT、-ENOLINK、-ENODEV |
gadget_serial_clean_write() |
取消并清空待发送队列 | 0 或负 errno |
gadget_serial_get_connect_status() |
查询连接 | 1/0 或负 errno |
gadget_serial_wait_connect(timeout_ms) |
等待连接 | 0 成功,超时/断开/未初始化返回负 errno |
gadget_serial_get_param(cdc_param) |
获取当前 line coding 快照 | 0 或 -ENODEV |
gadget_serial_cleanup() 会解除 composite 驱动并释放端点/复合对象;调用前必须停止应用读写线程。
6.2 HID
包含:
#include <usb/gadget_hid.h>
int gadget_hid_init(const struct gadget_id *id,
const struct hid_report_descriptor *report,
struct hid_callback *hid_cb);
void gadget_hid_cleanup(void);
int gadget_hid_read(uint8_t *buffer, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_hid_write(const uint8_t *buffer, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_hid_get_connect_status(void);
int gadget_hid_wait_connect(uint32_t timeout_ms);
一个配置内提供两个 HID function 时:
int gadget_hid_init2(const struct gadget_id *id,
const struct hid_report_descriptor *report0,
struct hid_callback *hid_cb0,
const struct hid_report_descriptor *report1,
struct hid_callback *hid_cb1);
void gadget_hid_cleanup2(void);
void gadget_hid_get2(struct usb_function **hid0,
struct usb_function **hid1);
int gadget_hid_read2(struct usb_function *hid, uint8_t *buffer,
uint32_t count, uint8_t block,
uint32_t timeout_ms);
int gadget_hid_write2(struct usb_function *hid,
const uint8_t *buffer, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_hid_get_connect_status2(struct usb_function *hid);
int gadget_hid_wait_connect2(struct usb_function *hid,
uint32_t timeout_ms);
行为:
gadget_hid_init()要求id和report非空;不满足时当前实现panic()。gadget_hid_read()从 Host OUT 报告队列读取,返回实际字节数;无数据和block == 0返回-EAGAIN。gadget_hid_write()一次只允许一个 HID IN 传输在途;写入长度不超过report_length,返回实际提交长度。gadget_hid_write()的block != 0和完整队列场景会等待上一个 HID IN 完成。gadget_hid_get2()只能在gadget_hid_init2()的 composite bind 完成后使用;输出句柄由驱动持有。- 双 HID 的
*2接口必须在其他线程调用gadget_hid_get2()获得 function 句柄。
6.2.1 HID Wakeup
包含:
#include <usb/gadget_hid_wakeup.h>
int gadget_hid_wakeup_init(const struct gadget_id *id,
const struct hid_report_descriptor *report,
struct hid_callback *hid_cb,
suspend_callback_t suspend_cb);
void gadget_hid_wakeup_cleanup(void);
int gadget_hid_read(uint8_t *buffer, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_hid_write(const uint8_t *buffer, uint32_t count,
uint8_t block, uint32_t timeout_ms);
int gadget_hid_get_connect_status(void);
int gadget_hid_wait_connect(uint32_t timeout_ms);
int gadget_hid_get_suspend_status(void);
int gadget_hid_wait_resume(uint32_t timeout_ms);
int gadget_hid_wakeup(void);
唤醒相关接口:
| 接口 | 说明 |
|---|---|
gadget_hid_get_suspend_status() |
返回 1 表示 suspend;未初始化返回负 errno |
gadget_hid_wait_resume(timeout_ms) |
等待 suspend 结束;成功返回 0,超时/断开返回负 errno |
gadget_hid_wakeup() |
请求 Device remote wakeup;成功返回 0 |
唤醒失败时示例常通过 -EHOSTDOWN 检测 Host 已经停止调度;应用应唤醒后尽快发送一帧 HID 数据,避免 Host 再次进入睡眠。
6.3 Bulk 与 WinUSB
6.3.1 Generic Bulk
包含:
#include <usb/gadget_bulk.h>
int gadget_bulk_init(const struct gadget_id *id,
connect_callback_t connect_cb);
void gadget_bulk_cleanup(void);
int gadget_bulk_read(uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_bulk_write(const uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_bulk_flush_data(uint32_t timeout_ms);
int gadget_bulk_get_connect_status(void);
int gadget_bulk_wait_connect(uint32_t timeout_ms);
行为:
- 双向 bulk 通道使用内部固定大小 request 池;每次读写返回本次实际搬移字节数。
len == 0或 buffer 为NULL返回 0。- 未连接返回
-ENOLINK,未初始化返回-ENODEV,非阻塞无数据/无 TX request 返回-EAGAIN,等待超时返回-ETIMEDOUT。 gadget_bulk_write()返回正数表示数据已复制到 TX request 并提交,不保证 Host 已收完;gadget_bulk_flush_data()等待活动 TX request 完成。- 当前内部单 request 大小为 8192 字节;大于该值的写入会被分成多个 request,但调用方仍应检查返回值并处理短写。
6.3.2 WinUSB
包含:
#include <usb/gadget_winusb.h>
int gadget_winusb_init(const struct gadget_id *id,
struct winusb_descriptor *des,
connect_callback_t connect_cb);
void gadget_winusb_cleanup(void);
int gadget_winusb_read(uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_winusb_write(const uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_winusb_flush_data(uint32_t timeout_ms);
int gadget_winusb_get_connect_status(void);
int gadget_winusb_wait_connect(uint32_t timeout_ms);
WinUSB 的 bulk I/O 和 Generic Bulk 具有相同的阻塞、返回值、flush 和缓冲区语义。区别是初始化必须提供 OS String/WinUSB 描述符;id 或 des 为空时当前实现 panic()。
6.4 Printer
包含:
#include <usb/gadget_printer.h>
int gadget_printer_init(const struct gadget_id *id,
connect_callback_t connect_cb);
void gadget_printer_cleanup(void);
int gadget_printer_read(uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_printer_write(const uint8_t *buffer, uint32_t len,
uint8_t block, uint32_t timeout_ms);
int gadget_printer_flush_data(uint32_t timeout_ms);
int gadget_printer_get_connect_status(void);
int gadget_printer_wait_connect(uint32_t timeout_ms);
int gadget_printer_get_status(u8 *status);
int gadget_printer_set_status(u8 status);
Printer IO 语义与 Generic Bulk 一致。状态接口使用:
| 常量 | 值 | 说明 |
|---|---|---|
PRINTER_STATUS_NOT_ERROR |
0x08 |
无错误 |
PRINTER_STATUS_SELECTED |
0x10 |
已选中 |
PRINTER_STATUS_PAPER_EMPTY |
0x20 |
缺纸 |
gadget_printer_get_status() 要求 status 非空;当前实现使用 assert() 校验。
6.5 Display
包含:
#include <usb/gadget_display.h>
int gadget_display_init(const struct gadget_id *id,
struct display_edid *edid,
struct display_specific_des *des,
connect_callback_t connect_cb);
void gadget_display_cleanup(void);
int gadget_display_get_buffer(struct display_buffer *display_buffer,
uint32_t timeout_ms);
int gadget_display_put_buffer(struct display_buffer *display_buffer);
int gadget_display_get_connect_status(void);
int gadget_display_wait_connect(uint32_t timeout_ms);
int gadget_display_get_status(u8 *status);
int gadget_display_set_status(u8 status);
缓冲区流程:
gadget_display_get_buffer()从 Host 文本/控制数据 OUT 队列取得一块 request。- 驱动填充
buf、length、actual和内部private_data。 - 应用读取
buf[0..actual-1]后必须调用gadget_display_put_buffer()归还 request。 put_buffer()清空display_buffer的描述字段。
timeout_ms == 0 时无数据返回 -EAGAIN;未连接返回 -ENOLINK。edid、des 或其 buffer/size 无效时初始化会 panic()。
6.6 Mass Storage
包含:
#include <usb/gadget_mass_storage.h>
int gadget_mass_storage_init(const struct gadget_id *id,
struct fsg_config *config);
void gadget_mass_storage_cleanup(void);
功能说明: 把应用提供的 LUN 配置暴露为 USB Mass Storage 设备。
参数/返回:
| 参数 | 说明 |
|---|---|
id |
VID/PID,必填 |
config |
LUN、介质回调、只读/可移动/CD-ROM、块大小、扇区数、序列号和连接回调 |
返回:
| 返回值 | 说明 |
|---|---|
0 |
composite probe 成功 |
| 负 errno | 描述符、端点或绑定失败 |
| 不返回 | 配置为空或 LUN 回调/大小无效时 panic() |
补充说明:
nluns > 8时当前实现截断为 8 并打印提示。read_callback/write_callback的偏移量是字节偏移,不是扇区号。- 回调在 FSG 工作线程中执行,应快速完成。
USB_GADGET_STORAGE_NUM_BUFFERS控制内部缓冲数量,范围 2 到 8。
6.7 网络类 Gadget(RNDIS/NCM)
6.7.1 RNDIS
包含:
#include <usb/gadget_rndis.h>
int gadget_rndis_init(const struct gadget_id *id,
struct usb_rndis_param *param);
void gadget_rndis_cleanup(void);
功能说明: 创建 RNDIS USB 网络设备,并绑定到 lwIP 网络接口。
返回:
| 返回值 | 说明 |
|---|---|
0 |
成功 |
| 负 errno | composite 或 lwIP 接口初始化失败 |
| 不返回 | id == NULL 时 panic() |
补充说明:
- 要求
CONFIG_MIDDLEWARE_NET_LWIP=y;RNDIS 本身不依赖LIB_RING_MEM。 - 应用应在
gadget_rndis_init()前初始化 lwIP,例如调用tcpip_init(NULL, NULL)。 param必须非空,并提供有效的ipaddr、netmask、gw字符串;初始化只复制结构体,不复制字符串指向的存储。当前实现虽允许param == NULL进入函数,但后续 bind 会解引用这些 IP 指针,因此空参数不是可用的默认配置。- cleanup 会注销 composite、清零参数并关闭内部 DTRNG(若使用)。
6.7.2 NCM
包含:
#include <usb/gadget_ncm.h>
gadget_ncm.h 声明不按 Kconfig 条件裁剪,但只有启用 CONFIG_USB_GADGET_NCM 时对应实现才参与链接。
int gadget_ncm_init(const struct gadget_id *id,
struct usb_ncm_param *param);
void gadget_ncm_cleanup(void);
struct netif *gadget_ncm_get_netif(void);
int gadget_ncm_set_link(bool up);
int gadget_ncm_set_ipv4(const char *ip, const char *netmask,
const char *gw);
int gadget_ncm_set_ipv6(const char *ip6, int prefix_len);
int gadget_ncm_clear_ipv6(void);
功能说明:
创建一个 CDC NCM Ethernet function,在 bind 时建立名为 ue* 的 lwIP netif,并把 Device 侧以太网流量通过 NCM function 与 lwIP pbuf 连接。应用不直接收发 USB request 或 pbuf,而是使用 lwIP socket/API 并经 netif 转发。
gadget_ncm_init():
| 返回值 | 说明 |
|---|---|
0 |
composite probe 成功 |
| 负 errno | composite、netif、端点或内存初始化失败 |
| 不返回 | id == NULL 时当前实现 panic() |
初始化要求:
- 先成功调用
usb_core_init()。 - 在调用本函数前启动 lwIP TCP/IP 线程,可使用
im_net_lwip_core_init()或当前平台等价的一次性初始化。 param应非空,且ipaddr、netmask、gw指向有效的 IPv4 字符串;mtu == 0时使用 1500。dev_mac/host_mac全零时驱动生成默认 MAC;非零 MAC 由应用保证唯一、单播且两端不同。idVendor/idProduct来自id。
netif 与链路接口:
| 接口 | 行为 | 返回 |
|---|---|---|
gadget_ncm_get_netif() |
返回当前 active NCM netif;对象归驱动所有,不能释放或长期缓存跨 cleanup | 指针;未 bind 时为 NULL |
gadget_ncm_set_link(true) |
把 netif link 置 up;USB 数据接口尚未 configured/connected 时拒绝 | 0;无 netif 为 -ENODEV;未连接为 -ENOLINK;其他 lwIP err_t |
gadget_ncm_set_link(false) |
把 netif link 置 down,可随时在 netif 存在时调用 | 0 或负值/lwIP err_t |
USB 完成枚举并选择 NCM data interface alt 1 后,如果 netif 已 up,驱动会自动把 link 置 up;因此应用通常可以先配置 IP,再等待连接。set_link(true) 可用于设备连接后由应用显式恢复链路,但应用需要重试直到 USB data interface 已就绪。
IP 配置接口:
| 接口 | 行为 | 返回 |
|---|---|---|
gadget_ncm_set_ipv4(ip, netmask, gw) |
解析三个 IPv4 字符串,设置 netif 地址和网关并把 netif 置 up;不会在 USB 未连接时强制 link up | 0;无 netif -ENODEV;解析失败 -EINVAL;lwIP 配置失败时返回 err_t |
gadget_ncm_set_ipv6(ip6, prefix_len) |
设置 IPv6 地址槽 1、valid/preferred 生命周期和 preferred 状态,并把 netif 置 up;只接受 /64 |
0;无 netif -ENODEV;非 /64 或地址非法 -EINVAL;未启用 LWIP_IPV6 返回 -EOPNOTSUPP |
gadget_ncm_clear_ipv6() |
清除 IPv6 地址槽 1,不关闭 netif,也不清除 IPv4 | 成功 0;其他同 set_ipv6() |
gadget_ncm_set_ipv4() / gadget_ncm_set_ipv6() 修改的是当前 netif 配置,不写入持久化存储。地址在 NCM Gadget 生命周期内保持;USB disconnect/reconnect 不会重建 netif,因此地址仍保留。gadget_ncm_cleanup() 会注销 composite、停止 RX worker、移除 netif 并清零内部 NCM 参数;调用前应停止使用 gadget_ncm_get_netif() 返回值的应用线程。
6.8 UAC1
包含:
#include <usb/gadget_uac1.h>
int gadget_uac1_init(const struct gadget_id *id,
const struct uac1_params *params);
void gadget_uac1_cleanup(void);
int gadget_uac1_get_connect_status(void);
int gadget_uac1_wait_connect(u32 timeout_ms);
int gadget_uac1_wait_playback_start(u32 timeout_ms, u32 *rate);
int gadget_uac1_wait_capture_start(u32 timeout_ms, u32 *rate);
int gadget_uac1_playback_read(u8 *buffer, u32 len);
int gadget_uac1_capture_write(u8 *buffer, u32 len);
行为:
gadget_uac1_playback_read()从 Host→Device 的播放 ring 读取当前可用 PCM 数据,不阻塞。gadget_uac1_capture_write()把 Device→Host 的采集 PCM 写入 ring,不阻塞。- 这两个接口在未连接时返回
-ENOLINK,端点未启用/无数据格式时返回-ENOTCONN,参数为空返回-EINVAL,成功返回实际字节数;ring 暂时无数据时可能返回 0。 wait_playback_start()/wait_capture_start()成功时返回 0,并在rate != NULL时写入当前采样率。params必须保持到 Gadget cleanup;若字段指向支持采样率数组,这些数组也必须保持有效。p_chmask == 0或等价配置用于关闭 playback;c_chmask == 0用于关闭 capture。
6.9 UVC
包含:
#include <usb/gadget_uvc.h>
void gadget_uvc_init(const struct gadget_id *id,
const struct uvc_device_config *config,
const struct uvc_callback *callback);
void gadget_uvc_cleanup(void);
int gadget_uvc_write(struct uvc_buffer *buf,
uint8_t block, uint32_t timeout_ms);
int gadget_uvc_get_connect_status(void);
int gadget_uvc_wait_connect(uint32_t timeout_ms);
int gadget_uvc_wait_stream(uint32_t timeout_ms,
struct uvc_video_format *format);
回调:
typedef void (*format_callback_t)(const struct uvc_video_format *format);
typedef int (*stream_callback_t)(int enable);
typedef int (*fparam_callback_t)(const struct uvc_control_request *req);
gadget_uvc_init() 返回 void,不能通过返回值判断失败。id 或 config 无效会触发断言/panic;初始化成功后应用等待 UVC 连接/stream 事件。
gadget_uvc_write() 行为:
| 条件 | 结果 |
|---|---|
buf/buf->mem 为空或 length == 0 |
-EINVAL |
| 未初始化 | -ENODEV |
| 已连接但尚未 stream on | -ENOTCONN |
| 已断开 | -ENOLINK |
内部 queue 满且 block == 0 |
-EAGAIN |
| 阻塞等待超时 | -ETIMEDOUT |
| 成功 | 0,buffer 进入 queued/active 状态 |
提交成功后,buffer 的所有权交给 UVC function;应用必须等待 buf->complete 或 buf->state 为 UVC_BUF_STATE_DONE/ERROR 后才可以重用。
format_cb 在 Host 选择新格式时通知应用;stream_cb(enable) 在 Host 执行 stream on/off 时调用;param_cb 处理 UVC 控制请求。wait_stream() 在 streaming 建立后返回,并把当前格式复制到可选的 format 参数。
6.10 复合 Gadget
6.10.1 Serial + HID
包含:
#include <usb/gadget_serial_hid.h>
int gadget_serial_hid_init(const struct gadget_id *id,
const struct hid_report_descriptor *report,
const struct usb_cdc_serial_param *param,
const struct cdc_hid_callback *callback);
void gadget_serial_hid_cleanup(void);
cdc_hid_callback 包含:
struct cdc_hid_callback {
connect_callback_t hid_connect_cb;
hid_request_callback_t hid_request_cb;
connect_callback_t cdc_connect_cb;
serial_param_callback_t serial_cb;
};
初始化后可使用 CDC Serial 和 HID 两组共用接口:
gadget_serial_break_ctl()gadget_serial_flush_chars()gadget_serial_clean_write()gadget_serial_read()/gadget_serial_write()gadget_serial_get_connect_status()/gadget_serial_wait_connect()gadget_serial_get_param()gadget_hid_read()/gadget_hid_write()gadget_hid_get_connect_status()/gadget_hid_wait_connect()
6.10.2 UVC + HID
包含:
#include <usb/gadget_uvc_hid.h>
void gadget_uvc_hid_init(const struct gadget_id *id,
const struct uvc_device_config *dev_config,
const struct hid_report_descriptor *report,
const struct uvc_hid_callback *callback);
void gadget_uvc_hid_cleanup(void);
uvc_hid_callback 提供 UVC callback、HID connect callback 和 HID request callback。初始化后可使用 gadget_uvc_* 与 gadget_hid_* 共用接口。
struct uvc_hid_callback {
struct uvc_callback *uvc_cb;
connect_callback_t hid_connect_cb;
hid_request_callback_t hid_request_cb;
};
6.10.3 UVC + CDC
包含:
#include <usb/gadget_uvc_serial.h>
void gadget_uvc_serial_init(
const struct gadget_id *id,
const struct uvc_device_config *uvc_config,
const struct usb_cdc_serial_param *cdc_param,
const struct uvc_cdc_callback *callback);
void gadget_uvc_serial_cleanup(void);
uvc_cdc_callback 包含 UVC callback、CDC connect callback 和 CDC param callback。初始化后可使用 gadget_uvc_* 与 gadget_serial_*。
struct uvc_cdc_callback {
struct uvc_callback *uvc_cb;
connect_callback_t serial_connect_cb;
serial_param_callback_t serial_param_cb;
};
6.10.4 多路 UVC
包含:
#include <usb/gadget_uvc_n.h>
void gadget_uvc_n_init(const struct gadget_id *id,
const struct uvc_device_config *config,
uint32_t config_count);
void gadget_uvc_n_cleanup(void);
int gadget_uvc_n_write(uint32_t uvc_id, struct uvc_buffer *buf,
uint8_t block, uint32_t timeout_ms);
int gadget_uvc_n_get_connect_status(uint32_t uvc_id);
int gadget_uvc_n_wait_connect(uint32_t uvc_id, uint32_t timeout_ms);
int gadget_uvc_n_wait_stream(uint32_t uvc_id, uint32_t timeout_ms,
struct uvc_video_format *format);
每个 uvc_device_config 内嵌自己的 callback,回调的第一个参数是 uvc_id。config_count 必须不超过 UVC_MAX_COUNT(4)。
typedef void (*uvc_n_format_callback_t)(
uint32_t uvc_id, const struct uvc_video_format *format);
typedef int (*uvc_n_stream_callback_t)(uint32_t uvc_id, int enable);
typedef int (*uvc_n_fparam_callback_t)(
uint32_t uvc_id, const struct uvc_control_request *req);
typedef void (*uvc_n_connect_callback_t)(uint32_t uvc_id, int connect);
struct uvc_callback {
uvc_n_format_callback_t format_cb;
uvc_n_stream_callback_t stream_cb;
uvc_n_connect_callback_t connect_cb;
uvc_n_fparam_callback_t param_cb;
};
6.10.5 UVC + UAC1
包含:
#include <usb/gadget_uvc_uac1.h>
void gadget_uvc_uac1_init(
const struct gadget_id *id,
const struct uvc_device_config *uvc_config,
const struct uvc_callback *uvc_callback,
const struct uac1_params *uac1_params);
void gadget_uvc_uac1_cleanup(void);
初始化后可同时使用 gadget_uvc_* 和 gadget_uac1_*。
6.10.6 多路 UVC + UAC1
包含:
#include <usb/gadget_uvc_n_uac1.h>
void gadget_uvc_n_uac1_init(
const struct gadget_id *id,
const struct uvc_device_config *config,
uint32_t config_count,
const struct uac1_params *uac1_params);
void gadget_uvc_n_uac1_cleanup(void);
初始化后可同时使用 gadget_uvc_n_* 和 gadget_uac1_*,最多支持 4 路 UVC。
6.10.7 Display + UAC1 + HID
包含:
#include <usb/gadget_display_uac1_hid.h>
int gadget_display_audio_hid_init(
const struct gadget_id *id,
struct display_params *display_params,
struct uac1_params *uac1_params,
struct hid_params *hid_params);
void gadget_display_audio_hid_cleanup(void);
初始化后可同时使用:
gadget_display_*gadget_uac1_*gadget_hid_*
display_params 包含 EDID、厂商自定义描述符和 display connect callback;hid_params 包含 report descriptor、connect callback 和 request callback。所有指针和描述符必须在 cleanup 前有效。
struct display_params {
struct display_edid edid;
struct display_specific_des des;
connect_callback_t connect_cb;
};
struct hid_params {
struct hid_report_descriptor des;
connect_callback_t connect_cb;
hid_request_callback_t request_cb;
};
7. Host 接口
Host 类接口的共同约束:
- 先调用
usb_host_init()注册 Host Core 和类驱动,再调用usb_core_init()启动控制器。 - 在
usb_core_init()前注册类级插拔回调。 - 枚举完成后,类 bitmap 回调给出当前可用设备;每个 bit 对应一个类设备索引。
- open/read/write/set 等接口在线程上下文调用,可能睡眠。
- close 前停止应用 I/O 线程;close 会等待该 minor 上正在执行的类操作结束。
7.1 Host 管理
Host 核心接口已列于 5.1:
void usb_host_init(void);
void usb_host_fast_disconnect_all_devices(void); /* USB_HOST_FAST_ENUM */
典型枚举事件顺序:
USB 设备插入
-> Host Hub 线程检测端口变化
-> USB Core 分配地址、读取描述符
-> 类驱动 probe
-> 更新 devices_bit 并调用类 register_callback
-> 应用按 bitmap 调用类 open
设备移除时反向执行 disconnect/close 流程。bitmap 回调可能在 Hub 工作线程执行,不应在回调中执行长时间阻塞操作。
7.2 CDC ACM 与 NCM
7.2.1 CDC ACM
包含:
#include <usb/host_cdc_acm.h>
void usb_host_cdc_acm_register_callback(
cdc_acm_device_callback_t callback);
u32 usb_host_cdc_acm_get_devices_bit(void);
int usb_host_cdc_acm_open(u8 id,
cdc_acm_notify_callback_t callback);
void usb_host_cdc_acm_close(u8 id);
int usb_host_cdc_acm_set_line_coding(
u8 id, struct cdc_acm_line_coding *param, int timeout);
int usb_host_cdc_acm_set_control(u8 id, u16 ctrlout, int timeout);
int usb_host_cdc_acm_send_break(u8 id, int state, int timeout);
int usb_host_cdc_acm_read(u8 id, void *data, int len, int timeout);
int usb_host_cdc_acm_write(u8 id, void *data, int len, int timeout);
id 范围为 0 到 ACM_MINORS - 1,当前为 0 到 31。
7.2.2 回调与枚举
typedef void (*cdc_acm_device_callback_t)(u32 devices_bit);
typedef void (*cdc_acm_notify_callback_t)(u8 id, void *buf, int length);
device_callback(devices_bit)在 CDC ACM 设备插入/移除后触发。notify_callback(id, buf, length)接收 CDC 控制接口的 notification,例如 serial state。buf通常按struct usb_cdc_notification解析,notification 数据区从dr + 1开始。notify_callback可能在 URB completion 上下文运行,必须短小且 ISR-safe。
7.2.3 usb_host_cdc_acm_open
| 返回值 | 说明 |
|---|---|
0 |
成功,提交 notification URB |
-ENODEV |
id 越界或设备不存在 |
-EBUSY |
该 minor 已打开 |
-EIO |
notification URB 提交失败 |
应用必须在设备移除或业务结束时调用 usb_host_cdc_acm_close(id)。
7.2.4 usb_host_cdc_acm_close
- 成功打开的设备必须显式关闭。
- 关闭时等待
acm->used归零,因此会阻塞到所有正在执行的 control/bulk 操作结束。 id越界或设备未打开时直接返回。- 不要在 read/write 的应用线程自身持有锁或等待 join 时从同一线程关闭。
7.2.5 line coding、控制线和 break
| 接口 | 参数 | 返回 |
|---|---|---|
set_line_coding |
param 必须非空;字段转换为 CDC SET_LINE_CODING |
成功 0,参数/设备/USB 错误返回负 errno |
set_control |
ctrlout 使用 ACM_CTRL_DTR、ACM_CTRL_RTS |
成功 0,否则负 errno |
send_break |
state != 0 发送约 65535 ms break,否则结束 break |
成功 0,否则负 errno |
timeout <= 0 按永久等待处理。
7.2.6 读写
| 返回值 | 说明 |
|---|---|
| 正数 | 实际传输字节数 |
-EINVAL |
data == NULL、len == 0 或 id 参数非法 |
-ENODEV |
未打开或设备已移除 |
-ETIMEDOUT |
底层 USB 等待超时 |
| 其他负 errno | Host Core/URB 错误 |
读/写是同步 USB 传输,按设备端点类型使用 bulk 或 interrupt pipe。timeout <= 0 表示永久等待;超时后即使实际传输了部分数据,底层状态也可能返回错误,应用应结合返回值终止本次操作。
7.2.7 NCM 主机接口
包含:
#include <usb/host_cdc_ncm.h>
host_cdc_ncm.h 中的类型和接口只在 CONFIG_USB_HOST_CDC_NCM 定义时可见。
void usb_host_cdc_ncm_register_callback(
cdc_ncm_device_callback_t callback);
u32 usb_host_cdc_ncm_get_devices_bit(void);
const struct usb_host_cdc_ncm_stats *usb_host_cdc_ncm_get_stats(void);
int usb_host_cdc_ncm_set_ipv4(const char *ip, const char *netmask,
const char *gw);
int usb_host_cdc_ncm_set_ipv6(const char *ip6, int prefix_len);
int usb_host_cdc_ncm_clear_ipv6(void);
int usb_host_cdc_ncm_start_dhcp(void);
int usb_host_cdc_ncm_set_tx_agg(u16 datagrams, bool auto_fallback);
int usb_host_cdc_ncm_reset_tx_agg(void);
int usb_host_cdc_ncm_ra_start(const char *prefix_cidr);
int usb_host_cdc_ncm_ra_stop(void);
int usb_host_cdc_ncm_ra_once(void);
void usb_host_cdc_ncm_dump_stats(void);
void usb_host_cdc_ncm_dump_params(void);
void usb_host_cdc_ncm_dump_quirks(void);
void usb_host_cdc_ncm_dump_tx_agg(void);
void usb_host_cdc_ncm_dump_ra(void);
CDC NCM Host 驱动匹配 CDC Communication Class / NCM subclass,自动选择 data interface alt setting、配置 NTB16 或 NTB32、启动 bulk RX/TX worker 并注册 lwIP netif。应用不需要 open/close 设备;枚举成功即开始网络链路,移除设备时驱动自动停止。
NCM 设备要成功 probe,需要满足:
- communication interface 的设备类为 CDC Communication,subclass 为 NCM,并支持 NTB parameters。
- descriptor 中存在 CDC Union 或可用的 data-interface fallback;当前 quirk 表为空,因此标准 CDC Union 是默认要求。
- data interface 同时提供 bulk IN 和 bulk OUT endpoint。
- communication interface 提供 interrupt IN status endpoint。
- 设备声明并接受 NTB16 或 NTB32;优先 NTB16,仅支持 32 bit 格式时使用 NTB32。
初始化顺序:
- 以
im_net_lwip_core_init()初始化 lwIP TCP/IP 线程。 - 调用
usb_host_init()注册 Host Core 和 CDC NCM driver。 - 调用
usb_host_cdc_ncm_register_callback()注册设备 bitmap 回调。 - 调用
usb_core_init()。 - 在 bit 0 置位后配置静态 IP、DHCP、IPv6 或 RA。
设备 bitmap:
| 接口 | 行为 |
|---|---|
usb_host_cdc_ncm_register_callback(callback) |
保存回调并立即用当前 devices_bit 调用一次;传 NULL 只注销回调,不关闭当前设备。 |
usb_host_cdc_ncm_get_devices_bit() |
返回当前 bitmap,成功 probe 时 bit 0 为 1;当前实现只支持一个活动 NCM 设备。 |
回调在 USB Hub 枚举或设备移除工作线程中执行。回调中不要等待 DHCP、IPv6 地址配置或网络 I/O;应唤醒应用线程处理。
IPv4 与 DHCP:
| 接口 | 行为 | 返回 |
|---|---|---|
set_ipv4(ip, netmask, gw) |
停止当前 DHCP,设置静态地址/掩码/网关,把 netif 和 link 置 up,并保存到下一次 NCM reconnect | 0;无活动 netif -ENODEV;三个字符串任一解析失败 -EINVAL;lwIP 配置失败时返回 err_t |
start_dhcp() |
把 netif 和 link 置 up,启动 lwIP DHCP,并保存“reconnect 后继续 DHCP”模式 | 0;无活动 netif -ENODEV;未编入 DHCP -EOPNOTSUPP;lwIP 失败返回 err_t |
ip、netmask、gw 必须非空并指向有效 IPv4 字符串。静态 IPv4 或 DHCP 配置在 USB 断开后仍保留,下一次连接成功时自动恢复;它们不会写入非易失存储,掉电后恢复默认状态。
IPv6 与 Router Advertisement:
| 接口 | 行为 | 返回 |
|---|---|---|
set_ipv6(ip6, prefix_len) |
设置 netif IPv6 地址槽 1,保存静态配置,并把 netif/link 置 up;只支持 /64 |
0;无活动 netif -ENODEV;非 /64 或地址非法 -EINVAL;未启用 LWIP_IPV6 为 -EOPNOTSUPP |
clear_ipv6() |
清除 IPv6 槽 1 和保存的静态 IPv6;不清除 IPv4 或 RA 发送配置 | 同上 |
ra_start(prefix_cidr) |
保存并启用路由器通告;设备已连接时给 Host netif 配置 prefix::1 并启动 RA 线程 |
0;前缀不是 /64、组播、链路本地或地址非法 -EINVAL;无 IPv6 为 -EOPNOTSUPP |
ra_stop() |
停止 RA 线程、禁用 RA,并关闭“首次连接自动启用默认 RA”的行为 | 0;无 IPv6 为 -EOPNOTSUPP |
ra_once() |
立即发送一次当前 RA | 0;RA 未启用或无活动 netif -ENODEV;分配失败 -ENOMEM;发送失败返回 lwIP err_t |
RA 行为:
- 首次连接且此前没有显式
ra_stop()时,驱动自动启用默认前缀fd12:3456:789a:7::/64,Host netif 使用fd12:3456:789a:7::1。 - 前缀只保留高 64 bit;低 64 bit 会被清零。拒绝组播和链路本地前缀。
- RA 启动后立即发送;前 3 次之间间隔 1 秒,之后每 30 秒发送一次。Router Lifetime、Valid Lifetime 和 Preferred Lifetime 分别为 1800、86400 和 14400 秒。
ra_stop()只停止通告和自动恢复策略,不清除 netif 当前 IPv6 地址;需要删除地址时再调用clear_ipv6()。- 静态 IPv6 地址和 RA 的 Host 地址都使用 IPv6 地址槽 1。设备已连接时,后调用的接口覆盖当前地址;reconnect 时驱动先恢复保存的静态 IPv6,再应用已启用的 RA,因此两者同时保存时 RA 地址最终生效。
TX 聚合:
| 接口 | 行为 |
|---|---|
set_tx_agg(datagrams, auto_fallback) |
只接受 1、4、8、16 个 datagram;设置当前设备的目标聚合数并保存到下一次连接。设备能力不足时实际值会被 clamp。 |
reset_tx_agg() |
等价于 set_tx_agg(1, false)。 |
auto_fallback == true时,TX 队列满或 USB 错误累计达到阈值后,实际聚合数按 16→8→4→1 降级;false时保持调用方目标值。- 没有活动设备时
set_tx_agg()仍会验证并保存设置,下一次 probe 时恢复。 - 参数不在
{1, 4, 8, 16}时返回-EINVAL。
诊断接口:
| 接口 | 输出 |
|---|---|
get_stats() |
返回当前内部统计指针;无活动设备为 NULL,设备移除后旧指针失效。 |
dump_stats() |
netif/IP/MAC、probe 阶段、控制请求、RX/TX URB、NTB/NDP/DPE、CRC 和 TX 聚合计数。 |
dump_params() |
VID/PID、interface/endpoint、NTB 格式、最大尺寸、alignment、MTU 和 IO 限制。 |
dump_quirks() |
当前 VID/PID quirk profile 与标志;当前源码 quirk 表为空,仅保留兼容框架。 |
dump_tx_agg() |
目标、当前、能力上限、manual/auto 模式、fallback 和错误分数。 |
dump_ra() |
RA 启用状态、线程状态、默认策略、前缀和生命周期。 |
dump_*() 主要用于 shell/诊断,输出通过 printf() 或 shell IO,可能阻塞;不要在 USB callback 中调用。
NCM Host 没有公开的 open、read、write 或 close 接口。应用应通过 lwIP 的 socket、netconn 或 raw API 访问 netif;驱动管理的 netif 名称以 nc 开头,并在没有默认 netif 时成为 lwIP 默认接口。
7.3 CH341
包含:
#include <usb/host_ch341.h>
void usb_host_ch341_register_callback(
ch341_device_callback_t callback);
u32 usb_host_ch341_get_devices_bit(void);
int usb_host_ch341_open(u8 id,
ch341_notify_callback_t callback);
void usb_host_ch341_close(u8 id);
int usb_host_ch341_set_line_coding(
u8 id, struct ch341_line_coding *param);
int usb_host_ch341_set_control(u8 id, u16 ctrlout);
int usb_host_ch341_send_break(u8 id, int state);
int usb_host_ch341_read(u8 id, void *data, int len, int timeout);
int usb_host_ch341_write(u8 id, void *data, int len, int timeout);
id 范围为 0 到 CH341_MINORS - 1,当前为 0 到 31。
特殊语义:
usb_host_ch341_open()成功时不只返回 0;返回值是一个非负的初始控制线状态 bitmap(CH341_CTI_*)。示例使用ret < 0判断失败,而不是ret != 0。ch341_notify_callback_t(id, buf, length)接收控制线/状态通知,通常在 URB completion 上下文执行。set_line_coding支持 5、6、7、8 数据位和USB_CH341_*校验常量;bCharFormat == 2选择 2 停止位,其他值按 1 停止位编码,不支持的数据位返回-EINVAL。set_control使用CH341_CTO_D(DTR)、CH341_CTO_R(RTS)等位;硬流控使能时驱动会附加 RTS。set_line_coding、set_control、send_break没有显式 timeout 参数,由底层控制传输默认等待策略决定。- read/write 与 CDC ACM 一样是同步调用;
timeout <= 0永久等待。 usb_host_ch341_close()等待正在执行的used操作结束,id越界或未打开时直接返回。
7.4 HID 键盘、鼠标与 Vendor
7.4.1 Boot Keyboard
包含:
#include <usb/host_kbd.h>
typedef void (*kbd_notify_callback_t)(
unsigned char *report, unsigned int report_len,
char *data, unsigned int data_len);
void usb_kbd_register_callback(kbd_notify_callback_t callback);
功能说明: 注册 Boot Keyboard 报文回调。驱动自动匹配 HID Boot Keyboard,获取 report descriptor,并持续提交 interrupt IN URB。
回调说明:
report/report_len是原始 HID report。data/data_len是驱动解析后的数据。- 指针只在回调期间有效,不能保存到回调返回之后。
- 回调由 interrupt URB completion 调用,必须 ISR-safe。
- 传
NULL可注销回调;设备仍由 Host 驱动管理。
7.4.2 Boot Mouse
包含:
#include <usb/host_mouse.h>
typedef void (*mouse_notify_callback_t)(
unsigned char btn_state,
signed char rel_x,
signed char rel_y,
signed char rel_wheel);
void usb_mouse_register_callback(mouse_notify_callback_t callback);
功能说明:
注册 Boot Mouse 报文回调。驱动只在实际报文长度至少 4 时调用;rel_x、rel_y、rel_wheel 是相对位移,btn_state 是按键位图。
回调在 URB completion 上下文执行,不能阻塞。
Boot Keyboard/Mouse 的公开回调是全局单一回调,参数不携带设备 minor;同时连接多个相同 HID 类设备时,应用无法仅凭该回调区分来源。需要多设备区分时应使用 USB_HOST_HID_VENDOR 或扩展 Host Core 类驱动。
7.4.3 HID Vendor
包含:
#include <usb/host_hid_vendor.h>
void usb_host_hid_vendor_register_callback(
hid_vendor_device_callback_t callback);
u32 usb_host_hid_vendor_get_devices_bit(void);
int usb_host_hid_vendor_open(u8 id);
void usb_host_hid_vendor_close(u8 id);
int usb_hid_vendor_get_in_size(u8 id);
int usb_hid_vendor_get_out_size(u8 id);
int usb_host_hid_vendor_read(u8 id, void *buf,
int len, int timeout);
int usb_host_hid_vendor_write(u8 id, void *data,
int len, int timeout);
id 范围为 0 到 HID_VENDOR_MINORS - 1(当前 0 到 31)。
行为:
open不提交应用 I/O request,只标记设备已打开;成功返回 0,已打开返回-EBUSY,不存在返回-ENODEV。get_in_size/get_out_size只有在设备已打开且尚未进入退出流程时返回端点报文大小,否则返回-ENODEV。read/write的len必须严格等于对应 IN/OUT endpoint 的报文大小,否则返回-EINVAL。read/write使用 interrupt endpoint;返回实际传输字节数或负 errno。close等待该设备正在执行的 I/O 结束。设备真正拔出后驱动也会完成对象回收。- Vendor 驱动不做 HID 报文语义解析,应用自行处理协议。
7.5 UVC
包含:
#include <usb/host_uvc.h>
void usb_host_uvc_register_callback(uvc_device_callback_t callback);
u32 usb_host_uvc_get_devices_bit(void);
int usb_host_uvc_open(u8 id, uvc_notify_callback_t callback);
int usb_host_uvc_close(u8 id);
int usb_host_uvc_get_video_num(u8 id);
int usb_host_uvc_get_video(u8 id, u8 video_id,
struct uvc_host_video *video);
int usb_host_uvc_set_format(
u8 id, const struct uvc_host_video *video,
u8 format_index, u8 frame_index, u32 interval);
int usb_host_uvc_get_format(
u8 id, const struct uvc_host_video *video,
struct uvc_video_format *video_format);
int usb_host_uvc_request_buffer(
u8 id, const struct uvc_host_video *video,
u32 frame_nums, u32 frame_size);
int usb_host_uvc_free_buffer(
u8 id, const struct uvc_host_video *video);
int usb_host_uvc_stream_on(
u8 id, const struct uvc_host_video *video);
int usb_host_uvc_stream_off(
u8 id, const struct uvc_host_video *video);
int usb_host_uvc_get_buffer(
u8 id, const struct uvc_host_video *video,
struct uvc_buffer **buf_p, u32 timeout_ms);
int usb_host_uvc_put_buffer(
u8 id, const struct uvc_host_video *video,
struct uvc_buffer *buf);
id 范围为 0 到 UVC_MINORS - 1,当前为 0 到 31。
7.5.1 回调
typedef void (*uvc_device_callback_t)(u32 devices_bit);
typedef void (*uvc_notify_callback_t)(
u8 id, struct uvc_status *status, int len);
device_callback在 UVC 设备插入/移除时更新 bitmap。notify_callback接收 UVC Status endpoint 数据;可解析bStatusType、bOriginator、bEvent和 union。status指针只在回调期间有效;callback 必须短小。
7.5.2 usb_host_uvc_open / close
| 接口 | 返回/行为 |
|---|---|
open |
成功 0;不存在 -ENODEV;已打开 -EBUSY;若提供 notify callback,会启动 status endpoint |
close |
成功 0;未打开/非法 -EINVAL;id 越界 -ENODEV |
close 会设置 exiting 标志并等待正在进行中的 API 操作结束,但不等待应用尚未归还的 buffer;调用前仍应先停止应用 UVC 线程、完成 stream_off() 和 put_buffer()。
7.5.3 查询 video 和格式
| 接口 | 说明 | 返回 |
|---|---|---|
get_video_num |
返回 Host 识别到的有效 video stream 数量 | 非负数或负 errno |
get_video |
按 video_id 填充 uvc_host_video |
成功 0,不存在 -ENXIO |
set_format |
Probe/Commit Host 格式,interval 使用 UVC 100 ns 单位 |
成功 0,参数/设备错误返回负 errno |
get_format |
返回当前格式,fps = 10000000 / dwFrameInterval |
成功 0 |
set_format 必须在 request_buffer() 和 stream_on() 之前调用。format_index 对应 video->formats[format_index],frame_index 对应所选格式的 frames[frame_index]。驱动会将 interval clamp 到设备支持范围,并尝试最近的离散帧间隔。
7.5.4 缓冲区和流
推荐调用顺序:
open
-> get_video_num
-> get_video
-> set_format
-> get_format
-> request_buffer(frame_nums, frame_size)
-> stream_on
-> get_buffer
-> 处理 buf->mem[0..bytesused-1]
-> put_buffer
-> ...重复 get/put...
-> stream_off
-> free_buffer
-> close
request_buffer():
- 只支持
UVC_STREAM_TYPE_VIDEO_CAPTURE。 frame_nums和frame_size必须非零。- 每个 buffer 的
mem由 Host UVC 驱动分配,length == frame_size。 - queue 已初始化时返回
-EBUSY。 - 分配失败返回
-ENOMEM,设备不存在/未打开返回-ENODEV。
stream_on():
- 必须先 request buffer。
- queue 未初始化或正在 cancel 时返回
-ESRCH。 - 设备断开返回
-ENODEV。 - 成功后 Host 开始填充 ready queue。
get_buffer():
- 返回的 buffer 归应用临时使用,直到
put_buffer()。 timeout_ms == 0且没有 ready buffer 时返回-ENODATA。- 等待超时返回
-ETIMEDOUT;stream 正在 cancel 返回-EINTR。 - 断开时可能先返回带有
UVC_BUF_STATE_ERROR、bytesused == 0的 buffer,也可能返回-ENODATA。 buf->mem[0..bytesused-1]是本次帧数据。
put_buffer():
- 将 buffer 重新放回 Host UVC queue。
- 在 stream off 前必须归还所有已取得的 buffer。
- 若设备在归还时已断开,buffer 会进入 ready queue 并标记错误,供应用观察。
stream_off():
- 停止 Host 侧视频流,不释放 buffer。
- 应在所有应用 buffer 已归还后调用。
free_buffer():
- 要求 stream 已停止;stream 未停止返回
-EBUSY。 - 要求调用方先归还所有借出的 buffer,然后释放全部 buffer。
- queue 未初始化返回
-EINVAL,正在 cancel 返回-EBUSY。
7.6 Mass Storage
包含:
#include <usb/host_mass_storage.h>
int usb_host_mass_storage_link_bind(struct usb_storage_device *dev);
void usb_host_mass_storage_unlink(struct usb_storage_device *dev);
void usb_host_mass_storage_unbind(struct usb_storage_device *dev);
int usb_host_mass_storage_read(
struct usb_storage_device *dev, u8 *buffer,
u32 sector, u16 count);
int usb_host_mass_storage_write(
struct usb_storage_device *dev, u8 *buffer,
u32 sector, u16 count);
7.6.1 插入和拔出回调
link_bind 和 unlink 是驱动在 U 盘插入/拔出时调用的应用 hook:
| 构建配置 | 行为 |
|---|---|
USB_HOST_MASS_STORAGE_AUTO_MOUNT=y |
驱动提供 link_bind/unlink,自动注册块设备并发布分区 |
| 未启用自动挂载 | 应用必须实现 link_bind() 和 unlink() |
应用实现约定:
link_bind(dev)成功返回 0,失败返回负 errno。dev->block_size、dev->block_count、dev->index以及 INQUIRY 字符串只读。unlink(dev)应立即停止新的 read/write,并在所有使用结束后调用usb_host_mass_storage_unbind(dev)。- 拔出时 Host 驱动最多等待
USB_STORAGE_UNBIND_WAIT_TIMEOUT(当前 10 秒);超时后会强制清除绑定,此后继续使用dev是不安全行为。
7.6.2 usb_host_mass_storage_unbind
清除 bind_flag 并唤醒 Host 驱动的拔出等待者。调用后 dev 指针和数据成员不能再用于 read/write。
7.6.3 read / write
| 参数 | 说明 |
|---|---|
dev |
当前已绑定的设备 |
buffer |
应用缓冲区,必须至少 count * dev->block_size 字节 |
sector |
起始逻辑扇区 |
count |
扇区数,u16 范围 |
| 返回值 | 说明 |
|---|---|
0 |
指定全部扇区传输成功 |
-ENODEV |
SCSI/USB 传输失败或设备已移除 |
当前实现在 dev 为空、buffer 为空、内部 private data 为空、LUN 越界或 bind_flag 不匹配时使用 assert();调用方必须先确认绑定有效。
8. 行为约束与实现说明
8.1 回调上下文
USB 类回调没有统一的“一定是线程上下文”保证。文档中的安全规则是:
| 回调 | 典型执行路径 | 约束 |
|---|---|---|
| Host HID 键盘/鼠标 | interrupt URB completion | 必须 ISR-safe,不阻塞 |
| CDC ACM/CH341 notification | interrupt URB completion | 必须 ISR-safe,不阻塞 |
| Host 类设备 bitmap | Hub 枚举/移除工作线程 | 只做状态转发,不执行长操作 |
| CDC NCM device bitmap | Hub 枚举/移除工作线程,probe 完成后触发 | 不在回调中等待 IP/DHCP/RA;bit 0 清零后停止使用统计指针 |
| Host UVC status | status URB completion | 不保存 status 指针,不阻塞 |
| Gadget connect/format/stream/param | 控制请求或功能启停路径 | 短操作;不要调用会等待自身 cleanup 的接口 |
| Device state/power | UDC/HCD 中断或切换路径 | 不阻塞,不做复杂电源决策 |
如果应用需要执行文件、图像、网络或音频处理,应通过 OS 队列、信号量、task notification 或等待器把工作转交给普通线程。
8.2 缓冲区所有权
| 对象 | 所有权 |
|---|---|
Gadget gadget_*_read() 的接收 buffer |
应用所有,调用返回后可以复用 |
Gadget gadget_*_write() 的发送 buffer |
数据会被复制到内部 USB request;返回后应用可复用 buffer |
Gadget UVC uvc_buffer |
提交后由 UVC function 持有;完成回调前不得复用 |
Gadget Display display_buffer |
get_buffer() 后归应用临时使用,必须 put_buffer() 归还 |
| Gadget Mass Storage LUN buffer | 由 FSG 驱动传入 read/write callback,回调返回后失效 |
Host UVC uvc_buffer |
驱动分配;get_buffer() 后应用临时使用,必须 put_buffer() 归还 |
| Host Mass Storage read/write buffer | 应用所有 |
NCM pbuf 与 NTB 缓冲区 |
NCM driver 内部创建、排队和释放;应用不直接提交或持有 pbuf,只通过 lwIP netif/socket 使用网络链路 |
| NCM 统计对象 | usb_host_cdc_ncm_get_stats() 返回的指针归驱动所有,只读;断开后失效 |
| 描述符与回调 | 应用所有;Gadget 生命周期内保持有效 |
USB 驱动不会替应用复制 WinUSB extension property 指向的字符串/数据、RNDIS/NCM IP 字符串或 Mass Storage vendor/product 字符串。NCM Gadget 的 struct usb_ncm_param 只复制结构体,三个 IP 字符串指针指向的存储仍归应用所有。
8.3 连接、断开与清理
- Host 类设备的 bitmap 清零表示设备已移除,应用应停止 I/O、join 工作线程并 close。
- Gadget connect callback 的 0 表示类端点已禁用;read/write 随后会返回
-ENOLINK。 - UVC Gadget 在 Host stream off 后进入 connected 状态,但 connection status 仍返回 1;stream 状态应通过
wait_stream()或stream_cb判断。 gadget_*_cleanup()会注销 composite driver,随后等待已进入的类操作退出;应用应先停止自己的读写线程。- Host
close()会等待正在执行的类操作结束,不能从等待自身结束的同一应用中重入调用。 - CDC NCM Host 没有应用 close;设备移除时由 Host Core 自动调用 driver disconnect。应用应从 bitmap 回调获知 bit 0 清零,并在此之前停止引用统计对象。
- NCM Gadget cleanup 会移除 netif 并停止接收 worker;调用前应停止使用
gadget_ncm_get_netif()返回值和网络 socket。 usb_core_exit()必须在所有类资源释放后调用。
8.4 超时和阻塞
- Gadget
timeout_ms是毫秒,特殊值THREAD_COND_TIMEOUT_NO_LIMIT_MS/UINT32_MAX表示永久等待。 - Host 串口/HID/UVC
timeout是毫秒;Host 串口/HID 的timeout <= 0和 Host UVC 的UINT32_MAX表示永久等待。 block == 0的 Gadget 接口不等待;部分接口用timeout_ms == 0表示非阻塞。- 所有等待接口只适合线程上下文。ISR 和 callback 不能阻塞。
- NCM 的 IP/RA 配置通过
netifapi_*投递到 lwIP TCP/IP 线程,可能等待该线程处理;不要在 USB callback、lwIP callback 内同步调用会等待自身线程的 NCM 配置接口。 - OS 后端为 FreeRTOS 时,相对毫秒超时会按 tick 向上取整;不能假设严格 1 ms 精度。
8.5 编译期和单实例限制
USB_GADGET_*是 choice;一个固件只有一个 Gadget 或复合 Gadget 主入口。- 多个复合类公开同名
gadget_hid_*、gadget_serial_*、gadget_uvc_*、gadget_uac1_*符号。不要同时编译/包含多个 Gadget 类,否则会出现重复定义或符号语义冲突。 usb_core_init()只允许一个活动 Core 实例;重复调用返回-EBUSY。usb_host_init()没有重复调用保护,应由系统初始化代码只调用一次。- UVC N 的
UVC_MAX_COUNT为 4;传入更大的config_count不是受支持配置。 - Host CDC/CH341/HID/UVC 的 minor 数均为 32;设备索引始终小于 32。
- CDC NCM Host 当前为单实例实现,只使用 bitmap bit 0;不能同时管理两个 NCM 设备。
- RNDIS/NCM 依赖
MIDDLEWARE_NET_LWIP;当前 lwIP Kconfig 要求 FreeRTOS 后端,不能在其他 OS 后端的固件中启用。
8.6 VBUS 与电源配置
- 正常 Device 模式由 USB PHY 检测 VBUS 并驱动 UDC connect/disconnect。
- 启用
USB_DISABLE_VBUS后,硬件 VBUS 状态被忽略;应用必须根据外部检测电路调用usb_device_vbus_status_update()。 - Host/OTG 模式可通过
USB_HOST_VBUS_POWER_GPIO_{NONE,A..E}、USB_HOST_VBUS_POWER_PIN和 active-low 选项控制外部 5 V 电源。 - VBUS GPIO 配置无效或未连接时,
USB_HOST_VBUS_POWER_PIN应保持-1。 USB_GADGET_VBUS_DRAW影响 Device 声明的总线电流,不替代产品级电源限流设计。usb_core_suspend()/usb_core_resume()编排控制器及平台 PHY 电源状态;PHY 的寄存器级行为见USB PHY HAL/LL 文档,应用仍需保存和恢复类业务状态。
9. 典型用法
以下示例只展示正式接口和推荐顺序,不包含应用业务线程的完整错误恢复。
9.1 CDC ACM Gadget
#include <usb/usb.h>
#include <usb/gadget_serial.h>
static const struct gadget_id id = {
.vendor_id = 0x0525,
.product_id = 0xa4a7,
};
static const struct usb_cdc_serial_param param = {
.dwDTERate = 115200,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8,
};
static void on_connect(int connect)
{
/* 只做状态通知,实际 I/O 由工作线程完成。 */
}
void usb_cdc_demo_start(void)
{
if (usb_core_init() != 0)
return;
if (gadget_serial_init(&id, ¶m, on_connect, NULL) != 0) {
usb_core_exit();
return;
}
/* 由普通线程使用 gadget_serial_read/write/wait_connect。 */
}
void usb_cdc_demo_stop(void)
{
gadget_serial_cleanup();
usb_core_exit();
}
9.2 CDC ACM Host
#include <usb/usb.h>
#include <usb/host_cdc_acm.h>
static void on_device_bitmap(u32 devices_bit)
{
for (u8 id = 0; id < ACM_MINORS; id++) {
if (devices_bit & BIT(id)) {
/* 通知工作线程打开并处理该设备。 */
}
}
}
void usb_host_demo_start(void)
{
usb_host_init();
usb_host_cdc_acm_register_callback(on_device_bitmap);
if (usb_core_init() != 0)
return;
/* 插入后工作线程:
* usb_host_cdc_acm_open(id, notify_cb);
* usb_host_cdc_acm_set_line_coding(id, &coding, 1000);
* usb_host_cdc_acm_set_control(id, ACM_CTRL_DTR | ACM_CTRL_RTS, 1000);
* usb_host_cdc_acm_read/write(id, ..., 1000);
* 移除后 usb_host_cdc_acm_close(id);
*/
}
9.3 UVC Gadget
#include <usb/usb.h>
#include <usb/gadget_uvc.h>
static u8 fb[640 * 480 * 2]; /* YUYV: 2 bytes per pixel */
static const u32 fps[] = { 30, 15 };
static const struct uvc_frame_config frames[] = {
{ .width = 640, .height = 480, .fps_num = 2, .frame_fps = fps },
};
static const struct uvc_format_config formats[] = {
{ .fcc = V4L2_PIX_FMT_YUYV, .bpp = 16,
.frames_num = 1, .frames = frames },
};
static const struct uvc_device_config config = {
.format_num = 1,
.formats = formats,
.camera_param_config = UVC_PARAM_BRIGHTNESS | UVC_PARAM_CONTRAST,
.camera_feature_config = UVC_FEATURE_AUTO_EXPOSURE_PRIORITY,
};
static struct uvc_buffer buffer = {
.mem = fb,
.length = sizeof(fb),
};
static void on_complete(struct uvc_buffer *buf)
{
/* 通知工作线程:buffer 已可再次使用。 */
}
static void on_stream(int on)
{
/* 启动/停止采集线程。 */
}
static const struct uvc_callback callback = {
.stream_cb = on_stream,
};
void usb_uvc_demo_start(void)
{
buffer.complete = on_complete;
if (usb_core_init() != 0)
return;
gadget_uvc_init(&(struct gadget_id){ 0x1d6b, 0x0102 },
&config, &callback);
}
实际应用应先 gadget_uvc_wait_stream() 获得 Host 选择的格式,再按格式计算 buffer 长度并提交 gadget_uvc_write()。
9.4 UVC Host
int uvc_capture_one_frame(u8 id)
{
struct uvc_host_video video;
struct uvc_video_format fmt;
struct uvc_buffer *buf;
u32 frame_size;
int ret;
ret = usb_host_uvc_open(id, NULL);
if (ret)
return ret;
ret = usb_host_uvc_get_video(id, 0, &video);
if (ret)
goto close;
ret = usb_host_uvc_set_format(id, &video, 0, 0, 0);
if (ret)
goto close;
ret = usb_host_uvc_get_format(id, &video, &fmt);
if (ret)
goto close;
frame_size = video.formats[0].frames[0].dwMaxVideoFrameBufferSize;
ret = usb_host_uvc_request_buffer(id, &video, 3, frame_size);
if (ret)
goto close;
ret = usb_host_uvc_stream_on(id, &video);
if (ret)
goto free_buffer;
ret = usb_host_uvc_get_buffer(id, &video, &buf, 1000);
if (!ret) {
/* 处理 buf->mem[0..buf->bytesused-1]。 */
usb_host_uvc_put_buffer(id, &video, buf);
}
usb_host_uvc_stream_off(id, &video);
free_buffer:
usb_host_uvc_free_buffer(id, &video);
close:
usb_host_uvc_close(id);
return ret;
}
实际产品应把该函数放入普通工作线程,并在断开回调中唤醒它退出,而不是在 UVC status/HID URB callback 中执行。
9.5 Mass Storage Host
自动挂载不需要应用实现 hook:
CONFIG_DRIVER_USB_PHY=y
CONFIG_USB_DRIVER=y
CONFIG_USB0=y
CONFIG_USB_HOST=y
CONFIG_USB_HOST_MASS_STORAGE=y
CONFIG_USB_HOST_MASS_STORAGE_AUTO_MOUNT=y
CONFIG_DFS_ELMFAT=y
手工管理时需要实现:
int usb_host_mass_storage_link_bind(struct usb_storage_device *dev)
{
/* 初始化介质、检查容量、通知文件系统。 */
return 0;
}
void usb_host_mass_storage_unlink(struct usb_storage_device *dev)
{
/* 停止应用读写;等待所有 read/write 返回。 */
usb_host_mass_storage_unbind(dev);
}
拔出流程中,unlink() 必须在 10 秒内完成 unbind;否则 Host 驱动会强制撤销绑定,应用此后不得再访问 dev。
9.6 NCM Gadget
#include <usb/usb.h>
#include <usb/gadget_ncm.h>
#include "im_net_common.h"
static const struct gadget_id ncm_id = {
.vendor_id = 0x0525,
.product_id = 0xa4a2,
};
static struct usb_ncm_param ncm_param = {
.ipaddr = "192.168.1.100",
.netmask = "255.255.255.0",
.gw = "192.168.1.1",
.mtu = 1500,
.dev_mac = { 0x02, 0x12, 0x34, 0x56, 0x78, 0x9a },
.host_mac = { 0x02, 0x12, 0x34, 0x56, 0x78, 0x9b },
};
int usb_ncm_device_start(void)
{
int ret;
if (im_net_lwip_core_init() != IM_OK)
return -EIO;
ret = usb_core_init();
if (ret)
return ret;
ret = gadget_ncm_init(&ncm_id, &ncm_param);
if (ret) {
usb_core_exit();
return ret;
}
/* USB data interface 连接后会自动把已 up 的 netif link 置 up。 */
return 0;
}
void usb_ncm_device_stop(void)
{
/* 先停止应用侧 socket/网络线程。 */
gadget_ncm_cleanup();
usb_core_exit();
}
运行中修改地址可使用 gadget_ncm_set_ipv4() 或 gadget_ncm_set_ipv6()。IPv6 只接受 /64,并固定使用 lwIP IPv6 地址槽 1。
9.7 NCM Host
#include <usb/usb.h>
#include <usb/host_cdc_ncm.h>
#include "im_net_common.h"
static volatile u32 ncm_devices;
static void ncm_device_event(u32 devices_bit)
{
ncm_devices = devices_bit;
/* 唤醒普通工作线程,不要在 Hub 回调中配置 IP。 */
}
int usb_ncm_host_start(void)
{
int ret;
if (im_net_lwip_core_init() != IM_OK)
return -EIO;
usb_host_init();
usb_host_cdc_ncm_register_callback(ncm_device_event);
ret = usb_core_init();
if (ret)
return ret;
/* 可在设备出现前保存 NCM reconnect 策略。 */
usb_host_cdc_ncm_set_tx_agg(8, true);
return 0;
}
int usb_ncm_host_on_device(void)
{
if (!(ncm_devices & BIT(0)))
return -ENODEV;
/* 静态 IPv4 与 DHCP 二选一。 */
return usb_host_cdc_ncm_set_ipv4("192.168.7.1",
"255.255.255.0",
"192.168.7.2");
}
Host NCM 首次连接默认启用 fd12:3456:789a:7::/64 RA;如果产品不使用 RA,应在连接前或连接后调用 usb_host_cdc_ncm_ra_stop()。应用业务应使用 lwIP socket/netconn API,不直接访问 CDC NCM 的 NTB/RX/TX 内部接口。
10. 示例程序与构建
示例入口:
code/middleware/usb/example/main/main.ccode/middleware/usb/example/main/im_usb_example.hcode/middleware/usb/example/main/device/code/middleware/usb/example/main/host/
示例使用方式:
- 选择
IM_USB_EXAMPLE_SELECTION。 - 使用对应
code/middleware/usb/example/defconfigs/*_defconfig。 - 配置
SDK_APP_DIR=code/middleware/usb/example。 - 编译并烧录。
示例构建命令:
tools/imde/bin/imde use-defconfig \
code/middleware/usb/example/defconfigs/usb_gadget_serial_defconfig
tools/imde/bin/imde -D SDK_APP_DIR=code/middleware/usb/example configure
tools/imde/bin/imde build
切换到 Host 示例时,应改用对应的 usb_host_*_defconfig,并重新执行 configure。
当前示例覆盖:
| 类型 | 示例 |
|---|---|
| Device | CDC、HID keyboard/mouse/vendor/wakeup、Bulk、WinUSB、Printer、Mass Storage、RNDIS、NCM、UAC1、UVC、UVC+HID、UVC+CDC、多路 UVC |
| Host | CDC ACM、CDC NCM、CH341、HID keyboard/mouse/vendor、UVC、Mass Storage |
code/devices/wireless/ 下也有名称包含 usb 的厂商 WiFi 芯片代码,但它们属于具体无线设备驱动,不是 G32S10 USB MID 的正式设备/主机接口,本文不将其列为公共 USB API。
11. 错误码
USB MID 使用 code/include/usb/usb_errno_compat.h 提供的 Linux errno 兼容值,常见返回值如下:
| 错误 | 数值 | 常见场景 |
|---|---|---|
-EINVAL |
-22 | 空指针、长度/索引非法、格式不匹配 |
-ENOMEM |
-12 | USB request、URB、buffer、内部对象分配失败 |
-EBUSY |
-16 | 设备已打开、queue 已初始化、资源正在使用 |
-ENODEV |
-19 | 未初始化、设备索引无效、Host 设备已移除 |
-ENOLINK |
-67 | Gadget 已连接但当前未建立类链路 |
-ENOTCONN |
-128 | UVC 已连接但未 stream on;UAC 端点未启用 |
-EAGAIN |
-11 | 非阻塞调用暂时没有数据或发送空间 |
-ETIMEDOUT |
-116 | 等待数据、连接、视频流或 USB 请求超时 |
-ENODATA |
-61 | Host UVC 暂时没有 ready buffer |
-ESRCH |
-3 | Host UVC queue 未初始化或正在取消 |
-EINTR |
-4 | Host UVC stream 正在取消或被中断 |
-EIO |
-5 | Host Core/URB I/O 失败 |
-EOPNOTSUPP |
-95 | UVC/UAC feature/control request 不支持;NCM IPv6/DHCP 能力未编入 |
-EHOSTDOWN |
-117 | HID wakeup 时 Host 已停止调度/不可唤醒 |
错误码规则:
- 负 errno 表示失败;调用方不要假设所有接口都返回同一组错误。
- 初始化接口返回
int时,0表示成功;UVC/UVC 复合接口返回void,不能使用返回值判断成功。 - Gadget IO 的正数返回值是本次字节数;短写/短读不自动重试。
- Host 串口/HID IO 的正数返回值是实际传输字节数。
- NCM 静态 IP、DHCP 和 RA 的部分失败路径返回 lwIP
err_t,应使用ERR_OK/ERR_*和负值判断,不要假定一定是本表列出的 Linux errno。