From 4ef0f61bde446453a1ada60bbc1a319665d2e839 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Nov 2025 16:23:00 +0700 Subject: [PATCH 1/5] add tu_fifo_discard_n() and tu_edpt_stream_discard() --- src/common/tusb_fifo.c | 253 +++++++++++++++----------------------- src/common/tusb_fifo.h | 38 +++--- src/common/tusb_private.h | 4 + src/tusb.c | 3 +- 4 files changed, 127 insertions(+), 171 deletions(-) diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 27b97310a7..f78167f94a 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -56,6 +56,9 @@ TU_ATTR_ALWAYS_INLINE static inline void ff_unlock(osal_mutex_t mutex) { #endif +//--------------------------------------------------------------------+ +// Setup API +//--------------------------------------------------------------------+ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_size, bool overwritable) { // Limit index space to 2*depth - this allows for a fast "modulo" calculation // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable @@ -80,6 +83,36 @@ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_si return true; } +// clear fifo by resetting read and write indices +bool tu_fifo_clear(tu_fifo_t *f) { + ff_lock(f->mutex_wr); + ff_lock(f->mutex_rd); + + f->rd_idx = 0; + f->wr_idx = 0; + + ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_rd); + return true; +} + +// Change the fifo overwritable mode +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { + if (f->overwritable == overwritable) { + return true; + } + + ff_lock(f->mutex_wr); + ff_lock(f->mutex_rd); + + f->overwritable = overwritable; + + ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_rd); + + return true; +} + //--------------------------------------------------------------------+ // Pull & Push // copy data to/from fifo without updating read/write pointers @@ -295,49 +328,27 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t correct_read_index(tu_fifo_t *f, ui return rd_idx; } -// peek() using local write/read index. Be careful, caller must not lock mutex, since this Will also try to lock mutex -// in case of overflowed to correct read index -static bool ff_peek_local(tu_fifo_t *f, void *buf, uint16_t wr_idx, uint16_t rd_idx) { - const uint16_t ovf_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); - if (ovf_count == 0) { - return false; // nothing to peek - } - - // Correct read index if overflow - if (ovf_count > f->depth) { - ff_lock(f->mutex_rd); - rd_idx = correct_read_index(f, wr_idx); - ff_unlock(f->mutex_rd); - } - - const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); - memcpy(buf, f->buffer + (rd_ptr * f->item_size), f->item_size); - - return true; -} - //--------------------------------------------------------------------+ -// Application API +// n-API //--------------------------------------------------------------------+ // Works on local copies of w and r -// Must be protected by mutexes since in case of an overflow read pointer gets modified +// Must be protected by read mutex since in case of an overflow read pointer gets modified uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_access_mode_t access_mode) { - uint16_t ovf_cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); - - if (ovf_cnt == 0) { + uint16_t count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); + if (count == 0) { return 0; // nothing to peek } // Check overflow and correct if required - if (ovf_cnt > f->depth) { + if (count > f->depth) { rd_idx = correct_read_index(f, wr_idx); - ovf_cnt = f->depth; + count = f->depth; } - if (ovf_cnt < n) { - n = ovf_cnt; // limit to available count + if (count < n) { + n = count; // limit to available count } const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); @@ -346,6 +357,27 @@ uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, ui return n; } +// Read n items without removing it from the FIFO, correct read pointer if overflowed +uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) { + ff_lock(f->mutex_rd); + const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_INC_ADDR_RW8); + ff_unlock(f->mutex_rd); + return ret; +} + +// Read n items from fifo with access mode +uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_access_mode_t access_mode) { + ff_lock(f->mutex_rd); + + // Peek the data: f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = tu_fifo_peek_n_access_mode(f, buffer, n, f->wr_idx, f->rd_idx, access_mode); + f->rd_idx = advance_index(f->depth, f->rd_idx, n); + + ff_unlock(f->mutex_rd); + return n; +} + +// Write n items to fifo with access mode uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_access_mode_t access_mode) { if (n == 0) { return 0; @@ -419,40 +451,41 @@ uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, return n; } -uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_access_mode_t access_mode) { +uint16_t tu_fifo_discard_n(tu_fifo_t *f, uint16_t n) { + const uint16_t count = tu_min16(n, tu_fifo_count(f)); // limit to available count ff_lock(f->mutex_rd); - - // Peek the data: f->rd_idx might get modified in case of an overflow so we can not use a local variable - n = tu_fifo_peek_n_access_mode(f, buffer, n, f->wr_idx, f->rd_idx, access_mode); - f->rd_idx = advance_index(f->depth, f->rd_idx, n); - + f->rd_idx = advance_index(f->depth, f->rd_idx, count); ff_unlock(f->mutex_rd); - return n; -} -// Only use in case tu_fifo_overflow() returned true! -void tu_fifo_correct_read_pointer(tu_fifo_t *f) { - ff_lock(f->mutex_rd); - correct_read_index(f, f->wr_idx); - ff_unlock(f->mutex_rd); + return count; } -/******************************************************************************/ -/*! - @brief Read one element out of the buffer. +//--------------------------------------------------------------------+ +// One API +//--------------------------------------------------------------------+ - This function will return the element located at the array index of the - read pointer, and then increment the read pointer index. - This function checks for an overflow and corrects read pointer if required. +// peek() using local write/read index, correct read index if overflowed +// Be careful, caller must not lock mutex, since this Will also try to lock mutex +static bool ff_peek_local(tu_fifo_t *f, void *buf, uint16_t wr_idx, uint16_t rd_idx) { + const uint16_t ovf_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); + if (ovf_count == 0) { + return false; // nothing to peek + } - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] buffer - Pointer to the place holder for data read from the buffer + // Correct read index if overflow + if (ovf_count > f->depth) { + ff_lock(f->mutex_rd); + rd_idx = correct_read_index(f, wr_idx); + ff_unlock(f->mutex_rd); + } - @returns TRUE if the queue is not empty - */ -/******************************************************************************/ + const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + memcpy(buf, f->buffer + (rd_ptr * f->item_size), f->item_size); + + return true; +} + +// Read one element out of the buffer, correct read index if overflowed bool tu_fifo_read(tu_fifo_t *f, void *buffer) { // Peek the data // f->rd_idx might get modified in case of an overflow so we can not use a local variable @@ -466,61 +499,12 @@ bool tu_fifo_read(tu_fifo_t *f, void *buffer) { return ret; } -/******************************************************************************/ -/*! - @brief Read one item without removing it from the FIFO. - This function checks for an overflow and corrects read pointer if required. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] p_buffer - Pointer to the place holder for data read from the buffer - - @returns TRUE if the queue is not empty - */ -/******************************************************************************/ +// Read one item without removing it from the FIFO, correct rad index if overflowed bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) { return ff_peek_local(f, p_buffer, f->wr_idx, f->rd_idx); } -/******************************************************************************/ -/*! - @brief Read n items without removing it from the FIFO - This function checks for an overflow and corrects read pointer if required. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] p_buffer - Pointer to the place holder for data read from the buffer - @param[in] n - Number of items to peek - - @returns Number of bytes written to p_buffer - */ -/******************************************************************************/ -uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) { - ff_lock(f->mutex_rd); - const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_INC_ADDR_RW8); - ff_unlock(f->mutex_rd); - return ret; -} - -/******************************************************************************/ -/*! - @brief Write one element into the buffer. - - This function will write one element into the array index specified by - the write pointer and increment the write index. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] data - The byte to add to the FIFO - - @returns TRUE if the data was written to the FIFO (overwrittable - FIFO will always return TRUE) - */ -/******************************************************************************/ +// Write one element into the buffer bool tu_fifo_write(tu_fifo_t *f, const void *data) { bool ret; ff_lock(f->mutex_wr); @@ -541,51 +525,9 @@ bool tu_fifo_write(tu_fifo_t *f, const void *data) { return ret; } -/******************************************************************************/ -/*! - @brief Clear the fifo read and write pointers - - @param[in] f - Pointer to the FIFO buffer to manipulate - */ -/******************************************************************************/ -bool tu_fifo_clear(tu_fifo_t *f) { - ff_lock(f->mutex_wr); - ff_lock(f->mutex_rd); - - f->rd_idx = 0; - f->wr_idx = 0; - - ff_unlock(f->mutex_wr); - ff_unlock(f->mutex_rd); - return true; -} - -/******************************************************************************/ -/*! - @brief Change the fifo mode to overwritable or not overwritable - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] overwritable - Overwritable mode the fifo is set to - */ -/******************************************************************************/ -bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { - if (f->overwritable == overwritable) { - return true; - } - - ff_lock(f->mutex_wr); - ff_lock(f->mutex_rd); - - f->overwritable = overwritable; - - ff_unlock(f->mutex_wr); - ff_unlock(f->mutex_rd); - - return true; -} +//--------------------------------------------------------------------+ +// Index API +//--------------------------------------------------------------------+ /******************************************************************************/ /*! @@ -607,6 +549,13 @@ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) { f->wr_idx = advance_index(f->depth, f->wr_idx, n); } +// Correct the read index in case tu_fifo_overflow() returned true! +void tu_fifo_correct_read_pointer(tu_fifo_t *f) { + ff_lock(f->mutex_rd); + correct_read_index(f, f->wr_idx); + ff_unlock(f->mutex_rd); +} + /******************************************************************************/ /*! @brief Advance read pointer - intended to be used in combination with DMA. diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index 4d8448c44c..42f154bca2 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -156,9 +156,9 @@ typedef enum { //--------------------------------------------------------------------+ // Setup API //--------------------------------------------------------------------+ +bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_size, bool overwritable); bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable); bool tu_fifo_clear(tu_fifo_t *f); -bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); #if OSAL_MUTEX_REQUIRED TU_ATTR_ALWAYS_INLINE static inline @@ -170,6 +170,22 @@ void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_m #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) #endif +//--------------------------------------------------------------------+ +// Index API +//--------------------------------------------------------------------+ +void tu_fifo_correct_read_pointer(tu_fifo_t *f); + +// Pointer modifications intended to be used in combinations with DMAs. +// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); +void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n); + +// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies +// to handle a possible wrapping part. These functions deliver a pointer to start +// reading/writing from/to and a valid linear length along which no wrap occurs. +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); + //--------------------------------------------------------------------+ // Peek API // peek() will correct/re-index read pointer in case of an overflowed fifo to form a full fifo @@ -189,6 +205,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_read_n(tu_fifo_t *f, void * return tu_fifo_read_n_access_mode(f, buffer, n, TU_FIFO_INC_ADDR_RW8); } +// discard first n items from fifo i.e advance read pointer by n with mutex +// return number of discarded items +uint16_t tu_fifo_discard_n(tu_fifo_t *f, uint16_t n); + //--------------------------------------------------------------------+ // Write API //--------------------------------------------------------------------+ @@ -198,22 +218,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_write_n(tu_fifo_t *f, const return tu_fifo_write_n_access_mode(f, data, n, TU_FIFO_INC_ADDR_RW8); } -//--------------------------------------------------------------------+ -// Index API -//--------------------------------------------------------------------+ -void tu_fifo_correct_read_pointer(tu_fifo_t *f); - -// Pointer modifications intended to be used in combinations with DMAs. -// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! -void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); -void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n); - -// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies -// to handle a possible wrapping part. These functions deliver a pointer to start -// reading/writing from/to and a valid linear length along which no wrap occurs. -void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); -void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); - //--------------------------------------------------------------------+ // Internal Helper Local // work on local copies of read/write indices in order to only access them once for re-entrancy diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 48fd1d6d2b..8643bb020a 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -172,6 +172,10 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_peek(tu_edpt_stream_t *s return tu_fifo_peek(&s->ff, ch); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_edpt_stream_discard(tu_edpt_stream_t *s, uint32_t len) { + return (uint32_t)tu_fifo_discard_n(&s->ff, (uint16_t)len); +} + #ifdef __cplusplus } #endif diff --git a/src/tusb.c b/src/tusb.c index c589e105e7..2d122885b0 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -533,8 +533,7 @@ uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, ui num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); } else { // non-fifo mode - memcpy(buffer, s->ep_buf, bufsize); - num_read = bufsize; + num_read = 0; } tu_edpt_stream_read_xfer(hwid, s); From f3dc2186aea9a472fe13ecab06464fbead50f0dd Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Nov 2025 16:25:19 +0700 Subject: [PATCH 2/5] add tud_vendor_n_read_discard(), refactor vendor device. Update webusb example for more robust --- examples/device/cdc_msc/src/main.c | 1 + examples/device/webusb_serial/src/main.c | 54 +++--- src/class/vendor/vendor_device.c | 204 ++++++++++++----------- src/class/vendor/vendor_device.h | 75 +++++---- 4 files changed, 167 insertions(+), 167 deletions(-) diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c index e4a205533e..06a4f732fa 100644 --- a/examples/device/cdc_msc/src/main.c +++ b/examples/device/cdc_msc/src/main.c @@ -124,6 +124,7 @@ void cdc_task(void) { if ((btn_prev == 0u) && (btn != 0u)) { uart_state.dsr ^= 1; + uart_state.dcd ^= 1; tud_cdc_notify_uart_state(&uart_state); } btn_prev = btn; diff --git a/examples/device/webusb_serial/src/main.c b/examples/device/webusb_serial/src/main.c index 0c2acd94e5..4fcddd7247 100644 --- a/examples/device/webusb_serial/src/main.c +++ b/examples/device/webusb_serial/src/main.c @@ -101,7 +101,7 @@ int main(void) { while (1) { tud_task(); // tinyusb device task - cdc_task(); + tud_cdc_write_flush(); led_blinking_task(); } } @@ -116,13 +116,7 @@ static void echo_all(const uint8_t buf[], uint32_t count) { // echo to cdc if (tud_cdc_connected()) { - for (uint32_t i = 0; i < count; i++) { - tud_cdc_write_char(buf[i]); - if (buf[i] == '\r') { - tud_cdc_write_char('\n'); - } - } - tud_cdc_write_flush(); + tud_cdc_write(buf, count); } } @@ -162,7 +156,9 @@ void tud_resume_cb(void) { // return false to stall control endpoint (e.g unsupported request) bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { // nothing to with DATA & ACK stage - if (stage != CONTROL_STAGE_SETUP) return true; + if (stage != CONTROL_STAGE_SETUP) { + return true; + } switch (request->bmRequestType_bit.type) { case TUSB_REQ_TYPE_VENDOR: @@ -215,33 +211,20 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ return false; } -void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) { - (void) itf; +void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize) { + (void)idx; - echo_all(buffer, bufsize); +// since we used data without pulling it from RX FIFO, we need to discard number of items used +#if CFG_TUD_VENDOR_RX_BUFSIZE > 0 + tud_vendor_read_discard(bufsize); +#endif - // if using RX buffered is enabled, we need to flush the buffer to make room for new data - #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 - tud_vendor_read_flush(); - #endif + echo_all(buffer, bufsize); } //--------------------------------------------------------------------+ // USB CDC //--------------------------------------------------------------------+ -void cdc_task(void) { - if (tud_cdc_connected()) { - // connected and there are data available - if (tud_cdc_available()) { - uint8_t buf[64]; - - uint32_t count = tud_cdc_read(buf, sizeof(buf)); - - // echo back to both web serial and cdc - echo_all(buf, count); - } - } -} // Invoked when cdc when line state changed e.g connected/disconnected void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { @@ -255,8 +238,13 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { } // Invoked when CDC interface received data from host -void tud_cdc_rx_cb(uint8_t itf) { - (void)itf; +void tud_cdc_rx_cb(uint8_t idx) { + (void)idx; + while (tud_cdc_available()) { + uint8_t buf[64]; + const uint32_t count = tud_cdc_read(buf, sizeof(buf)); + echo_all(buf, count); // echo back to both web serial and cdc + } } //--------------------------------------------------------------------+ @@ -267,7 +255,9 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (board_millis() - start_ms < blink_interval_ms) { + return; // not enough time + } start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index c7903375d1..c2393c25d9 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -42,22 +42,20 @@ typedef struct { /*------------- From this point, data is not cleared by bus reset -------------*/ struct { - tu_edpt_stream_t stream; - #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 - uint8_t ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; - #endif - } tx; + tu_edpt_stream_t tx; + tu_edpt_stream_t rx; - struct { - tu_edpt_stream_t stream; - #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 - uint8_t ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE]; - #endif - } rx; + #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 + uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; + #endif + #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 + uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE]; + #endif + } stream; } vendord_interface_t; -#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num)) +#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num)) static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR]; @@ -71,14 +69,14 @@ CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ -TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) { - (void) itf; +TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize) { + (void)idx; (void) buffer; (void) bufsize; } -TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { - (void) itf; +TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) { + (void)idx; (void) sent_bytes; } @@ -86,59 +84,65 @@ TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { // Application API //-------------------------------------------------------------------- -bool tud_vendor_n_mounted(uint8_t itf) { - TU_VERIFY(itf < CFG_TUD_VENDOR); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return p_itf->rx.stream.ep_addr || p_itf->tx.stream.ep_addr; +bool tud_vendor_n_mounted(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return p_itf->stream.rx.ep_addr || p_itf->stream.tx.ep_addr; } //--------------------------------------------------------------------+ // Read API //--------------------------------------------------------------------+ -uint32_t tud_vendor_n_available(uint8_t itf) { - TU_VERIFY(itf < CFG_TUD_VENDOR, 0); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_read_available(&p_itf->rx.stream); +uint32_t tud_vendor_n_available(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_read_available(&p_itf->stream.rx); } -bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) { - TU_VERIFY(itf < CFG_TUD_VENDOR); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_peek(&p_itf->rx.stream, u8); +bool tud_vendor_n_peek(uint8_t idx, uint8_t *u8) { + TU_VERIFY(idx < CFG_TUD_VENDOR); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_peek(&p_itf->stream.rx, u8); } -uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) { - TU_VERIFY(itf < CFG_TUD_VENDOR, 0); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_read(p_itf->rhport, &p_itf->rx.stream, buffer, bufsize); +uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_read(p_itf->rhport, &p_itf->stream.rx, buffer, bufsize); } -void tud_vendor_n_read_flush (uint8_t itf) { - TU_VERIFY(itf < CFG_TUD_VENDOR, ); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - tu_edpt_stream_clear(&p_itf->rx.stream); - tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->rx.stream); +uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_discard(&p_itf->stream.rx, count); +} + +void tud_vendor_n_read_flush(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR, ); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + tu_edpt_stream_clear(&p_itf->stream.rx); + tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->stream.rx); } //--------------------------------------------------------------------+ // Write API //--------------------------------------------------------------------+ -uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) { - TU_VERIFY(itf < CFG_TUD_VENDOR, 0); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_write(p_itf->rhport, &p_itf->tx.stream, buffer, (uint16_t)bufsize); +uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_write(p_itf->rhport, &p_itf->stream.tx, buffer, (uint16_t)bufsize); } -uint32_t tud_vendor_n_write_flush (uint8_t itf) { - TU_VERIFY(itf < CFG_TUD_VENDOR, 0); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_write_xfer(p_itf->rhport, &p_itf->tx.stream); +uint32_t tud_vendor_n_write_flush(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_write_xfer(p_itf->rhport, &p_itf->stream.tx); } -uint32_t tud_vendor_n_write_available (uint8_t itf) { - TU_VERIFY(itf < CFG_TUD_VENDOR, 0); - vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_write_available(p_itf->rhport, &p_itf->tx.stream); +uint32_t tud_vendor_n_write_available(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR, 0); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_write_available(p_itf->rhport, &p_itf->stream.tx); } //--------------------------------------------------------------------+ @@ -152,32 +156,30 @@ void vendord_init(void) { vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i]; #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 - uint8_t *rx_ff_buf = p_itf->rx.ff_buf; + uint8_t *rx_ff_buf = p_itf->stream.rx_ff_buf; #else uint8_t *rx_ff_buf = NULL; #endif - tu_edpt_stream_init(&p_itf->rx.stream, false, false, false, - rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, - p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE); + tu_edpt_stream_init(&p_itf->stream.rx, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, p_epbuf->epout, + CFG_TUD_VENDOR_EPSIZE); #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 - uint8_t *tx_ff_buf = p_itf->tx.ff_buf; + uint8_t *tx_ff_buf = p_itf->stream.tx_ff_buf; #else - uint8_t* tx_ff_buf = NULL; + uint8_t *tx_ff_buf = NULL; #endif - tu_edpt_stream_init(&p_itf->tx.stream, false, true, false, - tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, - p_epbuf->epin, CFG_TUD_VENDOR_EPSIZE); + tu_edpt_stream_init(&p_itf->stream.tx, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, p_epbuf->epin, + CFG_TUD_VENDOR_EPSIZE); } } bool vendord_deinit(void) { for(uint8_t i=0; irx.stream); - tu_edpt_stream_deinit(&p_itf->tx.stream); + tu_edpt_stream_deinit(&p_itf->stream.rx); + tu_edpt_stream_deinit(&p_itf->stream.tx); } return true; } @@ -188,30 +190,42 @@ void vendord_reset(uint8_t rhport) { for(uint8_t i=0; irx.stream); - tu_edpt_stream_close(&p_itf->rx.stream); - tu_edpt_stream_clear(&p_itf->tx.stream); - tu_edpt_stream_close(&p_itf->tx.stream); + tu_edpt_stream_clear(&p_itf->stream.rx); + tu_edpt_stream_close(&p_itf->stream.rx); + + tu_edpt_stream_clear(&p_itf->stream.tx); + tu_edpt_stream_close(&p_itf->stream.tx); + } +} + +// Find vendor interface by endpoint address +static uint8_t find_vendor_itf(uint8_t ep_addr) { + for (uint8_t idx = 0; idx < CFG_TUD_VENDOR; idx++) { + const vendord_interface_t *p_vendor = &_vendord_itf[idx]; + if (ep_addr == 0) { + // find unused: require both ep == 0 + if (p_vendor->stream.rx.ep_addr == 0 && p_vendor->stream.tx.ep_addr == 0) { + return idx; + } + } else if (ep_addr == p_vendor->stream.rx.ep_addr || ep_addr == p_vendor->stream.tx.ep_addr) { + return idx; + } else { + // nothing to do + } } + return 0xff; } -uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) { +uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) { TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0); const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len; const uint8_t* p_desc = tu_desc_next(desc_itf); // Find available interface - vendord_interface_t* p_vendor = NULL; - uint8_t itf; - for(itf=0; itfrhport = rhport; p_vendor->itf_num = desc_itf->bInterfaceNumber; @@ -225,13 +239,13 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin // open endpoint stream, skip if already opened (multiple IN/OUT endpoints) if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { - tu_edpt_stream_t *stream_tx = &p_vendor->tx.stream; + tu_edpt_stream_t *stream_tx = &p_vendor->stream.tx; if (stream_tx->ep_addr == 0) { tu_edpt_stream_open(stream_tx, desc_ep); tu_edpt_stream_write_xfer(rhport, stream_tx); // flush pending data } } else { - tu_edpt_stream_t *stream_rx = &p_vendor->rx.stream; + tu_edpt_stream_t *stream_rx = &p_vendor->stream.rx; if (stream_rx->ep_addr == 0) { tu_edpt_stream_open(stream_rx, desc_ep); TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data @@ -247,38 +261,30 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void) result; + const uint8_t idx = find_vendor_itf(ep_addr); + TU_VERIFY(idx < CFG_TUD_VENDOR); + vendord_interface_t *p_vendor = &_vendord_itf[idx]; + vendord_epbuf_t *p_epbuf = &_vendord_epbuf[idx]; - uint8_t itf; - vendord_interface_t* p_vendor; - - for (itf = 0; itf < CFG_TUD_VENDOR; itf++) { - p_vendor = &_vendord_itf[itf]; - if ((ep_addr == p_vendor->rx.stream.ep_addr) || (ep_addr == p_vendor->tx.stream.ep_addr)) { - break; - } - } - TU_VERIFY(itf < CFG_TUD_VENDOR); - vendord_epbuf_t* p_epbuf = &_vendord_epbuf[itf]; - - if ( ep_addr == p_vendor->rx.stream.ep_addr ) { + if (ep_addr == p_vendor->stream.rx.ep_addr) { // Received new data: put into stream's fifo - tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes); + tu_edpt_stream_read_xfer_complete(&p_vendor->stream.rx, xferred_bytes); // Invoked callback if any - tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes); + tud_vendor_rx_cb(idx, p_epbuf->epout, (uint16_t)xferred_bytes); - tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream); - } else if ( ep_addr == p_vendor->tx.stream.ep_addr ) { + tu_edpt_stream_read_xfer(rhport, &p_vendor->stream.rx); + } else if (ep_addr == p_vendor->stream.tx.ep_addr) { // Send complete - tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes); + tud_vendor_tx_cb(idx, (uint16_t)xferred_bytes); - #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 + #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 // try to send more if possible - if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->tx.stream) ) { + if (0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->stream.tx)) { // If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero - tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->tx.stream, xferred_bytes); + tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->stream.tx, xferred_bytes); } - #endif + #endif } return true; diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 5376f39176..e9dec788bb 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -30,84 +30,87 @@ #include "common/tusb_common.h" #ifndef CFG_TUD_VENDOR_EPSIZE -#define CFG_TUD_VENDOR_EPSIZE 64 + #define CFG_TUD_VENDOR_EPSIZE 64 #endif // RX FIFO can be disabled by setting this value to 0 #ifndef CFG_TUD_VENDOR_RX_BUFSIZE -#define CFG_TUD_VENDOR_RX_BUFSIZE 64 + #define CFG_TUD_VENDOR_RX_BUFSIZE 64 #endif // TX FIFO can be disabled by setting this value to 0 #ifndef CFG_TUD_VENDOR_TX_BUFSIZE -#define CFG_TUD_VENDOR_TX_BUFSIZE 64 + #define CFG_TUD_VENDOR_TX_BUFSIZE 64 #endif #ifdef __cplusplus - extern "C" { +extern "C" { #endif //--------------------------------------------------------------------+ // Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1 //--------------------------------------------------------------------+ -bool tud_vendor_n_mounted (uint8_t itf); -uint32_t tud_vendor_n_available (uint8_t itf); -uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize); -bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8); -void tud_vendor_n_read_flush (uint8_t itf); +bool tud_vendor_n_mounted(uint8_t idx); +uint32_t tud_vendor_n_available(uint8_t idx); +bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8); -uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); -uint32_t tud_vendor_n_write_flush (uint8_t itf); -uint32_t tud_vendor_n_write_available (uint8_t itf); +uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize); +uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count); +void tud_vendor_n_read_flush(uint8_t idx); -TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str); +uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize); +uint32_t tud_vendor_n_write_flush(uint8_t idx); +uint32_t tud_vendor_n_write_available(uint8_t idx); + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t idx, const char *str) { + return tud_vendor_n_write(idx, str, strlen(str)); +} // backward compatible -#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf) +#define tud_vendor_n_flush(idx) tud_vendor_n_write_flush(idx) //--------------------------------------------------------------------+ // Application API (Single Port) i.e CFG_TUD_VENDOR = 1 //--------------------------------------------------------------------+ - -TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t itf, char const* str) { - return tud_vendor_n_write(itf, str, strlen(str)); -} - TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_mounted(void) { - return tud_vendor_n_mounted(0); + return tud_vendor_n_mounted(0); } TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_available(void) { - return tud_vendor_n_available(0); + return tud_vendor_n_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t *ui8) { + return tud_vendor_n_peek(0, ui8); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) { - return tud_vendor_n_read(0, buffer, bufsize); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void *buffer, uint32_t bufsize) { + return tud_vendor_n_read(0, buffer, bufsize); } -TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t* ui8) { - return tud_vendor_n_peek(0, ui8); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read_discard(uint32_t count) { + return tud_vendor_n_read_discard(0, count); } TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) { - tud_vendor_n_read_flush(0); + tud_vendor_n_read_flush(0); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(void const* buffer, uint32_t bufsize) { - return tud_vendor_n_write(0, buffer, bufsize); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(const void *buffer, uint32_t bufsize) { + return tud_vendor_n_write(0, buffer, bufsize); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(char const* str) { - return tud_vendor_n_write_str(0, str); +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(const char *str) { + return tud_vendor_n_write_str(0, str); } TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) { - return tud_vendor_n_write_flush(0); + return tud_vendor_n_write_flush(0); } #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) { - return tud_vendor_n_write_available(0); + return tud_vendor_n_write_available(0); } #endif @@ -119,9 +122,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) { //--------------------------------------------------------------------+ // Invoked when received new data -void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize); +void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize); // Invoked when last rx transfer finished -void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes); +void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes); //--------------------------------------------------------------------+ // Inline Functions @@ -134,11 +137,11 @@ void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes); void vendord_init(void); bool vendord_deinit(void); void vendord_reset(uint8_t rhport); -uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *idx_desc, uint16_t max_len); bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); #ifdef __cplusplus - } +} #endif #endif /* TUSB_VENDOR_DEVICE_H_ */ From 9a9bf0fd7b5a005fd4701ab6cb5b1728ecc0baa2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Nov 2025 18:41:57 +0700 Subject: [PATCH 3/5] change tud_vendor_rx_cb() behavior, buffer and bufsize only available when CFG_TUD_VENDOR_RX_BUFSIZE = 0 update vendor device to omit ep buf when dedicated hwfifo is supported --- examples/device/webusb_serial/src/main.c | 15 ++++---- src/class/cdc/cdc_device.c | 11 +++--- src/class/vendor/vendor_device.c | 45 ++++++++++++++++++------ src/class/vendor/vendor_device.h | 9 +++-- src/common/tusb_types.h | 5 +++ src/tusb.c | 25 +++++++------ 6 files changed, 73 insertions(+), 37 deletions(-) diff --git a/examples/device/webusb_serial/src/main.c b/examples/device/webusb_serial/src/main.c index 4fcddd7247..50794bdbac 100644 --- a/examples/device/webusb_serial/src/main.c +++ b/examples/device/webusb_serial/src/main.c @@ -211,15 +211,16 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ return false; } -void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize) { +void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) { (void)idx; + (void)buffer; + (void)bufsize; -// since we used data without pulling it from RX FIFO, we need to discard number of items used -#if CFG_TUD_VENDOR_RX_BUFSIZE > 0 - tud_vendor_read_discard(bufsize); -#endif - - echo_all(buffer, bufsize); + while (tud_vendor_available()) { + uint8_t buf[64]; + const uint32_t count = tud_vendor_read(buf, sizeof(buf)); + echo_all(buf, count); + } } //--------------------------------------------------------------------+ diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 7ef8aa7385..d7792afe45 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -266,9 +266,8 @@ void cdcd_init(void) { uint8_t *epout_buf = NULL; uint8_t *epin_buf = NULL; #else - cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[i]; - uint8_t *epout_buf = p_epbuf->epout; - uint8_t *epin_buf = p_epbuf->epin; + uint8_t *epout_buf = _cdcd_epbuf[i].epout; + uint8_t *epin_buf = _cdcd_epbuf[i].epin; #endif tu_edpt_stream_init(&p_cdc->stream.rx, false, false, false, p_cdc->stream.rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, @@ -516,11 +515,9 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ } // Data sent to host, we continue to fetch from tx fifo to send. - // Note: This will cause incorrect baudrate set in line coding. - // Though maybe the baudrate is not really important !!! + // Note: This will cause incorrect baudrate set in line coding. Though maybe the baudrate is not really important ! if (ep_addr == stream_tx->ep_addr) { - // invoke transmit callback to possibly refill tx fifo - tud_cdc_tx_complete_cb(itf); + tud_cdc_tx_complete_cb(itf); // invoke callback to possibly refill tx fifo if (0 == tu_edpt_stream_write_xfer(rhport, stream_tx)) { // If there is no data left, a ZLP should be sent if needed diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index c2393c25d9..bb736aba8e 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -59,20 +59,30 @@ typedef struct { static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR]; +#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || CFG_TUD_VENDOR_RX_BUFSIZE == 0 typedef struct { + // Skip local EP buffer if dedicated hw FIFO is supported + #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || CFG_TUD_VENDOR_RX_BUFSIZE == 0 TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE); + #endif + + // Skip local EP buffer if dedicated hw FIFO is supported + #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE); + #endif } vendord_epbuf_t; CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; + #endif //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ -TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize) { + +TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) { (void)idx; - (void) buffer; - (void) bufsize; + (void)buffer; + (void)bufsize; } TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) { @@ -153,7 +163,19 @@ void vendord_init(void) { for(uint8_t i=0; i 0 uint8_t *rx_ff_buf = p_itf->stream.rx_ff_buf; @@ -161,7 +183,7 @@ void vendord_init(void) { uint8_t *rx_ff_buf = NULL; #endif - tu_edpt_stream_init(&p_itf->stream.rx, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, p_epbuf->epout, + tu_edpt_stream_init(&p_itf->stream.rx, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf, CFG_TUD_VENDOR_EPSIZE); #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 @@ -170,7 +192,7 @@ void vendord_init(void) { uint8_t *tx_ff_buf = NULL; #endif - tu_edpt_stream_init(&p_itf->stream.tx, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, p_epbuf->epin, + tu_edpt_stream_init(&p_itf->stream.tx, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf, CFG_TUD_VENDOR_EPSIZE); } } @@ -264,16 +286,19 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint const uint8_t idx = find_vendor_itf(ep_addr); TU_VERIFY(idx < CFG_TUD_VENDOR); vendord_interface_t *p_vendor = &_vendord_itf[idx]; - vendord_epbuf_t *p_epbuf = &_vendord_epbuf[idx]; if (ep_addr == p_vendor->stream.rx.ep_addr) { // Received new data: put into stream's fifo tu_edpt_stream_read_xfer_complete(&p_vendor->stream.rx, xferred_bytes); - // Invoked callback if any - tud_vendor_rx_cb(idx, p_epbuf->epout, (uint16_t)xferred_bytes); + // invoke callback + #if CFG_TUD_VENDOR_RX_BUFSIZE == 0 + tud_vendor_rx_cb(idx, p_vendor->stream.rx.ep_buf, xferred_bytes); + #else + tud_vendor_rx_cb(idx, NULL, 0); + #endif - tu_edpt_stream_read_xfer(rhport, &p_vendor->stream.rx); + tu_edpt_stream_read_xfer(rhport, &p_vendor->stream.rx); // prepare next data } else if (ep_addr == p_vendor->stream.tx.ep_addr) { // Send complete tud_vendor_tx_cb(idx, (uint16_t)xferred_bytes); diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index e9dec788bb..05de84f208 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -121,9 +121,12 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) { // Application Callback API (weak is optional) //--------------------------------------------------------------------+ -// Invoked when received new data -void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint16_t bufsize); -// Invoked when last rx transfer finished +// Invoked when received new data. +// - CFG_TUD_VENDOR_RX_BUFSIZE > 0; buffer and bufsize must not be used (both NULL,0) since data is in RX FIFO +// - CFG_TUD_VENDOR_RX_BUFSIZE = 0: Buffer and bufsize are valid +void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize); + +// Invoked when tx transfer is finished void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes); //--------------------------------------------------------------------+ diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 73c816e3ea..d473e53e68 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -323,6 +323,11 @@ typedef struct { tusb_speed_t speed; } tusb_rhport_init_t; +typedef struct { + uint16_t len; + uint8_t *buffer; +} tusb_buffer_t; + //--------------------------------------------------------------------+ // USB Descriptors //--------------------------------------------------------------------+ diff --git a/src/tusb.c b/src/tusb.c index 2d122885b0..94121b174b 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -450,12 +450,19 @@ uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t *s, const void *buf TU_VERIFY(bufsize > 0); // TODO support ZLP if (0 == tu_fifo_depth(&s->ff)) { - // non-fifo mode, ep_buf must be valid - TU_VERIFY(s->ep_buf != NULL, 0); + // non-fifo mode TU_VERIFY(stream_claim(hwid, s), 0); - const uint32_t xact_len = tu_min32(bufsize, s->ep_bufsize); - memcpy(s->ep_buf, buffer, xact_len); + uint32_t xact_len; + if (s->ep_buf != NULL) { + // using ep buf + xact_len = tu_min32(bufsize, s->ep_bufsize); + memcpy(s->ep_buf, buffer, xact_len); + } else { + // using hwfifo + xact_len = bufsize; + } TU_ASSERT(stream_xfer(hwid, s, (uint16_t) xact_len), 0); + return xact_len; } else { const uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize); @@ -494,7 +501,8 @@ uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) { //--------------------------------------------------------------------+ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) { if (0 == tu_fifo_depth(&s->ff)) { - // non-fifo mode + // non-fifo mode: RX need ep buffer + TU_VERIFY(s->ep_buf != NULL, 0); TU_VERIFY(stream_claim(hwid, s), 0); TU_ASSERT(stream_xfer(hwid, s, s->ep_bufsize), 0); return s->ep_bufsize; @@ -507,11 +515,8 @@ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) { // and slowly move it to the FIFO when read(). // This pre-check reduces endpoint claiming TU_VERIFY(available >= mps); - TU_VERIFY(stream_claim(hwid, s), 0); - - // get available again since fifo can be changed before endpoint is claimed - available = tu_fifo_remaining(&s->ff); + available = tu_fifo_remaining(&s->ff); // re-get available since fifo can be changed if (available >= mps) { // multiple of packet size limit by ep bufsize @@ -532,7 +537,7 @@ uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, ui if (tu_fifo_depth(&s->ff) > 0) { num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); } else { - // non-fifo mode + // non-fifo mode not support this num_read = 0; } From 1465a9b5bc64e39d9051b281ffc1f13b5cb6ad9e Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Nov 2025 19:14:23 +0700 Subject: [PATCH 4/5] make vendor_read_* API() is only available when CFG_TUD_VENDOR_RX_BUFSIZE > 0 vendor_write_flush() and write_available() only available when CFG_TUD_VENDOR_TX_BUFSIZE > 0 --- src/class/vendor/vendor_device.c | 4 ++++ src/class/vendor/vendor_device.h | 11 +++++++++-- src/common/tusb_fifo.c | 2 +- src/tusb.c | 9 +-------- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index bb736aba8e..d468bd0ba4 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -103,6 +103,7 @@ bool tud_vendor_n_mounted(uint8_t idx) { //--------------------------------------------------------------------+ // Read API //--------------------------------------------------------------------+ +#if CFG_TUD_VENDOR_RX_BUFSIZE > 0 uint32_t tud_vendor_n_available(uint8_t idx) { TU_VERIFY(idx < CFG_TUD_VENDOR, 0); vendord_interface_t *p_itf = &_vendord_itf[idx]; @@ -133,6 +134,7 @@ void tud_vendor_n_read_flush(uint8_t idx) { tu_edpt_stream_clear(&p_itf->stream.rx); tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->stream.rx); } +#endif //--------------------------------------------------------------------+ // Write API @@ -143,6 +145,7 @@ uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) { return tu_edpt_stream_write(p_itf->rhport, &p_itf->stream.tx, buffer, (uint16_t)bufsize); } +#if CFG_TUD_VENDOR_TX_BUFSIZE > 0 uint32_t tud_vendor_n_write_flush(uint8_t idx) { TU_VERIFY(idx < CFG_TUD_VENDOR, 0); vendord_interface_t *p_itf = &_vendord_itf[idx]; @@ -154,6 +157,7 @@ uint32_t tud_vendor_n_write_available(uint8_t idx) { vendord_interface_t *p_itf = &_vendord_itf[idx]; return tu_edpt_stream_write_available(p_itf->rhport, &p_itf->stream.tx); } +#endif //--------------------------------------------------------------------+ // USBD Driver API diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 05de84f208..daf9c19cf7 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -51,16 +51,21 @@ extern "C" { // Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1 //--------------------------------------------------------------------+ bool tud_vendor_n_mounted(uint8_t idx); + +#if CFG_TUD_VENDOR_RX_BUFSIZE > 0 uint32_t tud_vendor_n_available(uint8_t idx); bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8); - uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize); uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count); void tud_vendor_n_read_flush(uint8_t idx); +#endif uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize); + +#if CFG_TUD_VENDOR_TX_BUFSIZE > 0 uint32_t tud_vendor_n_write_flush(uint8_t idx); uint32_t tud_vendor_n_write_available(uint8_t idx); +#endif TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t idx, const char *str) { return tud_vendor_n_write(idx, str, strlen(str)); @@ -76,6 +81,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_mounted(void) { return tud_vendor_n_mounted(0); } +#if CFG_TUD_VENDOR_RX_BUFSIZE > 0 TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_available(void) { return tud_vendor_n_available(0); } @@ -95,6 +101,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read_discard(uint32_t co TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) { tud_vendor_n_read_flush(0); } +#endif TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(const void *buffer, uint32_t bufsize) { return tud_vendor_n_write(0, buffer, bufsize); @@ -104,11 +111,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(const char *st return tud_vendor_n_write_str(0, str); } +#if CFG_TUD_VENDOR_TX_BUFSIZE > 0 TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) { return tud_vendor_n_write_flush(0); } -#if CFG_TUD_VENDOR_TX_BUFSIZE > 0 TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) { return tud_vendor_n_write_available(0); } diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index f78167f94a..06b0d6a588 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -499,7 +499,7 @@ bool tu_fifo_read(tu_fifo_t *f, void *buffer) { return ret; } -// Read one item without removing it from the FIFO, correct rad index if overflowed +// Read one item without removing it from the FIFO, correct read index if overflowed bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) { return ff_peek_local(f, p_buffer, f->wr_idx, f->rd_idx); } diff --git a/src/tusb.c b/src/tusb.c index 94121b174b..b6cfd12600 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -533,14 +533,7 @@ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) { } uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) { - uint32_t num_read; - if (tu_fifo_depth(&s->ff) > 0) { - num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); - } else { - // non-fifo mode not support this - num_read = 0; - } - + const uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); tu_edpt_stream_read_xfer(hwid, s); return num_read; } From 6e9e9ce9d19d6aa30c69c6e6c82d09dde619f391 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Nov 2025 23:07:31 +0700 Subject: [PATCH 5/5] add CFG_TUD_VENDOR_RX_MANUAL_XFER per suggestion --- src/class/vendor/vendor_device.c | 13 ++++++++ src/class/vendor/vendor_device.h | 51 +++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index d468bd0ba4..62e183465d 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -136,6 +136,15 @@ void tud_vendor_n_read_flush(uint8_t idx) { } #endif +#if CFG_TUD_VENDOR_RX_MANUAL_XFER +bool tud_vendor_n_read_xfer(uint8_t idx) { + TU_VERIFY(idx < CFG_TUD_VENDOR); + vendord_interface_t *p_itf = &_vendord_itf[idx]; + return tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->stream.rx); +} +#endif + + //--------------------------------------------------------------------+ // Write API //--------------------------------------------------------------------+ @@ -274,7 +283,9 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin tu_edpt_stream_t *stream_rx = &p_vendor->stream.rx; if (stream_rx->ep_addr == 0) { tu_edpt_stream_open(stream_rx, desc_ep); + #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0 TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data + #endif } } } @@ -302,7 +313,9 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint tud_vendor_rx_cb(idx, NULL, 0); #endif + #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0 tu_edpt_stream_read_xfer(rhport, &p_vendor->stream.rx); // prepare next data + #endif } else if (ep_addr == p_vendor->stream.tx.ep_addr) { // Send complete tud_vendor_tx_cb(idx, (uint16_t)xferred_bytes); diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index daf9c19cf7..764d990702 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -27,8 +27,15 @@ #ifndef TUSB_VENDOR_DEVICE_H_ #define TUSB_VENDOR_DEVICE_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include "common/tusb_common.h" +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ #ifndef CFG_TUD_VENDOR_EPSIZE #define CFG_TUD_VENDOR_EPSIZE 64 #endif @@ -43,30 +50,53 @@ #define CFG_TUD_VENDOR_TX_BUFSIZE 64 #endif -#ifdef __cplusplus -extern "C" { +// Application will manually schedule RX transfer. This can be useful when using with non-fifo (buffered) mode +// i.e. CFG_TUD_VENDOR_RX_BUFSIZE = 0 +#ifndef CFG_TUD_VENDOR_RX_MANUAL_XFER + #define CFG_TUD_VENDOR_RX_MANUAL_XFER 0 #endif //--------------------------------------------------------------------+ // Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1 //--------------------------------------------------------------------+ -bool tud_vendor_n_mounted(uint8_t idx); + +// Return whether the vendor interface is mounted +bool tud_vendor_n_mounted(uint8_t idx); #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 +// Return number of available bytes for reading uint32_t tud_vendor_n_available(uint8_t idx); -bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8); + +// Peek a byte from RX buffer +bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8); + +// Read from RX FIFO uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize); + +// Discard count bytes in RX FIFO uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count); -void tud_vendor_n_read_flush(uint8_t idx); + +// Flush (clear) RX FIFO +void tud_vendor_n_read_flush(uint8_t idx); +#endif + +#if CFG_TUD_VENDOR_RX_MANUAL_XFER +// Start a new RX transfer to fill the RX FIFO, return false if previous transfer is still ongoing +bool tud_vendor_n_read_xfer(uint8_t idx); #endif +// Write to TX FIFO. This can be buffered and not sent immediately unless buffered bytes >= USB endpoint size uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize); #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 +// Force sending buffered data, return number of bytes sent uint32_t tud_vendor_n_write_flush(uint8_t idx); + +// Return number of bytes available for writing in TX FIFO uint32_t tud_vendor_n_write_available(uint8_t idx); #endif +// Write a null-terminated string to TX FIFO TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t idx, const char *str) { return tud_vendor_n_write(idx, str, strlen(str)); } @@ -103,6 +133,12 @@ TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) { } #endif +#if CFG_TUD_VENDOR_RX_MANUAL_XFER +TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_read_xfer(void) { + return tud_vendor_n_read_xfer(0); +} +#endif + TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(const void *buffer, uint32_t bufsize) { return tud_vendor_n_write(0, buffer, bufsize); } @@ -136,11 +172,6 @@ void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize); // Invoked when tx transfer is finished void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes); -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ - - //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+