复合功能主机方案
概述
USB 复合设备 (Composite Device) 架构允许单一物理 USB 设备通过配置多个接口描述符 (Interface Descriptors) 或接口关联描述符 (IAD),在逻辑上呈现多种独立的功能。
Ameba 平台的 USB 主机协议栈已全面支持复合设备的枚举与驱动加载。系统能够智能解析设备的配置结构,并为不同的接口加载对应的类驱动程序。这使得 Ameba 能够通过一个 USB 端口,同时并发处理多种不同类型的业务流。
特性
支持以下功能组合:
CDC ACM + CDC ECM
HID + UAC
支持热插拔
自动解析描述符,自适应速度模式
应用场景
作为 USB 主机,Ameba 可通过复合类驱动同时支持多种接口类的组合,能够与市面上复杂的 USB 复合设备进行交互,适用于多种物联网与多媒体应用场景,例如:
蜂窝网络接入 (CDC ECM + CDC ACM):这是 4G/LTE Cat.1/Cat.4 模组及 USB Dongle 最常见的复合模式。Ameba 利用 CDC ECM (以太网) 接口进行高速互联网接入,广泛应用于工业 IoT 网关的数据回传或车载终端的联网功能;同时 CDC ACM (虚拟串口) 接口并行用于传输 AT 指令,实现对模组信号状态的实时监控和远程控制。
交互式音频设备 (UAC + HID):这是带有物理控制按键的 USB 耳机、智能音箱或游戏手柄的典型应用。Ameba 利用 UAC (音频设备类) 接口处理高品质的语音通话、背景音乐或游戏音效流,同时通过 HID (人机接口设备) 接口实时响应设备上的物理按键(如音量调节、静音、媒体播放控制或手柄输入),实现音频体验与用户交互的无缝融合。
协议简介
USB 复合设备 (USB Composite Device) 规范定义了在 USB 规范框架下,通过单一物理接口同时承载多个独立功能接口的设备架构。 该机制允许主机将单一物理设备识别并枚举为多个逻辑功能的集合,实现不同功能类(如 HID、MSC、CDC 等)的并行处理与独立控制。 遵循该架构的常见设备包括无线键鼠接收器、带声卡的 USB 耳机及集成了存储与调试功能的工业设备等。
复合驱动仅负责顶层管理,具体子功能(如 HID 的报文格式、CDC 的通信协议)的数据处理需遵守其 对应的类协议规范,请参阅相关章节。
描述符结构
复合设备在遵循标准 USB 描述符(设备描述符、配置描述符)的基础上,通过在配置描述符集合中聚合多个接口描述符来实现多功能的定义。
单接口功能类
如果一个接口就代表了一个独立的功能,复合设备在聚合这种功能类的时候直接拼接使用。典型的单接口功能类有 HID 和 MSC。
以 HID 键盘 + HID 鼠标 复合设备为例,其描述符拓扑结构如下所示:
Device Descriptor
| bDeviceClass: 0xEF (Miscellaneous)
| bDeviceSubClass: 0x02 (Common Class)
| bDeviceProtocol: 0x01 (Interface Association Descriptor)
│
└── Configuration Descriptor
| bNumInterfaces: 2 (2 Interfaces)
│ ...
├── Interface Descriptor 0 (HID Keyboard)
│ bInterfaceNumber: 0
│ bInterfaceClass: 0x03 (HID)
│ bInterfaceSubClass: 0x01 (Boot Interface)
│ bInterfaceProtocol: 0x01 (Keyboard)
│
└── Interface Descriptor 1 (HID Mouse)
bInterfaceNumber: 1
bInterfaceClass: 0x03 (HID)
bInterfaceSubClass: 0x01 (Boot Interface)
bInterfaceProtocol: 0x02 (Mouse)
HID 键盘 + HID 鼠标 复合设备和单独的键盘/鼠标设备对比,描述符特征如下:
描述符层级 |
普通设备(Single Function) |
复合设备(Composite Device) |
|---|---|---|
设备描述符 |
|
|
配置描述符 |
1 个配置 |
1 个配置 |
接口描述符 |
1 个接口 |
有 2 个接口 |
端点描述符 |
归属于该接口 |
每个接口拥有独立的端点 |
备注
bDeviceClass = 0xEF:这是复合设备的标准标志,会触发主机驱动解析 IAD (可选)和配置描述符中的多个接口描述符,拆分出不同的设备功能,并创建这些逻辑子设备。
然后分别为 Interface 0 加载键盘驱动,为 Interface 1 加载鼠标驱动。
多接口功能类
如果一个逻辑功能需要占用多个接口才能完成,在使用这种功能类的时候必须加入 接口关联描述符 (Interface Association Descriptor, IAD) 将这些接口关联起来。
典型的多接口功能类
CDC (通信设备类): 通常需要 1 个控制接口 (CCI) + 1 个数据接口 (DCI)。
UVC (USB 视频类): 需要 1 个视频控制接口 (VC) + 1 个或多个视频流接口 (VS)。
UAC (USB 音频类): 需要 1 个音频控制接口 (AC) + 1 个或多个音频流接口 (AS)。
接口关联描述符(IAD)
IAD 向主机声明紧随其后的一组连续接口属于同一个功能,应由同一个驱动程序加载和管理。如果不加 IAD,主机有时候会把它们识别为两个独立的设备,或者驱动加载失败。
Interface Association Descriptor (IAD)
├── bLength : 1 byte → 0x08 (Fixed Length)
├── bDescriptorType : 1 byte → 0x0B (IAD type code)
├── bFirstInterface : 1 byte → First Interface Number
├── bInterfaceCount : 1 bytes → Count of associated interfaces
├── bFunctionClass : 1 byte → Function Class Code
├── bFunctionSubClass : 1 byte → Function SubClass Code
├── bFunctionProtocol : 1 byte → Function Protocol Code
└── iFunction : 1 byte → Function String Descriptor Index
IAD 使用示例
功能 1:设备类占用两个接口,为了让主机将其识别为单一的逻辑功能,必须使用 IAD 将这两个接口进行关联。
功能 2:设备类使用一个独立的接口,不需要 IAD 关联。
类特定请求
USB 协议规范中并没有定义专属的“复合类请求”,其核心逻辑在于通过 USB 标准请求中的寻址机制,将 类特定请求 (Class-Specific Requests) 精确指向指定的接口。
在 SETUP 包的 bmRequestType 字段中,低 5 位(Bits 0..4)表示 Recipient(接收者)。
普通单功能设备:控制请求通常是发给“整个设备”的,即将 Recipient 设置为
Device (00000)。复合设备:大量的控制请求必须精确地发给“特定的接口”,即将 Recipient 设置为
Interface (00001)。
以下是复合设备在控制请求处理上最显著的几个关键点:
字段 |
值 |
含义 |
复合设备中的体现 |
|---|---|---|---|
bmRequestType |
0x21 / 0xA1 |
Class Request, Recipient=Interface |
最常见。例如 CDC 设置波特率、HID 点亮键盘灯 |
wIndex |
Interface Number |
接口编号 |
当 Recipient 是接口时,wIndex 必须填入具体的接口号(如 0, 1, 2), 驱动必须根据这个值把请求分发给对应接口功能驱动处理 |
类驱动
复合设备(Composite Device)是指在一个物理 USB 设备中包含多个独立功能(Function)的设备。 复合主机驱动的主要职责是正确解析设备的配置描述符,并为每个功能加载相应的子驱动程序,将单一物理设备枚举为多个逻辑功能的集合。 各功能独立互不干扰(例如:在传输网络数据的同时发送 AT 命令,或在播放音乐的同时接收按键输入)。
本节详细分析如何设计并实现一个 USB 复合主机类驱动。复合类驱动充当“父类”角色,本身负责管理多个“子类”(如 CDC ECM、HID、UAC 等)的资源调度、请求分发和数据处理。
复合类驱动在系统架构中起着承上启下的作用,其核心交互逻辑主要围绕以下三个接口展开:
主机类驱动回调 API:复合类驱动通过注册标准的
usbh_class_driver_t结构体,实现与底层 USB Core 进行交互。面向应用的回调 API:通过应用层在初始化时注册的
usbh_composite_cb_t、usbh_user_cb_t和子功能驱动的回调函数usbh_composite_function_class_xx_usr_cb_t,向上层应用提供异步事件通知(如连接、断开、数据接收)。面向应用的 API:提供给应用层调用的功能接口。例如驱动加载/卸载,进行数据传输的调度等。
应用层回调 API
驱动为应用层提供了回调函数接口定义,以便应用代码能响应 USB 事件并处理业务逻辑,由应用层实现。
Host 应用层回调 API
usbh_user_cb_t是针对整个主机的回调。Composite 应用层回调 API
usbh_composite_cb_t是针对顶层复合驱动的回调。子功能类应用层回调 API
这是每个子功能自定义的回调,一般可选择实现:
typedef struct { int(* init)(void); int(* deinit)(void); int(* attach)(void); int(* detach)(void); int(* setup)(void); int(* received)(u8 *buf, u32 len); void(* transmitted)(u8 status); } usbh_composite_function_class_xx_usr_cb_t;
API
描述
init
在类驱动初始化时被调用,用于初始化应用相关的资源
deinit
在类驱动注销时被调用,用于注销应用相关的资源
attach
在类驱动执行attach回调时被调用,用于应用层处理设备连接事件
detach
在类驱动执行detach回调时被调用,用于应用层处理设备断开事件
setup
在类驱动执行setup回调时被调用,用于指示应用层类驱动已准备好进行数据传输
receive
在类驱动收到IN数据时被调用,用于应用层处理设备上报的数据
transmit
在类驱动OUT数据传输完成时被调用,用于应用层获取OUT传输状态
下面以 HID + UAC 为例介绍自定义的子功能类应用层回调:
/* HID user Callback Interface. */
typedef struct {
int(* init)(void); /**< Callback when HID driver is initialized */
int(* deinit)(void); /**< Callback when HID driver is de-initialized */
int(* attach)(void); /**< Callback when a HID device is attached */
int(* detach)(void); /**< Callback when a HID device is detached */
int(* setup)(void); /**< Callback during the Setup stage of a control transfer */
int(* report)(usbh_composite_hid_event_t *event);/**< Callback when a HID event report is received */
} usbh_composite_hid_usr_cb_t;
/* UAC User Callback Interface. */
typedef struct {
int(* init)(void); /**< Callback when UAC driver is initialized */
int(* deinit)(void); /**< Callback when UAC driver is de-initialized */
int(* attach)(void); /**< Callback when a UAC device is attached */
int(* detach)(void); /**< Callback when a UAC device is detached */
int(* setup)(void); /**< Callback during the Setup stage of a control transfer */
int(* isoc_transmitted)(usbh_urb_state_t state); /**< Callback when isochronous OUT (Play) transfer completes */
int(* isoc_received)(u8 *buf, u32 len); /**< Callback when isochronous IN (Record) data is received */
} usbh_composite_uac_usr_cb_t;
类驱动实现
主要是定义复合类主机和类驱动回调函数实现。
子功能类驱动
每个子功能(如 CDC ACM, UAC, HID)都是一个独立的类驱动,详细请参考子功能方案的对应章节。
独立的驱动结构: 每个子驱动定义一个标准的
usbh_class_driver_t结构体,实现自己的业务逻辑。独立的资源管理: 每个子驱动负责解析对应接口,管理自己的通道、数据缓冲区以及数据收发处理等。
复合类驱动
复合类驱动需要定义一个标准的
usbh_class_driver_t结构体,作为统一的入口注册到 USB Core 中,复合主机驱动会 遍历 每个子功能类驱动对应的回调函数。复合类驱动定义一个标准的
usbh_composite_host_t结构体,这是复合主机实例的核心,用于管理所有子功能类驱动。/* Composite Device */ static usbh_composite_host_t usbh_composite_host; /* Composite Class Driver */ static const usbh_class_driver_t usbh_composite_driver = { .id_table = composite_devs, /* 支持的设备 ID 列表,核心层使用此表与插入的设备进行匹配,以决定是否加载此驱动 */ .attach = usbh_composite_cb_attach, /* 设备连接并匹配成功后调用 */ .detach = usbh_composite_cb_detach, /* 设备断开时调用 */ .setup = usbh_composite_cb_setup, /* 枚举完成进入类请求阶段,用于发送类特定的标准控制请求,完成设备进入数据传输状态前的必要配置 */ .process = usbh_composite_cb_process, /* 类驱动驱动就绪后的核心状态机处理函数 */ .sof = usbh_composite_cb_sof, /* SOF 中断时被调用,用于时序要求严格的处理逻辑 */ .completed = usbh_composite_cb_completed, /* 当通道上的传输完成时调用 */ };
下面以 HID + UAC 为例介绍主机类驱动实例实现:
/* Composite Host structure. */
typedef struct {
usbh_class_driver_t *hid;
usbh_class_driver_t *uac;
usbh_composite_cb_t *cb;
usb_host_t *host;
} usbh_composite_host_t;
/* Composite Host */
static usbh_composite_host_t usbh_composite_host;
/* Composite Device ID */
static const usbh_dev_id_t composite_devs[] = {
{
.mMatchFlags = USBH_DEV_ID_MATCH_ITF_INFO,
.bInterfaceClass = USB_UAC1_CLASS_CODE,
.bInterfaceSubClass = USB_UAC1_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = 0x00,
},
{
},
};
/* Composite Class Driver */
static usbh_class_driver_t usbh_composite_driver = {
.id_table = composite_devs,
.attach = usbh_composite_hid_uac_cb_attach,
.detach = usbh_composite_hid_uac_cb_detach,
.setup = usbh_composite_hid_uac_cb_setup,
.process = usbh_composite_hid_uac_cb_process,
.sof = usbh_composite_hid_uac_cb_sof,
.completed = usbh_composite_hid_uac_cb_completed,
};
/********************** Function 1: HID Class *********************/
/* HID host structure. */
typedef struct {
usbh_composite_host_t *driver; /**< Pointer to the parent composite host structure. */
usbh_composite_hid_usr_cb_t *cb; /**< Pointer to the user-registered callback structure. */
usbh_pipe_t pipe; /**< Interrupt IN pipe handler. */
//....
} usbh_composite_hid_t;
/* HID Host */
static usbh_composite_hid_t usbh_composite_hid;
/* HID Class Driver */
const usbh_class_driver_t usbh_composite_hid_driver = {
.attach = usbh_composite_hid_cb_attach,
.detach = usbh_composite_hid_cb_detach,
.setup = usbh_composite_hid_cb_setup,
.process = usbh_composite_hid_cb_process,
.sof = usbh_composite_hid_cb_sof,
};
/********************** Function 2: UAC Class *********************/
/* UAC host structure. */
typedef struct {
usbh_uac_ac_itf_info_t ac_isoc_desc; /**< Audio Control interface topology (Feature Units + Terminals) */
usbh_uac_channel_t isoc_out; /**< OUT channel state (Playback/Speaker) */
usbh_uac_channel_t isoc_in; /**< IN channel state (Record/Microphone) */
usbh_composite_uac_usr_cb_t *cb; /**< User callback structure registered via usbh_composite_uac_init */
usbh_composite_host_t *driver; /**< Pointer to the parent USB Host Composite handle */
//...
} usbh_composite_uac_t;
/* UAC Host */
static usbh_composite_uac_t usbh_composite_uac;
/* UAC Class Driver */
const usbh_class_driver_t usbh_composite_uac_driver = {
.attach = usbh_composite_uac_cb_attach,
.detach = usbh_composite_uac_cb_detach,
.setup = usbh_composite_uac_cb_setup,
.process = usbh_composite_uac_cb_process,
.sof = usbh_composite_uac_cb_sof,
.completed = usbh_composite_uac_cb_completed,
};
加载与卸载类驱动
应用层通过下面两个主要函数来控制整个 Composite 类驱动的生命周期:
usbh_composite_init(): 加载函数接收应用层传入的参数,如各子功能的回调函数集。
调用每个子功能的驱动加载函数。
将子功能驱动实例链接到
usbh_composite_host_t结构体中。最后,调用
usbh_register_class()将复合主机驱动注册到 USB Core,使其生效。
usbh_composite_deinit(): 卸载函数调用
usbh_unregister_class()从 USB Core 注销复合主机驱动。遍历每个子功能的卸载函数,释放所有资源。
HID + UAC 示例
/**
* @brief Init composite class
* @param cb: User callback
* @retval Status
*/
int usbh_composite_init(usbh_composite_hid_usr_cb_t *hid_cb, usbh_composite_uac_usr_cb_t *uac_cb)
{
int ret;
usbh_composite_host_t *chost = &usbh_composite_host;
if ((hid_cb == NULL) || (uac_cb == NULL)) {
ret = HAL_ERR_PARA;
RTK_LOGS(TAG, RTK_LOG_ERROR, "Invalid user CB\n");
return ret;
}
ret = usbh_composite_hid_init(chost, hid_cb);
if (ret != HAL_OK) {
RTK_LOGS(TAG, RTK_LOG_ERROR, "Init HID itf fail: %d\n", ret);
return ret;
}
chost->hid = (usbh_class_driver_t *)&usbh_composite_hid_driver;
ret = usbh_composite_uac_init(chost, uac_cb);
if (ret != HAL_OK) {
RTK_LOGS(TAG, RTK_LOG_ERROR, "Init UAC itf fail: %d\n", ret);
usbh_composite_hid_deinit();
return ret;
}
chost->uac = (usbh_class_driver_t *)&usbh_composite_uac_driver;
usbh_register_class(&usbh_composite_driver);
return HAL_OK;
}
/**
* @brief Deinit composite class
* @retval Status
*/
int usbh_composite_deinit(void)
{
usbh_composite_host_t *chost = &usbh_composite_host;
usbh_unregister_class(&usbh_composite_driver);
usbh_composite_deinit_uac_class();
usbh_composite_deinit_hid_class();
chost->cb = NULL;
return HAL_OK;
}
连接与断连处理
设备的连接与断开由 USB Core 自动检测,并调用类驱动的对应回调进行资源管理。
设备连接
当 Core 层枚举到匹配
usbh_dev_id_t的 Composite 设备时,attach回调被调用,内部遍历每个子功能的attach回调函数。每个子功能驱动的主要职责是:
匹配子功能的
usbh_dev_id_t, 获得并解析对应的接口描述符。根据端点描述符,通过
usbh_open_pipe()打开对应通道。初始化每个通道对应的传输。
调用子功能驱动的用户
attach回调,告知应用层。
设备断开
当设备拔出时,
detach回调被调用,内部遍历每个子功能的attach回调函数。每个子功能驱动的主要职责是:
重置子功能驱动状态机的状态为 IDLE。
调用
usbh_close_pipe()关闭所有已打开的通道。调用子功能驱动的用户
detach回调,告知应用层。
HID + UAC 示例
/**
* @brief Attach callback.
* @param host: Host handle
* @retval Status
*/
static int usbh_composite_hid_uac_cb_attach(usb_host_t *host)
{
int ret;
usbh_composite_host_t *chost = &usbh_composite_host;
chost->host = host;
if ((chost->hid != NULL) && (chost->hid->attach != NULL)) {
ret = chost->hid->attach(host);
if (ret != HAL_OK) {
usbh_composite_deinit_hid_class();
RTK_LOGS(TAG, RTK_LOG_WARN, "Can not support hid\n");
}
}
if ((chost->uac != NULL) && (chost->uac->attach)) {
ret = chost->uac->attach(host);
if (ret != HAL_OK) {
usbh_composite_deinit_uac_class();
RTK_LOGS(TAG, RTK_LOG_WARN, "Can not support uac\n");
}
}
return HAL_OK;
}
/**
* @brief Detach callback.
* @param host: Host handle
* @retval Status
*/
static int usbh_composite_hid_uac_cb_detach(usb_host_t *host)
{
usbh_composite_host_t *chost = &usbh_composite_host;
if ((chost->hid != NULL) && (chost->hid->detach != NULL)) {
chost->hid->detach(host);
}
if ((chost->uac != NULL) && (chost->uac->detach)) {
chost->uac->detach(host);
}
return HAL_OK;
}
类驱动状态机
与设备端被动响应请求不同,主机端驱动需要主动维护设备状态。
USB 主机类驱动采用状态机来管理数据传输处理,驱动由 API 调用和底层中断反馈更新状态。
process 回调函数是主机端复合类驱动的核心状态机处理函数,内部遍历每个子功能驱动的 process 回调函数,每个子功能独立维护自己的状态机。
HID + UAC 示例
/**
* @brief State machine handling callback
* @param host: Host handle
* @param msg: Event message
* @retval Status
*/
static int usbh_composite_hid_uac_cb_process(usb_host_t *host, u32 msg)
{
usbh_composite_host_t *chost = &usbh_composite_host;
int ret = HAL_BUSY;
/* If the pocess has handle the msg, it return HAL_OK, else return HAL_BUSY */
if ((chost->hid != NULL) && (chost->hid->process != NULL)) {
ret = chost->hid->process(host, msg);
}
if ((ret != HAL_OK) && (chost->uac != NULL) && (chost->uac->process != NULL)) {
ret = chost->uac->process(host, msg);
}
return ret;
}
数据传输处理
数据传输由各子功能类驱动负责调度处理,详细的数据收发过程请参考子功能方案的对应章节。
HID + UAC 示例
音频播放:请参考音频主机方案的类驱动章节。
按钮事件上报:
/*************** HID + UAC Composite Class Driver *********************/ /* USB Standard Host driver */ static usbh_class_driver_t usbh_composite_driver = { .process = usbh_composite_hid_uac_cb_process, }; static int usbh_composite_hid_uac_cb_process(usb_host_t *host, u32 msg) { usbh_composite_host_t *chost = &usbh_composite_host; int ret = HAL_BUSY; //1. HID process if ((chost->hid != NULL) && (chost->hid->process != NULL)) { ret = chost->hid->process(host, msg); } //2. UAC process return ret; } /*************** HID Sub-funtion Class Driver *************************/ /* HID user Callback Interface. */ typedef struct { int(* report)(usbh_composite_hid_event_t *event); } usbh_composite_hid_usr_cb_t; /* USB Standard Class Driver */ const usbh_class_driver_t usbh_composite_hid_driver = { .process = usbh_composite_hid_cb_process, .sof = usbh_composite_hid_cb_sof, }; static int usbh_composite_hid_cb_sof(usb_host_t *host) { usbh_composite_hid_t *hid = &usbh_composite_hid; usbh_pipe_t *pipe = &(hid->pipe); //Regular reporting if (usbh_get_elapsed_ticks(host, pipe->tick) > UBSH_COMPOSITE_HID_TRIGGER_MAX_CNT) { usbh_notify_composite_class_state_change(host, pipe->pipe_num, USBH_COMPOSITE_HID_EVENT); } return HAL_OK; } /* Phase 1: Callback Process */ static int usbh_composite_hid_cb_process(usb_host_t *host, u32 msg) { usbh_composite_hid_t *hid = &usbh_composite_hid; usbh_pipe_t *pipe = &(hid->pipe); usbh_event_t event; event.d32 = msg; // 1. Check if the event belongs to the HID pipe if ((hid->hid_ctrl_buf) && (event.msg.pipe_num == pipe->pipe_num)) { // 2. Clear transfer flag hid->next_xfer = 0; // 3. Handle Interrupt IN transfer state machine usbh_composite_hid_in_process(host); // 4. If transfer started/updated, notify the main host driver if (hid->next_xfer) { usbh_notify_composite_class_state_change(host, pipe->pipe_num, USBH_COMPOSITE_HID_EVENT); } return HAL_OK; } return HAL_BUSY; } /* Phase 2: IN Transfer Process (Producer) */ static void usbh_composite_hid_in_process(usb_host_t *host) { usbh_composite_hid_t *hid = &usbh_composite_hid; usbh_pipe_t *pipe = &(hid->pipe); usbh_urb_state_t urb_state = USBH_URB_IDLE; u32 len; /* Handle Transfer State Machine */ switch (pipe->xfer_state) { // State A: Ready to start new transfer case USBH_EP_XFER_START: if (usbh_get_elapsed_ticks(host, pipe->tick) > pipe->ep_interval) { pipe->tick = usbh_get_tick(host); pipe->xfer_state = USBH_EP_XFER_BUSY; pipe->xfer_len = pipe->ep_mps; usbh_transfer_data(host, pipe);//Start USB Hardware transfer hid->next_xfer = 1; //Flag to notify main state machine } break; // State B: Waiting for transfer completion case USBH_EP_XFER_BUSY: urb_state = usbh_get_urb_state(host, pipe); /* Check if hardware finished transfer */ if (urb_state == USBH_URB_DONE) { len = usbh_get_last_transfer_size(host, pipe); /* Save raw HID data to ring buffer */ usb_ringbuf_add_tail(&(hid->report_msg), pipe->xfer_buf, len, 1); pipe->xfer_state = USBH_EP_XFER_START; //Set for next transfer } break; default: break; } } /* Phase 3: Parsing Thread (Consumer) */ static void usbh_composite_hid_msg_parse_thread(void *param) { UNUSED(param); usbh_composite_hid_t *hid = &usbh_composite_hid; usb_ringbuf_manager_t *handle = &(hid->report_msg); usbh_composite_hid_event_t *event = &(hid->report_event); u8 report_msg[10]; u32 read_cnt; while (hid->parse_task_exit == 0) { // 1. Try to read data from ring buffer read_cnt = usb_ringbuf_remove_head(handle, report_msg, 10, NULL); if (read_cnt) { if (hid->hid_ctrl) { // 2. Parse the raw bytes into a meaningful event ret = usbh_composite_hid_parse_hid_report(report_msg, len, &(hid->vol_caps)); if (ret != HAL_OK) { return; } // 3. Trigger User Callback if ((hid->cb != NULL) && (hid->cb->report != NULL)) { hid->cb->report(event); } } } else { rtos_time_delay_ms(50); } } } /****************** Application *************************/ static usbh_composite_hid_usr_cb_t usbh_hid_cfg = { .report = usbh_hid_cb_report, }; static int usbh_hid_cb_report(usbh_composite_hid_event_t *event) { switch (event->type) { case VOLUME_EVENT_CONSUMER_UP: RTK_LOGS(NOTAG, RTK_LOG_INFO, "=== Executing Volume Up (Consumer) ===\n"); break; case VOLUME_EVENT_CONSUMER_DOWN: RTK_LOGS(NOTAG, RTK_LOG_INFO, "=== Executing Volume Down (Consumer) ===\n"); break; //Other event default: break; } return HAL_OK; }
API 说明
应用示例
本节以 USB Composite (HID + UAC) 为例,介绍完整的应用实现和运行测试的方式。
应用设计
本节详细介绍复合主机驱动的完整开发设计流程,涵盖加载驱动、热插拔管理以及资源释放等核心环节。
加载驱动
使用 Composite 驱动前,需定义配置结构体并注册回调函数,随后调用初始化接口加载 USB 主机核心驱动及 Composite 类驱动。
步骤说明:
硬件配置:配置 USB 速度模式(High Speed/Full Speed)及中断优先级等。
回调注册:定义用户回调结构体
usbh_composite_cb_t,和usbh_composite_fucntion_xx_usr_cb挂载各个阶段的处理函数。加载核心驱动:调用
usbh_init()加载 USB 核心驱动。加载类驱动:调用
usbh_composite_init()加载 Composite 类驱动。
HID + UAC 示例
static usbh_config_t usbh_cfg = {
.speed = USB_SPEED_FULL,
.ext_intr_enable = USBH_SOF_INTR,
.isr_priority = INT_PRI_MIDDLE,
.main_task_priority = USBH_UAC_MAIN_THREAD_PRIORITY,
.tick_source = USBH_SOF_TICK,
};
static usbh_composite_uac_usr_cb_t usbh_uac_cfg = {
.init = usbh_uac_cb_init,
.deinit = usbh_uac_cb_deinit,
.attach = usbh_uac_cb_attach,
.detach = usbh_uac_cb_detach,
.setup = usbh_uac_cb_setup,
.isoc_transmitted = usbh_uac_cb_isoc_transmitted,
.isoc_in_frm_cnt = USBH_UAC_FRAME_CNT,
.isoc_out_frm_cnt = USBH_UAC_FRAME_CNT,
};
static usbh_composite_hid_usr_cb_t usbh_hid_cfg = {
.report = usbh_hid_cb_report,
};
static usbh_user_cb_t usbh_usr_cb = {
.process = usbh_uac_cb_process
};
int ret = 0;
/* Initialize USB host core driver with configuration. */
ret = usbh_init(&usbh_cfg, &usbh_usr_cb);
if (ret != HAL_OK) {
return;
}
/* Initialize class driver with application callback handler. */
ret = usbh_composite_init(&usbh_hid_cfg, &usbh_uac_cfg);
if (ret != HAL_OK) {
/* If class driver init fails, clean up the core driver */
usbh_deinit();
return;
}
热插拔事件处理
作为主机,系统必须能够健壮地处理 USB 设备的动态插入与移除,SDK 默认支持热插拔机制。
处理逻辑:
设备插入 (Attach):USB 核心检测到设备,重新执行枚举和驱动加载流程。
设备拔出 (Detach):触发回调释放信号量,应用线程捕获后执行反初始化,并释放堆内存。
HID + UAC 示例
static rtos_sema_t usbh_uac_detach_sema;
/* USB detach callback */
static usbh_composite_uac_usr_cb_t usbh_uac_cfg = {
.detach = usbh_uac_detach_sema,
};
/* Callback executed in ISR context */
static int usbh_uac_cb_detach(void)
{
RTK_LOGS(TAG, RTK_LOG_INFO, "UAC DETACH\n");
rtos_sema_give(usbh_uac_detach_sema);
return HAL_OK;
}
static void usbh_uac_hotplug_thread(void *param)
{
int ret = 0;
UNUSED(param);
for (;;) {
if (rtos_sema_take(usbh_uac_detach_sema, RTOS_SEMA_MAX_COUNT) == RTK_SUCCESS) {
rtos_time_delay_ms(100);//make sure disconnect handle finish before deinit.
/* 1. Clean up resources */
usbh_composite_deinit();
/* 2. De-initialize USB core */
usbh_deinit();
rtos_time_delay_ms(10);
RTK_LOGS(TAG, RTK_LOG_INFO, "Free heap: 0x%x\n", rtos_mem_get_free_heap_size());
/* 3. Re-initialize for next connection */
ret = usbh_init(&usbh_cfg, &usbh_usr_cb);
if (ret != HAL_OK) {
break;
}
ret = usbh_composite_init(&usbh_hid_cfg, &usbh_uac_cfg);
if (ret < 0) {
usbh_deinit();
break;
}
}
}
RTK_LOGS(TAG, RTK_LOG_ERROR, "Hotplug thread fail\n");
rtos_task_delete(NULL);
}
数据收发处理
应用层在设备连接后,可调用面向应用的 API,进行参数的配置、数据传输的调度等。
通过应用层在初始化时注册的 usbh_composite_cb_t 、 usbh_user_cb_t 和子功能驱动的回调函数 usbh_composite_function_class_xx_usr_cb_t 来获得设备状态和传输结果。
详细的数据收发过程请参考子功能方案的对应章节。
卸载驱动
在不再需要 USB 功能或系统关机时,按加载的反序释放资源。
HID + UAC 示例
/* Deinitialize Vendor class driver. */
usbd_composite_deinit();
/* Deinitialize USB host core driver. */
usbd_deinit();
运行方式
该示例演示了如何通过复合主机协议栈,将 Ameba 开发板配置为同时具备 USB HID (人机接口设备) 与 UAC (音频设备类) 功能的主机。
当开发板连接至标准 USB 耳机时,系统将识别出两个独立的逻辑设备。开发板会将音频数据发送到耳机,如果耳机上的按钮被按下,主机将识别该命令。
该示例路径: {SDK}/example/usb/usbh_composite_hid_uac ,可为开发者设计自定义 Composite 产品提供完整的参考方案。
配置与编译
编译与烧录
在 SDK 根目录下执行以下命令以配置环境,选择目标 SoC,编译工程,然后将生成的
Image文件烧录至开发板。# Initialize environment (required for every new terminal) source env.sh or env.bat(Windows system) # Select Target SoC (replace xxx with your specific SoCs) ameba.py soc xxx ameba.py build -a usbh_composite_hid_uac -p
Menuconfig 配置确认
若编译失败,请执行
ameba.py menuconfig,确认已选择USBH Composite HID + UAC 1.0。- Choose `CONFIG USB --->`: [*] Enable USB USB Mode (Host) ---> [*] Composite Select Composite Class (HID + UAC) ---> (X) HID + UAC Select UAC Version (UAC 1.0) --->
结果验证
启动设备
重启开发板,观察串口日志,应显示如下启动信息:
[COMP-I] USBH UAC&HID composite demo start
连接主机
使用 USB 线缆将开发板连接 USB Type-C 有线耳机。
自动播放测试
Ameba 开发板将识别出耳机,并自动进行音频播放测试。
录音测试
若耳机带有麦克风,将扬声器连接至开发板音频输出。对麦克风讲话,采集到的数据将通过扬声器实时播放。
音量控制测试
如果耳机还带有音量控制按钮,当按下按钮时,串口上将输出音量变化信息:
``` Volume Up Volume Down ```