杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD23N SDK 概述与芯片平台
    • 工程结构与模块划分
  • 快速开始

    • 开发环境搭建与工具链
    • 编译构建指南
    • 烧录与固件升级工具
  • 应用框架与产品工作流

    • 应用入口与模式调度
    • 音乐播放应用
    • MIDI 解码与键盘演奏
    • 录音应用
    • LINEIN 与扩音应用
    • USB 从设备应用
    • 待机、软关机与空闲检测
    • 公共 UI 与 LED 显示
  • 音频子系统

    • 音频解码器框架
    • 音频编码器框架
    • 音效算法库
    • 音频管理与输出通路
  • 存储与文件系统

    • 文件系统层
    • NOR Flash 与虚拟机存储
    • 设备与设备管理
  • 系统服务与运行时

    • 消息机制与事件分发
    • 按键扫描与输入处理
    • 电源管理与低功耗控制
    • 定时器与系统任务
  • 外设驱动与平台

    • CPU 平台与启动流程
    • USB 协议栈与主机/设备驱动
    • SPI 与通用外设接口
  • 固件升级与构建工具

    • 固件升级机制
    • 编译后处理与镜像打包
    • 构建系统与命令行工具

USB 协议栈与主机/设备驱动

AD23N SDK 的 USB 子系统完整参考:包含设备(Device/Slave)协议栈、主机(Host)协议栈、BSP 配置适配层与 PHY 传输抽象层,覆盖 USB 枚举、控制传输、Bulk/中断/同步端点传输、设备类接口与挂载/卸载生命周期管理。

Purpose and Scope

本文档描述 AD23N 平台 USB 子系统从底层 PHY 到上层设备类接口的完整实现机制,包括:

  • 设备模式(USB Device/Slave):usb_stack.h 定义的 EP0 控制传输状态机、接口 handler 分发与描述符拼接机制;
  • 主机模式(USB Host):usb_host.h 定义的挂载/卸载流程、设备类驱动接口(mass_storage、adb、hid、aoa)与信号量同步;
  • PHY 传输层:usb_phy.h 暴露的 Bulk/中断/同步端点读写 API 与字节序转换宏;
  • BSP 配置层:usb_config.h 提供的模式配置、释放与中断注册接口。

本文不深入具体应用层业务(如 USB 音频类应用细节、mbox_flash 项目专用逻辑),这些属于各自应用/业务页面;本文只说明协议栈如何被它们使用。

Overview

AD23N 是杰理科技(Jieli)面向嵌入式场景的 MCU,其 USB 控制器(SIE)可通过 OTG 方式在主机模式与设备模式之间切换。SDK 将 USB 子系统拆分为两个相互独立的协议栈(设备栈与主机栈),二者共享同一套 PHY 传输层 API,由 BSP 层负责模式选择与中断配置。这种"双栈 + 共享 PHY"的结构使得同一份芯片资源可以灵活承载不同应用:

  • 设备模式:让 AD23N 作为 USB 外设(如 U 盘、声卡、ADB 设备)连接 PC/手机,核心是 EP0 的 setup 状态机与各类描述符的组织;
  • 主机模式:让 AD23N 作为 USB 主机(如读 U 盘、连接 HID 键鼠、AOA 手机),核心是总线复位、枚举、设备地址分配与设备类驱动的挂载。

两个协议栈都以 C 语言回调(函数指针)作为扩展点:设备栈通过 itf_hander / desc_config 把不同的 USB 接口(audio、mass storage 等)挂接到枚举流程;主机栈通过 interface_ctrl 把具体设备类驱动(disk/adb/hid/aoa)注册到接口描述符之上。

Architecture

USB 子系统整体分层如下(各层命名均取自仓库中的实际头文件):

flowchart TD
    subgraph sg_App["应用层 (sdk/app)"]
        App["应用任务<br/>(mbox_flash/usb_slave.c 等)"]
        AudioItf["USB Audio 接口<br/>(usr/usb_audio_interface.h)"]
    end

    subgraph sg_Stack["协议栈层 (sdk/include_lib/device/usb)"]
        DevStack["设备栈 usb_stack.h<br/>usb_device_t / usb_setup_t / EP0 状态机"]
        HostStack["主机栈 usb_host.h<br/>usb_host_device / interface_ctrl / 挂载流程"]
    end

    subgraph sg_Bsp["BSP 配置层 (sdk/app/bsp/common/usb)"]
        UsbConfig["usb_config.h<br/>usb_config / usb_release<br/>usb_h_isr_reg / usb_g_isr_reg / usb_sof_isr_reg"]
    end

    subgraph sg_Phy["PHY 抽象层 (usb_phy.h)"]
        Phy["bulk / intr / iso 读写 API<br/>字节序宏与寄存器辅助函数"]
    end

    subgraph sg_Hw["硬件层 (asm/usb.h)"]
        Hw["USB SIE 控制器 / DP-DM 引脚"]
    end

    App --> DevStack
    App --> HostStack
    AudioItf --> DevStack
    DevStack --> UsbConfig
    HostStack --> UsbConfig
    DevStack --> Phy
    HostStack --> Phy
    UsbConfig --> Phy
    Phy --> Hw

各层职责与设计意图:

  • 设备栈(usb_stack.h):维护 struct usb_device_t(含 EP0 setup 状态机字段)和 struct usb_setup_t(设备 + 控制请求 + 最多 5 个接口 handler 的聚合)。设计意图是让"协议状态机"与"接口业务"解耦——状态机由栈驱动,具体接口如何响应请求、如何拼接描述符由注册的 itf_hander / desc_config 回调决定。
  • 主机栈(usb_host.h):以 struct usb_host_device 为宿主设备抽象,通过 struct interface_ctrl(set_power / get_power / ioctl)和 struct usb_interface_info 的联合体(mass_storage / adb / hid / aoa)把底层枚举与上层设备类驱动解耦;用 OS_SEM(信号量)做挂载/传输的同步原语。
  • BSP 配置层(usb_config.h):是栈与硬件之间的"装配车间"——usb_config() / usb_release() 完成模式配置与资源释放,usb_h_isr_reg / usb_g_isr_reg / usb_sof_isr_reg 负责把中断服务例程绑定到指定优先级与 CPU 核(AD23N 为多核架构)。
  • PHY 抽象层(usb_phy.h):对上层提供对称的传输 API——设备侧 usb_g_*(g = gadget/device),主机侧 usb_h_*;同时提供 cpu_to_be16/be32、le16_to_cpu 等字节序宏与 LOWORD/HIBYTE 等位域宏,供构造 USB 描述符与解析请求使用。
  • 硬件层(asm/usb.h):芯片寄存器定义,由 BSP/PHY 层间接引用。

设备模式(USB Device/Slave)实现

核心数据结构:struct usb_device_t

设备栈以 usb_device_t 为每次 USB 会话的"心脏",它把 EP0 控制传输所需的全部状态内聚在一个结构体中:

struct usb_device_t {
    u8 baddr;
    u8 bsetup_phase;          //ep0 setup状态机
    u16 wDataLength;    //ep0 setup data stage数据长度

    u8 *setup_buffer;   //本次传输的bufer地址
    u8 *setup_ptr;      //当前传输的位置
    u32(*setup_hook)(struct usb_device_t *, struct usb_ctrlrequest *);
    u32(*setup_recv)(struct usb_device_t *, struct usb_ctrlrequest *);

    u8 bDeviceStates;
    u8 bDataOverFlag;    //ep0 0包标识
    u8 bRemoteWakup: 1;
    u8 res: 7;
    u8 wDeviceClass;    // 设备类
};

Source: usb_stack.h

设计要点:

  • bsetup_phase + wDataLength + setup_ptr 共同构成 EP0 setup 状态机:控制传输被拆分为 setup / data / status 三个阶段,栈通过这几个字段记住"当前处于哪个阶段、还差多少数据"。
  • setup_hook 与 setup_recv 是两个可替换的回调:setup_hook 在收到 setup 包后、正式处理前被调用(可做过滤/预处理);setup_recv 负责接收请求后的分发。user_setup_filter_install() 提供了安装用户级 setup 过滤器的入口。
  • bDataOverFlag 标记 EP0 的 0 长度包(用于 data 阶段结束时的短包收尾,USB 规范要求短包标识传输结束)。
  • bRemoteWakup 是位域字段,支持远程唤醒特性。

设备状态机

设备栈显式枚举了 USB 规范定义的标准设备状态:

enum {
    USB_ATTACHED,
    USB_POWERED,
    USB_DEFAULT,
    USB_ADDRESS,
    USB_CONFIGURED,
    USB_SUSPENDED
};

Source: usb_stack.h

bDeviceStates 字段记录当前状态。状态迁移由中断/事件驱动:上电进入 USB_ATTACHED/USB_POWERED,总线复位进入 USB_DEFAULT,收到 SET_ADDRESS 进入 USB_ADDRESS,收到 SET_CONFIGURATION 进入 USB_CONFIGURED,之后接口 handler 才真正开始业务收发;总线挂起则进入 USB_SUSPENDED。

接口注册与描述符拼接

设备栈用一组函数指针类型把"协议处理"与"具体接口"解耦:

typedef u32(*itf_hander)(struct usb_device_t *usb_device, struct usb_ctrlrequest *);
typedef void(*itf_reset_hander)(struct usb_device_t *, u32 itf);
typedef void(*usb_interrupt)(struct usb_device_t *, u32 ep);
typedef u32(*desc_config)(const usb_dev usb_id, u8 *ptr, u32 *cur_itf_num);

Source: usb_stack.h

  • itf_hander:接口控制请求处理函数,收到 SET_INTERFACE / 类请求时被调用;
  • itf_reset_hander:接口复位回调,总线复位或 usb_reset_interface() 时触发;
  • usb_interrupt:端点中断回调,端点有数据或传输完成时触发;
  • desc_config:配置描述符拼接回调,usb_add_desc_config() 可注册多个(按 index 排序),栈在收到 GET_DESCRIPTOR(Configuration) 时依次调用它们把设备/配置/接口/端点描述符连续写入返回缓冲区,cur_itf_num 用于跨回调传递当前接口编号。

聚合结构 usb_setup_t 把设备、请求和两组回调数组绑在一起:

struct usb_setup_t {
    struct usb_device_t usb_device;
    struct usb_ctrlrequest request;
    itf_hander interface_hander[MAX_INTERFACE_NUM];
    itf_reset_hander reset_hander[MAX_INTERFACE_NUM];
} __attribute__((aligned(4)));

Source: usb_stack.h

MAX_INTERFACE_NUM 为 5,即一个复合设备最多支持 5 个接口(如同时做 U 盘 + 声卡 + ADB);__attribute__((aligned(4))) 保证 DMA/控制器访问对齐。usb_setup_init() 初始化该结构并绑定 setup 缓冲区(USB_SETUP_SIZE = 512 字节,覆盖最大控制传输 data 阶段),usb_setup_release() 释放。

设备模式枚举时序

sequenceDiagram
    participant H as USB 主机 (PC/手机)
    participant D as usb_device_t 状态机
    participant S as usb_setup_t / EP0
    participant I as itf_hander / desc_config

    H->>D: 总线复位 (RESET)
    D->>S: 状态 -> USB_DEFAULT
    H->>S: GET_DESCRIPTOR(Device)
    S->>D: setup_hook 预处理
    D-->>H: 返回设备描述符
    H->>S: SET_ADDRESS(addr)
    S->>D: baddr=addr, 状态 -> USB_ADDRESS
    H->>S: GET_DESCRIPTOR(Configuration)
    S->>I: 依次调用 desc_config 拼接
    I-->>S: 设备/配置/接口/端点描述符
    H->>S: SET_CONFIGURATION
    S->>D: 状态 -> USB_CONFIGURED
    Note over I: 应用接口开始 bulk/intr 收发

该流程完全由栈驱动:usb_control_transfer() 是 EP0 控制传输的驱动器,usb_set_data_payload() 把 data 阶段数据装入 setup 缓冲区并返回指针供继续填充,usb_set_setup_phase() 显式推进 setup 状态机。设备类通过 usb_device_set_class() 设定,usb_device_mode() 选择具体设备模式(class 参数决定启用哪些类逻辑)。

主机模式(USB Host)实现

核心数据结构:usb_host_device 与接口驱动

主机栈以 struct usb_host_device 抽象一个已挂载的 USB 从设备,内部包含私有数据、同步信号量与接口驱动信息:

struct usb_private_data {
    usb_dev usb_id;
    u8 status;
    u8 devnum;
    u8 ep0_max_packet_size;
};

struct usb_host_device {
    OS_SEM *sem;
    struct usb_private_data private_data;
    const struct usb_interface_info *interface_info[MAX_HOST_INTERFACE];
};

Source: usb_host.h

  • usb_private_data 记录主机分配的 devnum(设备地址)与 ep0_max_packet_size(EP0 最大包长,枚举时从设备描述符读出);
  • sem 是 OS_SEM 信号量,用于挂载/传输同步;
  • interface_info 数组容量 MAX_HOST_INTERFACE = 1(当前平台单接口主机设备),指向接口驱动信息。

设备类驱动的抽象是 interface_ctrl 与 usb_interface_info:

struct interface_ctrl {
    u8 interface_class;
    int (*set_power)(struct usb_host_device *host_dev, u32 value);
    int (*get_power)(struct usb_host_device *host_dev, u32 value);
    int (*ioctl)(struct usb_host_device *host_dev, u32 cmd, u32 arg);
};

struct usb_interface_info {
    struct interface_ctrl *ctrl;
    union {
        struct mass_storage *disk;
        struct adb_device_t *adb;
        struct hid_device_t *hid;
        struct aoa_device_t *aoa;
        void *p;
    } dev;
};

Source: usb_host.h

设计意图:interface_ctrl 提供与具体设备类无关的电源/IOCTL 操作面(主机栈可统一管理设备上下电),dev 联合体按 interface_class 关联到具体类驱动实例(U 盘 mass storage、ADB、HID、AOA)。新增设备类只需扩展联合体并实现 interface_ctrl 回调即可接入,无需改动枚举核心。

挂载/卸载生命周期

主机栈提供完整的生命周期 API:usb_host_init() 初始化控制器,usb_host_mount() 挂载,usb_host_unmount() 卸载,usb_host_remount() 带重试/通知的重新挂载,usb_host_suspend() / usb_host_resume() 管理总线挂起与恢复,usb_h_force_reset() 强制复位总线。

flowchart TD
    Start([usb_host_mount usb_id, retry, reset_delay, mount_timeout]) --> Init["usb_host_init / 复位总线"]
    Init --> Enum["枚举: 读设备描述符<br/>分配 devnum / ep0_max_packet_size"]
    Enum --> Chk{"ret == 0 ?"}
    Chk -->|"是"| Done["挂载成功, 返回 0<br/>interface_info 可访问"]
    Chk -->|"否且 DEV_ERR_OFFLINE"| Offline["check_usb_mount:<br/>log_info + goto __exit_fail"]
    Chk -->|"其它错误"| Retry["继续重试 (retry 次)<br/>reset_delay / mount_timeout 控制节奏"]
    Retry --> Enum
    Offline --> Unmount["usb_host_unmount"]
    Done --> Use["上层使用: bulk/intr/iso 传输"]

挂载失败处理由 check_usb_mount 宏统一收敛——它把 -DEV_ERR_OFFLINE(设备离线/拔出)与其它错误区分开:离线直接跳转到 __exit_fail 标签(调用方负责清理),其它错误则打印日志后 continue 重试:

#define     check_usb_mount(ret)    \
    if(ret == -DEV_ERR_OFFLINE){\
        log_info("%s() @ %d DEV_ERR_OFFLINE\n", __func__, __LINE__);\
        goto __exit_fail;\
    } else if(ret){\
        log_info("%s() @ %d %x\n", __func__, __LINE__, ret);\
        continue;\
    }

Source: usb_host.h

该宏的 goto/continue 设计依赖调用方所在循环/标签上下文,属于嵌入式 SDK 常见的"约定式错误处理",使用时需按相同模式组织挂载循环。

信号量与中断回调

主机栈用四个信号量封装函数替代裸 OS 调用,保证与协议栈的抽象边界:

int usb_sem_init(struct usb_host_device *host_dev);
int usb_sem_pend(struct usb_host_device *host_dev, u32 timeout);
int usb_sem_post(struct usb_host_device *host_dev);
int usb_sem_del(struct usb_host_device *host_dev);

Source: usb_host.h

端点中断通过 usb_h_set_ep_isr()(绑定 handler 与私有参数 p)或 usb_h_set_intr_hander()(按 usb_dev 注册)挂接,中断回调类型为 usb_h_interrupt。传输完成的唤醒通常就是"ISR → usb_sem_post → 等待者 usb_sem_pend 返回",这是典型的嵌入式中断-任务同步模式。

PHY 传输层(usb_phy.h)

PHY 层为设备/主机两侧提供对称的传输 API,并统一处理字节序与超时。

设备侧(slave api)

/*            slave api            */
u32 usb_g_bulk_read64byte_fast(const usb_dev usb_id, u32 ep, u8 *ptr, u32 len);
u32 usb_g_bulk_read(const usb_dev usb_id, u32 ep, u8 *ptr, u32 len, u32 block);
u32 usb_g_bulk_write(const usb_dev usb_id, u32 ep, const u8 *ptr, u32 len);
u32 usb_g_intr_read(const usb_dev usb_id, u32 ep, u8 *ptr, u32 len, u32  block);
u32 usb_g_intr_write(const usb_dev usb_id, u32 ep, const u8 *ptr, u32 len);
u32 usb_g_iso_read(const usb_dev usb_id, u32 ep, u8 *ptr, u32 len, u32 block);
u32 usb_g_iso_write(const usb_dev usb_id, u32 ep, const u8 *ptr, u32 len);
void usb_slave_init(const usb_dev usb_id);

Source: usb_phy.h

主机侧(host api)

/*            host api            */
u32 usb_h_bulk_read(const usb_dev usb_id, u8 host_ep, u16 rxmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_bulk_write(const usb_dev usb_id, u8 host_ep, u16 txmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_intr_read(const usb_dev usb_id, u8 host_ep, u16 rxmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_intr_write(const usb_dev usb_id, u8 host_ep, u16 txmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_iso_read(const usb_dev usb_id, u8 host_ep, u16 rxmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_iso_write(const usb_dev usb_id, u8 host_ep, u16 txmaxp, u8 target_ep, u8 *ptr, u32 len);
void usb_h_entry_suspend(const usb_dev usb_id);
void usb_h_resume(const usb_dev usb_id);
u32 usb_host_init(const usb_dev usb_id, u32 reset_delay, u32 timeout);
u32 usb_host_reset(const usb_dev usb_id, u32 reset_delay, u32 timeout);
u32 usb_h_force_reset(const usb_dev usb_id);

Source: usb_phy.h

主机侧传输函数多出 host_ep / rxmaxp(或 txmaxp) / target_ep 三个参数:主机需要显式指定"本地用于传输的端点号、本地端点最大包长、目标从设备的端点号";而设备侧只需本地 ep。block 参数控制阻塞/非阻塞行为。

字节序与位域辅助宏

USB 描述符多字段为小端序、SOF 帧号等为大端场景,PHY 层提供统一宏:

#define cpu_to_be16(v16) ___ntohl(v16)
#define cpu_to_be32(v32) ___ntohs(v32)
#define be16_to_cpu(v16) cpu_to_be16(v16)
#define cpu_to_le16(v16) (v16)
#define cpu_to_le32(v32) (v32)
#define le16_to_cpu(v16) cpu_to_le16(v16)
#define LOBYTE(w)           ((u8)(w))
#define HIBYTE(w)           ((u8)(((u16)(w) >> 8) & 0xFF))
#define DW1BYTE(dw)         (LOBYTE(LOWORD(dw)))

Source: usb_phy.h

小端宏为恒等映射(MCU 本身小端),大端宏执行字节交换;LOWORD/HIWORD/LOBYTE/HIBYTE/DW1-4BYTE 系列用于从 16/32 位值中抽取字段,是构造 wMaxPacketSize、bcdUSB 等描述符字段的常用工具。usb_host_timeout() 与 get_jiffies() 提供超时计算基础,usb_read_sofframe() / usb_read_dp_se() / usb_read_dm_se() 读取 SOF 帧号与 DP/DM 线状态(用于 OTG 检测与调试)。

BSP 配置层(usb_config.h)

usb_config.h 是协议栈与板级代码之间的装配接口,同时引用设备栈与主机栈头文件,向应用暴露统一的配置入口:

void usb_host_config(usb_dev usb_id);
void usb_host_free(usb_dev usb_id);
void *usb_h_get_ep_buffer(const usb_dev usb_id, u32 ep);
void usb_h_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id);
void usb_g_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id);
void usb_sof_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id);
void *usb_get_ep_buffer(const usb_dev usb_id, u32 ep);
u32 usb_config(const usb_dev usb_id);
u32 usb_release(const usb_dev usb_id);
void usb_otg_sof_check_init(const usb_dev id);

Source: usb_config.h

关键点:

  • usb_config() / usb_release():设备模式下的配置与释放(对应设备栈的 setup 初始化/释放),应用切换模式前必须先 release 再 config;
  • usb_host_config() / usb_host_free():主机模式下的配置与释放;
  • usb_g_isr_reg() / usb_h_isr_reg() / usb_sof_isr_reg():分别注册设备中断、主机中断与 SOF 中断,均带 priority 与 cpu_id 参数——AD23N 多核架构要求中断显式绑定到某个核,这是低延迟 USB 传输的关键配置;
  • usb_get_ep_buffer() / usb_h_get_ep_buffer():获取端点专用 DMA 缓冲区(设备侧/主机侧),批量传输的数据指针通常要求落在这些缓冲区中。

使用示例

示例 1:设备模式注册接口 handler 与描述符

设备模式应用(如 sdk/app/src/mbox_flash/usb_slave/usb_slave.c)的典型装配流程是:usb_device_mode() 选择设备类 → usb_set_interface_hander() 注册各接口 handler → usb_add_desc_config() 注册描述符拼接回调 → usb_setup_init() 启动 EP0。以下回调类型与注册接口来自设备栈:

typedef u32(*itf_hander)(struct usb_device_t *usb_device, struct usb_ctrlrequest *);
typedef void(*itf_reset_hander)(struct usb_device_t *, u32 itf);
typedef u32(*desc_config)(const usb_dev usb_id, u8 *ptr, u32 *cur_itf_num);

u32 usb_g_set_intr_hander(const usb_dev usb_id, u32 ep, usb_interrupt hander);
u32 usb_set_interface_hander(const usb_dev usb_id, u32 itf_num, itf_hander hander);
void usb_add_desc_config(const usb_dev usb_id, u32 index, const desc_config desc);
u32 usb_set_reset_hander(const usb_dev usb_id, u32 itf_num, itf_reset_hander hander);

Source: usb_stack.h

示例 2:主机模式挂载循环

主机模式应用按"retry + check_usb_mount"模式组织挂载,配合 usb_host_remount() 实现热插拔重挂载:

u32 usb_host_mount(const usb_dev usb_id, u32 retry, u32 reset_delay, u32 mount_timeout);
u32 usb_host_unmount(const usb_dev usb_id);
u32 usb_host_remount(const usb_dev usb_id, u32 retry, u32 delay, u32 ot, u8 notify);

#define     check_usb_mount(ret)    \
    if(ret == -DEV_ERR_OFFLINE){\
        log_info("%s() @ %d DEV_ERR_OFFLINE\n", __func__, __LINE__);\
        goto __exit_fail;\
    } else if(ret){\
        log_info("%s() @ %d %x\n", __func__, __LINE__, ret);\
        continue;\
    }

Sources: usb_host.h、usb_host.h

示例 3:主机批量读写

主机向从设备端点 target_ep 发起批量传输,需同时给出本地 host_ep 与最大包长:

u32 usb_h_bulk_read(const usb_dev usb_id, u8 host_ep, u16 rxmaxp, u8 target_ep, u8 *ptr, u32 len);
u32 usb_h_bulk_write(const usb_dev usb_id, u8 host_ep, u16 txmaxp, u8 target_ep, u8 *ptr, u32 len);

Source: usb_phy.h

Configuration Options

USB 子系统无集中式配置文件,行为通过 API 参数与常量控制:

配置项类型默认值说明
MAX_INTERFACE_NUM宏常量5设备栈最大接口数(interface_hander / reset_hander 数组容量)
USB_SETUP_SIZE宏常量512EP0 setup 缓冲区大小(覆盖最大控制传输)
MAX_HOST_INTERFACE宏常量1主机设备支持的接口数上限
HUSB_MODE宏常量0主机模式标识
usb_config(usb_id)函数—设备模式配置入口
usb_release(usb_id)函数—设备模式资源释放
usb_host_config(usb_id)函数—主机模式配置入口
usb_host_free(usb_id)函数—主机模式资源释放
usb_h_isr_reg(id, prio, cpu)函数—主机中断注册(优先级 + CPU 核绑定)
usb_g_isr_reg(id, prio, cpu)函数—设备中断注册(优先级 + CPU 核绑定)
usb_sof_isr_reg(id, prio, cpu)函数—SOF 中断注册
usb_host_mount(id, retry, reset_delay, timeout)函数由调用方决定挂载重试次数、复位延时与超时
usb_host_remount(id, retry, delay, ot, notify)函数由调用方决定重挂载参数与通知开关
usb_g_bulk_read(..., block)函数参数由调用方决定设备侧批量读是否阻塞
usb_h_bulk_read(..., rxmaxp, ...)函数参数由调用方决定主机侧端点最大包长

API Reference

设备栈(usb_stack.h)

usb_dev usb_device2id(const struct usb_device_t *usb_device)

设备结构体 → 设备 ID 的反向映射(供中断/API 定位控制器)。

struct usb_device_t *usb_id2device(const usb_dev usb_id)

设备 ID → 设备结构体,中断处理中由 usb_dev 快速取回 usb_device_t。

void usb_control_transfer(struct usb_device_t *usb_device)

EP0 控制传输驱动器:根据 bsetup_phase 推进 setup/data/status 三阶段,是设备枚举的核心执行函数。

void usb_device_set_class(struct usb_device_t *usb_device, u32 class_config)

设置设备类(写入 wDeviceClass),决定设备以何种类向主机呈现。

u32 usb_g_set_intr_hander(const usb_dev usb_id, u32 ep, usb_interrupt hander)

注册指定端点的中断回调(设备侧)。hander 为 void(*)(struct usb_device_t *, u32 ep)。

u32 usb_set_interface_hander(const usb_dev usb_id, u32 itf_num, itf_hander hander)

注册接口控制请求 handler,itf_num 取值范围 0..MAX_INTERFACE_NUM-1。

void usb_add_desc_config(const usb_dev usb_id, u32 index, const desc_config desc)

按 index 顺序注册配置描述符拼接回调;多个回调依次执行,通过 cur_itf_num 传递接口编号。

u32 usb_set_reset_hander(const usb_dev usb_id, u32 itf_num, itf_reset_hander hander)

注册接口复位回调,总线复位时由 usb_reset_interface() 触发。

void usb_set_setup_hook / usb_set_setup_recv(struct usb_device_t *, void *hook/recv)

安装 setup 预处理钩子与接收分发回调(setup_hook / setup_recv 字段)。

int usb_device_mode(const usb_dev usb_id, const u32 class)

进入指定设备模式(class 选择设备类组合)。

void usb_setup_init(const usb_dev usb_id, void *ptr, u8 *setup_buffer) / u32 usb_setup_release(const usb_dev usb_id)

初始化/释放 usb_setup_t 与 EP0 setup 缓冲区(USB_SETUP_SIZE)。

u8 *usb_set_data_payload(struct usb_device_t *usb_device, struct usb_ctrlrequest *req, const void *data, u32 len)

装载 EP0 data 阶段数据:把 data/len 拷贝入 setup 缓冲区并更新传输指针,返回当前写入位置。

void usb_set_setup_phase(struct usb_device_t *usb_device, u8 setup_phase)

显式推进 EP0 setup 状态机阶段。

void usb_ep_enable(const usb_dev usb_id, u32 ep, u32 is_enable)

使能/禁用指定端点。

void usb_start() / usb_stop() / usb_pause()

全局 USB 启动/停止/暂停控制。

主机栈(usb_host.h)

int host_dev_status(const struct usb_host_device *host_dev)

查询宿主设备当前状态(来自 private_data.status)。

const struct usb_host_device *host_id2device(const usb_dev id)

主机设备 ID → usb_host_device 结构体映射。

u32 usb_host_mount(const usb_dev usb_id, u32 retry, u32 reset_delay, u32 mount_timeout)

挂载 USB 从设备。返回: 0 成功,-DEV_ERR_OFFLINE 设备离线,其它负值错误。retry 控制重试次数,reset_delay 为复位间隔,mount_timeout 为挂载超时。

u32 usb_host_unmount(const usb_dev usb_id)

卸载并从总线上摘除设备。

u32 usb_host_remount(const usb_dev usb_id, u32 retry, u32 delay, u32 ot, u8 notify)

带参数的重挂载;ot 为超时,notify 控制是否通知上层。

void usb_host_suspend(const usb_dev usb_id) / void usb_host_resume(const usb_dev usb_id)

总线挂起/恢复(配合 usb_h_entry_suspend / usb_h_resume PHY 层实现)。

void usb_h_set_ep_isr(struct usb_host_device *host_dev, u32 ep, usb_h_interrupt hander, void *p) / u32 usb_h_set_intr_hander(const usb_dev usb_id, u32 ep, usb_h_interrupt hander)

注册主机端点中断回调,usb_h_set_ep_isr 额外携带私有参数 p。

PHY 层(usb_phy.h)

  • usb_g_bulk_read/write、usb_g_intr_read/write、usb_g_iso_read/write:设备侧批量/中断/同步端点传输,read 变体带 block 阻塞标志;
  • usb_g_bulk_read64byte_fast:64 字节快速批量读,面向高吞吐热路径;
  • usb_h_bulk_read/write、usb_h_intr_read/write、usb_h_iso_read/write:主机侧对应传输,需指定本地/目标端点与最大包长;
  • usb_host_init(usb_id, reset_delay, timeout) / usb_host_reset(usb_id, reset_delay, timeout) / usb_h_force_reset(usb_id):主机初始化与总线复位;
  • usb_read_sofframe(id)、usb_read_dp_se(id)、usb_read_dm_se(id):读取 SOF 帧号与 DP/DM 线状态。

故障模式、边界情况与并发

设备离线与挂载失败

主机模式下最常见的故障是设备在传输中途被拔出。SDK 的约定处理是 check_usb_mount 宏:返回 -DEV_ERR_OFFLINE 时记录日志并 goto __exit_fail 走清理路径;其它错误记录日志后 continue 继续重试。因此应用层挂载循环必须预留 __exit_fail 标签并保证循环结构匹配,否则宏展开会编译失败。usb_host_remount() 的 notify 参数允许在重挂载成功后通知上层刷新状态(如重新打开文件系统)。

EP0 短包与 0 包

控制传输 data 阶段要求"数据长度恰好是最大包长整数倍时补发 0 长度包"以标识传输结束,设备栈用 bDataOverFlag 字段跟踪。usb_set_data_payload() 负责把待发送数据写入 setup 缓冲区,若应用手工推进 bsetup_phase(usb_set_setup_phase())而非使用栈内建流程,必须自行处理短包规则,否则主机可能挂起等待。

中断并发与信号量同步

  • 主机栈的 OS_SEM 信号量由 ISR 与任务共享:usb_sem_post 通常在中断上下文调用(传输完成),usb_sem_pend(timeout) 在任务上下文等待。timeout 必须合理设置,避免挂载超时/传输超时时任务永久阻塞。
  • usb_h_set_ep_isr 携带私有指针 p,回调在中断上下文执行,禁止在回调内做耗时操作(如日志、内存分配)。
  • block 参数区分阻塞/非阻塞传输:非阻塞模式下返回值语义为"是否已提交",上层需要配合中断回调与信号量判断完成。

多核中断绑定

AD23N 为多核架构,usb_g_isr_reg / usb_h_isr_reg / usb_sof_isr_reg 均要求 cpu_id 参数。若中断绑定核与等待信号量的任务所在核不一致,需确保 OS 信号量支持跨核唤醒;否则应把 USB 中断与处理任务绑定到同一核以减少跨核开销与竞态。

模式切换竞态

设备/主机模式切换必须遵循"先 release 后 config"顺序:usb_release() 释放设备栈资源后再 usb_host_config() 进入主机模式(或反之)。切换期间应暂停相关任务,避免在 setup 状态机或挂载流程进行中被 usb_stop()/usb_pause() 打断产生悬挂状态。

性能与运维注意事项

  • DMA 缓冲区对齐与归属:端点传输的数据指针应取自 usb_get_ep_buffer() / usb_h_get_ep_buffer(),这些缓冲区位于 DMA 可访问内存且按端点对齐;直接使用栈/堆随机地址可能触发复制路径或传输失败。
  • 64 字节快速路径:usb_g_bulk_read64byte_fast() 是面向 64 字节包的热路径优化,音频/小包场景应优先使用;大块数据传输使用 usb_g_bulk_read(..., block)。
  • 超时参数:usb_host_mount 的 reset_delay / mount_timeout、usb_host_timeout() 计算应匹配总线拓扑与设备响应能力,U 盘类设备枚举通常需要数百 ms 级超时。
  • 中断优先级:USB 中断优先级应高于易失数据的任务,低于时钟/异常关键中断;多核绑定时选择负载较低且与 USB 消费任务同核的 CPU。

扩展点

USB 子系统的扩展完全基于回调与注册机制,无需修改协议栈内核:

扩展点接口用途
设备类接口usb_set_interface_hander(usb_id, itf_num, itf_hander)新增/替换接口的控制请求处理
描述符拼接usb_add_desc_config(usb_id, index, desc_config)自定义设备/配置/接口/端点描述符内容
接口复位usb_set_reset_hander(usb_id, itf_num, itf_reset_hander)总线复位时重置接口状态
端点中断usb_g_set_intr_hander / usb_h_set_intr_hander端点事件通知
setup 过滤user_setup_filter_install(usb_device)拦截/修改标准请求处理
主机设备类驱动struct interface_ctrl + usb_interface_info.dev 联合体扩展 mass_storage/adb/hid/aoa 之外的新设备类
设备类实例usb_device_set_class() / usb_device_mode()组合启用多个设备类

新增主机设备类的步骤(源自 usb_host.h 的结构设计):在 usb_interface_info.dev 联合体增加驱动实例指针 → 实现 interface_ctrl 的 set_power/get_power/ioctl → 挂载流程中根据 interface_class 绑定并初始化驱动。

测试与验证要点

仓库未提供独立 USB 单元测试目录;sdk/app/src/mbox_flash/usb_slave/ 下的 usb_slave.c / usb_slave_key.c 是设备模式的应用级验证(mbox_flash 项目通过 USB 从机与主机通信,含按键触发逻辑),doc/stuff/usb updater.pdf 记录了基于 USB 的升级流程。验证建议:

  • 设备模式:用 PC 的 USB 分析仪/dmesg 观察枚举过程,重点检查 GET_DESCRIPTOR 返回与 SET_CONFIGURATION 后的端点收发;
  • 主机模式:热插拔 U 盘验证 check_usb_mount 离线分支与 usb_host_remount 重挂载路径;
  • 低功耗:验证 usb_host_suspend/usb_host_resume 与 SOF 中断(usb_sof_isr_reg)配合下的总线挂起恢复。

Related Links

  • usb_stack.h(设备协议栈)
  • usb_host.h(主机协议栈)
  • usb_phy.h(PHY 传输抽象层)
  • usb_config.h(BSP 配置层)
  • usb_audio_interface.h(USB Audio 接口)
  • usb_slave.c(mbox_flash 设备模式应用)
  • usb_slave_key.c(USB 从机按键处理)
  • usb updater.pdf(USB 升级说明文档)
Prev
CPU 平台与启动流程
Next
SPI 与通用外设接口