TinyUSB
+ -

TinyUsb的类设备驱动

2026-06-14 本文链接为:http://www.usbzh.com/article/detail-1715.html ,欢迎转载,转载请附上本文链接。

USB复杂在于极其宠大的类设备,如HID,BOT,UAC,UVC等。tinyusb通过对不同的USB类实现相同的类回调函数实现其功能。这些抽像的回调函数由结构体usbd_class_driver_t中的函数指针实现。

typedef struct {
  char const* name;
  void     (* init             ) (void);
  bool     (* deinit           ) (void);
  void     (* reset            ) (uint8_t rhport);
  uint16_t (* open             ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
  bool     (* control_xfer_cb  ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
  bool     (* xfer_cb          ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
  bool     (* xfer_isr         ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); // optional, return false to defer to xfer_cb()
  void     (* sof              ) (uint8_t rhport, uint32_t frame_count); // optional
} usbd_class_driver_t;

这是 TinyUSB 协议栈中 usbd_class_driver_t 结构体的定义,用于描述一个 USB 设备类驱动。各函数指针的功能如下:

函数指针 功能说明
name 驱动名称(字符串常量),用于调试和日志输出,标识该类驱动的类型(如 “CDC”, “HID”, “MSC”)。
init 初始化该类驱动。在 USB 设备栈启动时调用,用于分配资源、初始化内部状态变量、注册回调等。
deinit 反初始化该类驱动。用于释放 init 中分配的资源,清理状态。返回 true 表示成功,false 表示失败。
reset 复位指定端口(rhport)上的该类驱动。当 USB 总线复位时调用,用于重置端点状态、清除挂起的传输等。
open 打开(注册)一个接口。当主机发送 SET_INTERFACE 或枚举时匹配到该类驱动的接口描述符时调用。参数包括端口号、接口描述符指针、剩余数据长度。返回实际解析/消耗的字节数。
control_xfer_cb 控制传输回调。当接收到针对该类驱动的端点0控制请求(Setup 包)时调用。stage 表示当前阶段(Setup/Data/Ack),request 是解析后的请求结构体。返回 true 表示已处理。
xfer_cb 普通(非控制)端点传输完成回调。当数据通过 IN/OUT 端点传输完成后调用(中断或批量传输)。参数包括端口、端点地址、传输结果、实际传输字节数。
xfer_isr 传输完成的中级回调(可选)。在中断上下文中立即执行,比 xfer_cb 更早触发。若返回 false,则后续会再调用 xfer_cb;若返回 true,则不再调用 xfer_cb(用于需要极低延迟处理的场景)。
sof Start Of Frame 回调(可选)。每收到一个 SOF 帧(约每 1ms 全速 / 125μs 高速)时调用。可用于需要精确时间同步或周期性任务处理的驱动(如音频类、MIDI 类)。

对于不同的类设备,usbd_class_driver_t可以支持不同的回调。例如usbd.c中对当前 tinyusb支持的类:

// Built-in class drivers
static const usbd_class_driver_t _usbd_driver[] = {
  #if CFG_TUD_CDC
    {
        .name             = DRIVER_NAME("CDC"),
        .init             = cdcd_init,
        .deinit           = cdcd_deinit,
        .reset            = cdcd_reset,
        .open             = cdcd_open,
        .control_xfer_cb  = cdcd_control_xfer_cb,
        .xfer_cb          = cdcd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_MSC
    {
        .name             = DRIVER_NAME("MSC"),
        .init             = mscd_init,
        .deinit           = NULL,
        .reset            = mscd_reset,
        .open             = mscd_open,
        .control_xfer_cb  = mscd_control_xfer_cb,
        .xfer_cb          = mscd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_HID
    {
        .name             = DRIVER_NAME("HID"),
        .init             = hidd_init,
        .deinit           = hidd_deinit,
        .reset            = hidd_reset,
        .open             = hidd_open,
        .control_xfer_cb  = hidd_control_xfer_cb,
        .xfer_cb          = hidd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_AUDIO
    {
        .name             = DRIVER_NAME("AUDIO"),
        .init             = audiod_init,
        .deinit           = audiod_deinit,
        .reset            = audiod_reset,
        .open             = audiod_open,
        .control_xfer_cb  = audiod_control_xfer_cb,
        .xfer_cb          = audiod_xfer_cb,
        .xfer_isr         = audiod_xfer_isr,
        .sof              = audiod_sof_isr
    },
    #endif

    #if CFG_TUD_VIDEO
    {
        .name             = DRIVER_NAME("VIDEO"),
        .init             = videod_init,
        .deinit           = videod_deinit,
        .reset            = videod_reset,
        .open             = videod_open,
        .control_xfer_cb  = videod_control_xfer_cb,
        .xfer_cb          = videod_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_MIDI
    {
        .name             = DRIVER_NAME("MIDI"),
        .init             = midid_init,
        .deinit           = midid_deinit,
        .open             = midid_open,
        .reset            = midid_reset,
        .control_xfer_cb  = midid_control_xfer_cb,
        .xfer_cb          = midid_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_MIDI2
    {
        .name             = DRIVER_NAME("MIDI2"),
        .init             = midi2d_init,
        .deinit           = midi2d_deinit,
        .open             = midi2d_open,
        .reset            = midi2d_reset,
        .control_xfer_cb  = midi2d_control_xfer_cb,
        .xfer_cb          = midi2d_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_VENDOR
    {
        .name             = DRIVER_NAME("VENDOR"),
        .init             = vendord_init,
        .deinit           = vendord_deinit,
        .reset            = vendord_reset,
        .open             = vendord_open,
        .control_xfer_cb  = tud_vendor_control_xfer_cb,
        .xfer_cb          = vendord_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_USBTMC
    {
        .name             = DRIVER_NAME("TMC"),
        .init             = usbtmcd_init_cb,
        .deinit           = usbtmcd_deinit,
        .reset            = usbtmcd_reset_cb,
        .open             = usbtmcd_open_cb,
        .control_xfer_cb  = usbtmcd_control_xfer_cb,
        .xfer_cb          = usbtmcd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_DFU_RUNTIME
    {
        .name             = DRIVER_NAME("DFU-RUNTIME"),
        .init             = dfu_rtd_init,
        .deinit           = dfu_rtd_deinit,
        .reset            = dfu_rtd_reset,
        .open             = dfu_rtd_open,
        .control_xfer_cb  = dfu_rtd_control_xfer_cb,
        .xfer_cb          = NULL,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_DFU
    {
        .name             = DRIVER_NAME("DFU"),
        .init             = dfu_moded_init,
        .deinit           = dfu_moded_deinit,
        .reset            = dfu_moded_reset,
        .open             = dfu_moded_open,
        .control_xfer_cb  = dfu_moded_control_xfer_cb,
        .xfer_cb          = NULL,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
    {
        .name             = DRIVER_NAME("NET"),
        .init             = netd_init,
        .deinit           = netd_deinit,
        .reset            = netd_reset,
        .open             = netd_open,
        .control_xfer_cb  = netd_control_xfer_cb,
        .xfer_cb          = netd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL,
    },
    #endif

    #if CFG_TUD_BTH
    {
        .name             = DRIVER_NAME("BTH"),
        .init             = btd_init,
        .deinit           = btd_deinit,
        .reset            = btd_reset,
        .open             = btd_open,
        .control_xfer_cb  = btd_control_xfer_cb,
        .xfer_cb          = btd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_MTP
    {
        .name             = DRIVER_NAME("MTP"),
        .init             = mtpd_init,
        .deinit           = mtpd_deinit,
        .reset            = mtpd_reset,
        .open             = mtpd_open,
        .control_xfer_cb  = mtpd_control_xfer_cb,
        .xfer_cb          = mtpd_xfer_cb,
        .xfer_isr         = NULL,
        .sof              = NULL
    },
    #endif

    #if CFG_TUD_PRINTER
    {
        .name             = DRIVER_NAME("PRINTER"),
        .init             = printerd_init,
        .deinit           = printerd_deinit,
        .reset            = printerd_reset,
        .open             = printerd_open,
        .control_xfer_cb  = printerd_control_xfer_cb,
        .xfer_cb          = printerd_xfer_cb,
        .sof              = NULL
    },
    #endif
};

enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };

TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) {
  usbd_class_driver_t const *driver = NULL;
  if (drvid < _app_driver_count) {
    // Application drivers
    driver = &_app_driver[drvid];
  } else{
    drvid -= _app_driver_count;
    if (drvid < BUILTIN_DRIVER_COUNT) {
      driver = &_usbd_driver[drvid];
    }
  }

  return driver;
}

调试宏

#define CFG_TUSB_DEBUG 3 // 启用枚举过程调试,(0-3,3为最详细)

串品打印日志

#define CFG_TUSB_DEBUG_PRINTF my_debug_printf // 自定义打印函数
本文链接为:http://www.usbzh.com/article/detail-1715.html ,欢迎转载,转载请附上本文链接。

0 篇笔记 写笔记

关注公众号
  • HID人机交互
  • Linux&USB
  • TinyUSB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • USB资源
  • XHCI 1.2b 规范
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    您的支持,是我们前进的动力!