TinyUSB
+ -

TinyUSB事件

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

事件结构体

typedef struct TU_ATTR_ALIGNED(4) {
  uint8_t rhport;//root hub port
  uint8_t event_id;

  union {
    // BUS RESET
    struct {
      tusb_speed_t speed;
    } bus_reset;

    // SOF
    struct {
      uint32_t frame_count;
    }sof;

    // SETUP_RECEIVED
    tusb_control_request_t setup_received;

    // XFER_COMPLETE
    struct {
      uint8_t  ep_addr;
      uint8_t  result;
      uint32_t len;
    }xfer_complete;

    // FUNC_CALL
    struct {
      void (*func) (void*);
      void* param;
    }func_call;
  };
} dcd_event_t;

根据共用体可以知,事件类型分为以下几种:

typedef enum {
  DCD_EVENT_INVALID = 0,    // 0
  DCD_EVENT_BUS_RESET,      // 1
  DCD_EVENT_UNPLUGGED,      // 2
  DCD_EVENT_SOF,            // 3
  DCD_EVENT_SUSPEND,        // 4 TODO LPM Sleep L1 support
  DCD_EVENT_RESUME,         // 5
  DCD_EVENT_SETUP_RECEIVED, // 6
  DCD_EVENT_XFER_COMPLETE,  // 7
  USBD_EVENT_FUNC_CALL,     // 8 Not an DCD event, just a convenient way to defer ISR function
  DCD_EVENT_COUNT
} dcd_eventid_t;

事件的发送

所以事件的发送有以下:

// helper to send bus signal event
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) {
  dcd_event_t event;
  event.rhport = rhport;
  event.event_id = eid;
  dcd_event_handler(&event, in_isr);
}

// helper to send bus reset event
TU_ATTR_ALWAYS_INLINE static inline  void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) {
  dcd_event_t event;
  event.rhport = rhport;
  event.event_id = DCD_EVENT_BUS_RESET;
  event.bus_reset.speed = speed;
  dcd_event_handler(&event, in_isr);
}

// helper to send setup received
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) {
  dcd_event_t event;
  event.rhport = rhport;
  event.event_id = DCD_EVENT_SETUP_RECEIVED;
  memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
  dcd_event_handler(&event, in_isr);
}

// helper to send transfer complete event
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) {
  dcd_event_t event;
  event.rhport = rhport;
  event.event_id = DCD_EVENT_XFER_COMPLETE;
  event.xfer_complete.ep_addr = ep_addr;
  event.xfer_complete.len     = xferred_bytes;
  event.xfer_complete.result  = result;
  dcd_event_handler(&event, in_isr);
}

TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) {
  dcd_event_t event;
  event.rhport = rhport;
  event.event_id = DCD_EVENT_SOF;
  event.sof.frame_count = frame_count;
  dcd_event_handler(&event, in_isr);
}

dcd_event_handlerb函数执行的是将事件精细化处理,并回调一些类驱动相关的回调,并

TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) {
  bool send = false;
  switch (event->event_id) {
    case DCD_EVENT_UNPLUGGED:
      _usbd_dev.connected = 0;
      _usbd_dev.addressed = 0;
      _usbd_dev.cfg_num = 0;
      _usbd_dev.suspended = 0;
      send = true;
      break;

    case DCD_EVENT_SUSPEND:
      // NOTE: When plugging/unplugging device, the D+/D- state are unstable and
      // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ).
      // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish
      // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected
      if (_usbd_dev.connected) {
        _usbd_dev.suspended = 1;
        send = true;
      }
      break;

    case DCD_EVENT_RESUME:
      // skip event if not connected (especially required for SAMD)
      if (_usbd_dev.connected) {
        _usbd_dev.suspended = 0;
        send = true;
      }
      break;

    case DCD_EVENT_SOF:
      // SOF driver handler in ISR context
      for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
        usbd_class_driver_t const* driver = get_driver(i);
        if (driver && driver->sof) {
          driver->sof(event->rhport, event->sof.frame_count);
        }
      }

      // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
      // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
      if (_usbd_dev.suspended) {
        _usbd_dev.suspended = 0;

        dcd_event_t const event_resume = {.rhport = event->rhport, .event_id = DCD_EVENT_RESUME};
        queue_event(&event_resume, in_isr);
      }

      if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
        dcd_event_t const event_sof = {.rhport = event->rhport, .event_id = DCD_EVENT_SOF, .sof.frame_count = event->sof.frame_count};
        queue_event(&event_sof, in_isr);
      }
      break;

    case DCD_EVENT_SETUP_RECEIVED:
      _usbd_queued_setup++;
      TU_LOG_USBD(" event setup %d\r\n", _usbd_queued_setup);
      send = true;
      break;

    default:
      send = true;
      break;
  }

  if (send) {
    queue_event(event, in_isr);
  }
}

queue_event中tud_event_hook_cb是一个空函数,如果需要,可以做一些其它的操作。
使用osal_queue_send事件放入FIFO中

TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
  TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr));
  tud_event_hook_cb(event->rhport, event->event_id, in_isr);
  return true;
}

osal_queue_send很简单,就是写FIFO.一个有意思的参数就是in_isr,在中断中就不用加锁了,不在中断中是需要加锁同步的。

TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
  if (!in_isr) {
    _osal_q_lock(qhdl);
  }

  bool success = tu_fifo_write(&qhdl->ff, data);

  if (!in_isr) {
    _osal_q_unlock(qhdl);
  }

  return success;
}

事件的处理

事件的处理是在一个tud_task任务中,这是单片机main函数中的死循环

    while (1) {
        tud_task(); // tinyusb device task
    }

调用tud_task_ext

void tud_task (void) {
  tud_task_ext(UINT32_MAX, false);
}

就是从FIFO中取出事件,然后依次处理。

void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
  (void) in_isr; // not implemented yet

  // Skip if stack is not initialized
  if (!tud_inited()) return;

  // Loop until there is no more events in the queue
  while (1) {
    dcd_event_t event;
    if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return;

#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
    if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup
    TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
#endif

    switch (event.event_id) {
      case DCD_EVENT_BUS_RESET:
        TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]);
        usbd_reset(event.rhport);
        _usbd_dev.speed = event.bus_reset.speed;
        break;

      case DCD_EVENT_UNPLUGGED:
        TU_LOG_USBD("\r\n");
        usbd_reset(event.rhport);
        tud_umount_cb();
        break;

      case DCD_EVENT_SETUP_RECEIVED:
        TU_LOG_USBD("SETUP received %d\r\n", _usbd_queued_setup);
        TU_ASSERT(_usbd_queued_setup > 0,);
        _usbd_queued_setup--;
        TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8);
        if (_usbd_queued_setup) {
          TU_LOG_USBD("  Skipped since there is other SETUP in queue\r\n");
          break;
        }

        // Mark as connected after receiving 1st setup packet.
        // But it is easier to set it every time instead of wasting time to check then set
        _usbd_dev.connected = 1;

        // mark both in & out control as free
        _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0;
        _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0;
        _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0;
        _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0;

        // Process control request
        if (!process_control_request(event.rhport, &event.setup_received)) {
          TU_LOG_USBD("  Stall EP0\r\n");
          // Failed -> stall both control endpoint IN and OUT
          dcd_edpt_stall(event.rhport, 0);
          dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK);
        }
        break;

      case DCD_EVENT_XFER_COMPLETE: {
        // Invoke the class callback associated with the endpoint address
        uint8_t const ep_addr = event.xfer_complete.ep_addr;
        uint8_t const epnum = tu_edpt_number(ep_addr);
        uint8_t const ep_dir = tu_edpt_dir(ep_addr);

        TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);

        _usbd_dev.ep_status[epnum][ep_dir].busy = 0;
        _usbd_dev.ep_status[epnum][ep_dir].claimed = 0;

        if (0 == epnum) {
          usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result,event.xfer_complete.len);
        } 
                else 
                    {
          usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]);
          TU_ASSERT(driver,);

          TU_LOG_USBD("  %s xfer callback\r\n", driver->name);
          driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);//hidd_xfer_cb
        }
        break;
      }

      case DCD_EVENT_SUSPEND:
        // NOTE: When plugging/unplugging device, the D+/D- state are unstable and
        // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event
        // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
        if (_usbd_dev.connected) {
          TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
          tud_suspend_cb(_usbd_dev.remote_wakeup_en);
        } else {
          TU_LOG_USBD(" Skipped\r\n");
        }
        break;

      case DCD_EVENT_RESUME:
        if (_usbd_dev.connected) {
          TU_LOG_USBD("\r\n");
          tud_resume_cb();
        } else {
          TU_LOG_USBD(" Skipped\r\n");
        }
        break;

      case USBD_EVENT_FUNC_CALL:
        TU_LOG_USBD("\r\n");
        if (event.func_call.func) event.func_call.func(event.func_call.param);
        break;

      case DCD_EVENT_SOF:
        if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
          TU_LOG_USBD("\r\n");
          tud_sof_cb(event.sof.frame_count);
        }
      break;

      default:
        TU_BREAKPOINT();
        break;
    }

#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
    // return if there is no more events, for application to run other background
    if (osal_queue_empty(_usbd_q)) return;
#endif
  }
}
本文链接为:http://www.usbzh.com/article/detail-1784.html ,欢迎转载,转载请附上本文链接。

0 篇笔记 写笔记

DirectShow 事件产生
要处理DirectShow事件,应用程序需要一种方法来确定事件何时在队列中等待。过滤器图形管理器提供了两种方法:窗口通知:每当出现新事件时,过滤器图形管理器就会向应用程序窗口发送用户定义的窗口消息。事件信号:如果队列中有DirectShow事件,Filter Graph Manager将向Windo......
DirectShow 接收事件
过滤器图形管理器公开了三个支持事件通知的接口。IMediaEventSink包含筛选器发布事件的方法。IMediaEvent包含应用程序检索事件的方法。IMediaEventEx继承并扩展IMediaEvent接口。过滤器通过调用Filter Graph Manager上的IMediaEventSi......
DirectShow 事件通知简介
过滤器通过事件通知将事件方式通知过滤器图形管理器。事件可以是预期的事件,例如流的结尾,也可以表示错误,例如未能渲染流。过滤器图形管理器自己处理一些过滤器事件,它将其他事件留给应用程序处理。如果过滤器图形管理器不处理过滤器事件,它会将事件通知放入队列中。过滤器图还可以为应用程序排队它自己的事件通知。应......
HIDCLASS电源事件IOCTL_GET_SYS_BUTTON_EVENT
系统应该是向HID设备发送一个IOCTL_GET_SYS_BUTTON_EVENT请求,该请求被HID设备Pending挂起,当有相关的Power Button报告时,完成该事件。#define FILE_DEVICE_BATTERY 0x00000029#define ......
V4L2 UVC事件
UVC事件包括两类,分别为数据事件(读写)和异常事件。这两类事件可通过select函数查看。事件的注册可见: struct v4l2_event_subscription sub; memset(&sub, 0, sizeof sub); sub.type = UVC......
TinyUSB事件
事件结构体typedef struct TU_ATTR_ALIGNED(4) { uint8_t rhport;//root hub port uint8_t event_id; union { // BUS RESET struct { tusb_spe......
关注公众号
  • HID人机交互
  • Linux&USB
  • Linux-USB百科
  • MIDI V1.0
  • TinyUSB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB3测试
  • USB3知识点
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • USB资源
  • XHCI 1.2b 规范
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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