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);

缓冲区流程:

  1. gadget_display_get_buffer() 从 Host 文本/控制数据 OUT 队列取得一块 request。
  2. 驱动填充 buf、length、actual 和内部 private_data。
  3. 应用读取 buf[0..actual-1] 后必须调用 gadget_display_put_buffer() 归还 request。
  4. 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。

初始化顺序:

  1. 以 im_net_lwip_core_init() 初始化 lwIP TCP/IP 线程。
  2. 调用 usb_host_init() 注册 Host Core 和 CDC NCM driver。
  3. 调用 usb_host_cdc_ncm_register_callback() 注册设备 bitmap 回调。
  4. 调用 usb_core_init()。
  5. 在 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, &param, 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.c
  • code/middleware/usb/example/main/im_usb_example.h
  • code/middleware/usb/example/main/device/
  • code/middleware/usb/example/main/host/

示例使用方式:

  1. 选择 IM_USB_EXAMPLE_SELECTION。
  2. 使用对应 code/middleware/usb/example/defconfigs/*_defconfig。
  3. 配置 SDK_APP_DIR=code/middleware/usb/example。
  4. 编译并烧录。

示例构建命令:

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。