From bc320ac9d98bfcb5de86fcf43e19998e0a6cc385 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 21 Nov 2025 00:09:35 +0100 Subject: dfu: use ep0 buffer if it is large enough Signed-off-by: HiFiPhile --- src/class/dfu/dfu_device.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src/class') diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index d3cc53918..6c42ca4e6 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -60,7 +60,9 @@ typedef struct { static dfu_state_ctx_t _dfu_ctx; +#if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE TU_ATTR_ALIGNED(4) uint8_t _transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE]; +#endif static void reset_state(void) { _dfu_ctx.state = DFU_IDLE; @@ -68,6 +70,15 @@ static void reset_state(void) { _dfu_ctx.flashing_in_progress = false; } +static inline uint8_t* get_xfer_buffer(void) { + // Use EP0 buffer if it is large enough, otherwise use dedicated buffer + #if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE + return _transfer_buf; + #else + return usbd_control_get_buffer(); + #endif +} + static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout); static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request); static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request); @@ -283,10 +294,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD); TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE); - const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _transfer_buf, + const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, get_xfer_buffer(), request->wLength); - return tud_control_xfer(rhport, request, _transfer_buf, xfer_len); + return tud_control_xfer(rhport, request, get_xfer_buffer(), xfer_len); } break; @@ -306,7 +317,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control if (request->wLength > 0) { // Download with payload -> transition to DOWNLOAD SYNC _dfu_ctx.state = DFU_DNLOAD_SYNC; - return tud_control_xfer(rhport, request, _transfer_buf, request->wLength); + return tud_control_xfer(rhport, request, get_xfer_buffer(), request->wLength); } else { // Download is complete -> transition to MANIFEST SYNC _dfu_ctx.state = DFU_MANIFEST_SYNC; @@ -380,7 +391,7 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus } else if (stage == CONTROL_STAGE_ACK) { if (_dfu_ctx.flashing_in_progress) { _dfu_ctx.state = DFU_DNBUSY; - tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _transfer_buf, _dfu_ctx.length); + tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, get_xfer_buffer(), _dfu_ctx.length); } else { _dfu_ctx.state = DFU_DNLOAD_IDLE; } -- cgit v1.3.1 From 3d809988b2fc7ff567c3bc98f8bd2a154095837a Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 21 Nov 2025 00:29:02 +0100 Subject: audio: use ep0 buffer if it is large enough Signed-off-by: HiFiPhile --- src/class/audio/audio_device.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'src/class') diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 55eba81dd..02f9257af 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -147,7 +147,9 @@ tu_static CFG_TUD_MEM_SECTION struct { #endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO // Control buffer -CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ]; +#if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE +tu_static CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ]; +#endif // Aligned buffer for feedback EP #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP @@ -423,6 +425,15 @@ bool tud_audio_n_mounted(uint8_t func_id) { return audio->mounted; } +static inline uint8_t* get_ctrl_buffer(void) { + // Use EP0 buffer if it is large enough, otherwise use dedicated buffer + #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE + return ctrl_buf; + #else + return usbd_control_get_buffer(); + #endif +} + //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ @@ -1296,20 +1307,20 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const if (tud_audio_n_version(func_id) == 2) { uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) { - _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf); + _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()); audiod_calc_tx_packet_sz(&_audiod_fct[func_id]); } } #endif // Invoke callback - return tud_audio_set_req_entity_cb(rhport, p_request, ctrl_buf); + return tud_audio_set_req_entity_cb(rhport, p_request, get_ctrl_buffer()); } else { // Find index of audio driver structure and verify interface really exists TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); // Invoke callback - return tud_audio_set_req_itf_cb(rhport, p_request, ctrl_buf); + return tud_audio_set_req_itf_cb(rhport, p_request, get_ctrl_buffer()); } } break; @@ -1324,7 +1335,7 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const if (_audiod_fct[func_id].ep_in == ep) { uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) { - _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf) & 0x00FFFFFF; + _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()) & 0x00FFFFFF; audiod_calc_tx_packet_sz(&_audiod_fct[func_id]); } } @@ -1332,7 +1343,7 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const #endif // Invoke callback - bool ret = tud_audio_set_req_ep_cb(rhport, p_request, ctrl_buf); + bool ret = tud_audio_set_req_ep_cb(rhport, p_request, get_ctrl_buffer()); #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP if (ret && tud_audio_n_version(func_id) == 1) { @@ -1429,7 +1440,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const } // If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished - TU_VERIFY(tud_control_xfer(rhport, p_request, ctrl_buf, sizeof(ctrl_buf))); + TU_VERIFY(tud_control_xfer(rhport, p_request, get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ)); return true; } @@ -1695,11 +1706,8 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req return false; } - // Crop length - if (len > sizeof(ctrl_buf)) len = sizeof(ctrl_buf); - // Copy into buffer - TU_VERIFY(0 == tu_memcpy_s(ctrl_buf, sizeof(ctrl_buf), data, (size_t) len)); + TU_VERIFY(0 == tu_memcpy_s(get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ, data, (size_t) len)); #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL if (tud_audio_n_version(func_id) == 2) { @@ -1708,7 +1716,7 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req uint8_t entityID = TU_U16_HIGH(p_request->wIndex); uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) { - _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf); + _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()); audiod_calc_tx_packet_sz(&_audiod_fct[func_id]); } } @@ -1716,7 +1724,7 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req #endif // Schedule transmit - return tud_control_xfer(rhport, p_request, ctrl_buf, len); + return tud_control_xfer(rhport, p_request, get_ctrl_buffer(), len); } // Verify an entity with the given ID exists and returns also the corresponding driver index -- cgit v1.3.1 From c5f91ec6f54509d03529e276ffd42b0d9abab695 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sun, 14 Dec 2025 21:33:55 +0100 Subject: reduce code size Signed-off-by: HiFiPhile --- src/class/audio/audio_device.c | 2 +- src/class/dfu/dfu_device.c | 2 +- src/device/usbd_control.c | 19 ++++++------------- src/device/usbd_pvt.h | 7 +++++-- 4 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src/class') diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 02f9257af..03cb51efe 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -430,7 +430,7 @@ static inline uint8_t* get_ctrl_buffer(void) { #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE return ctrl_buf; #else - return usbd_control_get_buffer(); + return _usbd_ctrl_epbuf.buf; #endif } diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 6c42ca4e6..36f9ffcf4 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -75,7 +75,7 @@ static inline uint8_t* get_xfer_buffer(void) { #if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE return _transfer_buf; #else - return usbd_control_get_buffer(); + return _usbd_ctrl_epbuf.buf; #endif } diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 49fdce214..1064185d4 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -59,9 +59,7 @@ typedef struct { static usbd_control_xfer_t _ctrl_xfer; -CFG_TUD_MEM_SECTION static struct { - TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); -} _ctrl_epbuf; +CFG_TUD_MEM_SECTION usbd_ctrl_epbuf_t _usbd_ctrl_epbuf; //--------------------------------------------------------------------+ // Application API @@ -93,12 +91,12 @@ static bool data_stage_xact(uint8_t rhport) { if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { ep_addr = EDPT_CTRL_IN; - if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { - TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); + if (0u != xact_len && _ctrl_xfer.buffer != _usbd_ctrl_epbuf.buf) { + TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); } } - return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_epbuf.buf : NULL, xact_len, false); } // Transmit data to/from the control endpoint. @@ -121,11 +119,6 @@ bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, voi return true; } -// Get control transfer endpoint buffer -uint8_t* usbd_control_get_buffer(void) { - return _ctrl_epbuf.buf; -} - //--------------------------------------------------------------------+ // USBD API //--------------------------------------------------------------------+ @@ -174,8 +167,8 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { TU_VERIFY(_ctrl_xfer.buffer); - if (_ctrl_xfer.buffer != _ctrl_epbuf.buf) { - memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes); + if (_ctrl_xfer.buffer != _usbd_ctrl_epbuf.buf) { + memcpy(_ctrl_xfer.buffer, _usbd_ctrl_epbuf.buf, xferred_bytes); } TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); } diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index f556b556b..bc9a737b2 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -72,8 +72,11 @@ void usbd_int_set(bool enabled); void usbd_spin_lock(bool in_isr); void usbd_spin_unlock(bool in_isr); -// Get control transfer endpoint buffer -uint8_t* usbd_control_get_buffer(void); +typedef struct { + TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); +} usbd_ctrl_epbuf_t; + +extern usbd_ctrl_epbuf_t _usbd_ctrl_epbuf; //--------------------------------------------------------------------+ // USBD Endpoint API -- cgit v1.3.1 From fef11cd4451f0f9c6e5bf5b5f2c067a9c54b1216 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sun, 28 Dec 2025 16:37:12 +0100 Subject: usbd/ncm: reset notification state on link down Signed-off-by: HiFiPhile --- src/class/net/ncm_device.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/class') diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 3e19b5f3a..1baef146d 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -940,6 +940,9 @@ bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t if (ncm_interface.itf_data_alt == 1) { tud_network_recv_renew_r(rhport); notification_xmit(rhport, false); + } else { + // Reset notification state to send link state update when interface is re-activated + ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED; } tud_control_status(rhport, request); } break; -- cgit v1.3.1 From 50256886a92e53c16b7fe8d47b1cee8ece086d98 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sun, 28 Dec 2025 16:38:58 +0100 Subject: usbd/ncm: implement copy-free ntb management Signed-off-by: HiFiPhile --- src/class/net/ncm_device.c | 82 +++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 23 deletions(-) (limited to 'src/class') diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 1baef146d..3e6891ed7 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -86,14 +86,24 @@ typedef struct { // recv handling recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs - recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic + recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic (circular buffer) + #if RECV_NTB_N > 1 + uint8_t recv_ready_head; // head index for recv_ready_ntb circular buffer + uint8_t recv_ready_tail; // tail index for recv_ready_ntb circular buffer + uint8_t recv_ready_count; // number of elements in recv_ready_ntb circular buffer + #endif recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram // xmit handling xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs - xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB + xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB (circular buffer) + #if XMIT_NTB_N > 1 + uint8_t xmit_ready_head; // head index for xmit_ready_ntb circular buffer + uint8_t xmit_ready_tail; // tail index for xmit_ready_ntb circular buffer + uint8_t xmit_ready_count; // number of elements in xmit_ready_ntb circular buffer + #endif xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver uint16_t xmit_sequence; // NTB sequence counter @@ -279,13 +289,17 @@ static xmit_ntb_t *xmit_get_free_ntb(void) { static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) { TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength); - for (int i = 0; i < XMIT_NTB_N; ++i) { - if (ncm_interface.xmit_ready_ntb[i] == NULL) { - ncm_interface.xmit_ready_ntb[i] = ready_ntb; - return; - } +#if XMIT_NTB_N == 1 + ncm_interface.xmit_ready_ntb[0] = ready_ntb; +#else + if (ncm_interface.xmit_ready_count >= XMIT_NTB_N) { + TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen + return; } - TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen + ncm_interface.xmit_ready_ntb[ncm_interface.xmit_ready_head] = ready_ntb; + ncm_interface.xmit_ready_head = (ncm_interface.xmit_ready_head + 1) % XMIT_NTB_N; + ncm_interface.xmit_ready_count++; +#endif } // xmit_put_ntb_into_ready_list /** @@ -293,14 +307,23 @@ static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) { * If the ready list is empty, return NULL. */ static xmit_ntb_t *xmit_get_next_ready_ntb(void) { - xmit_ntb_t *r = NULL; +#if XMIT_NTB_N == 1 + xmit_ntb_t *r = ncm_interface.xmit_ready_ntb[0]; + ncm_interface.xmit_ready_ntb[0] = NULL; + TU_LOG_DRV("xmit_get_next_ready_ntb: %p\n", r); + return r; +#else + if (ncm_interface.xmit_ready_count == 0) { + return NULL; // empty + } - r = ncm_interface.xmit_ready_ntb[0]; - memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0])); - ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL; + xmit_ntb_t *r = ncm_interface.xmit_ready_ntb[ncm_interface.xmit_ready_tail]; + ncm_interface.xmit_ready_tail = (ncm_interface.xmit_ready_tail + 1) % XMIT_NTB_N; + ncm_interface.xmit_ready_count--; - TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r); + TU_LOG_DRV("xmit_get_next_ready_ntb: %p\n", r); return r; +#endif } // xmit_get_next_ready_ntb /** @@ -458,14 +481,23 @@ static recv_ntb_t *recv_get_free_ntb(void) { * If the ready list is empty, return NULL. */ static recv_ntb_t *recv_get_next_ready_ntb(void) { - recv_ntb_t *r = NULL; +#if RECV_NTB_N == 1 + recv_ntb_t *r = ncm_interface.recv_ready_ntb[0]; + ncm_interface.recv_ready_ntb[0] = NULL; + TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r); + return r; +#else + if (ncm_interface.recv_ready_count == 0) { + return NULL; // empty + } - r = ncm_interface.recv_ready_ntb[0]; - memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0])); - ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL; + recv_ntb_t *r = ncm_interface.recv_ready_ntb[ncm_interface.recv_ready_tail]; + ncm_interface.recv_ready_tail = (ncm_interface.recv_ready_tail + 1) % RECV_NTB_N; + ncm_interface.recv_ready_count--; TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r); return r; +#endif } // recv_get_next_ready_ntb /** @@ -490,13 +522,17 @@ static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) { static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) { TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength); - for (int i = 0; i < RECV_NTB_N; ++i) { - if (ncm_interface.recv_ready_ntb[i] == NULL) { - ncm_interface.recv_ready_ntb[i] = ready_ntb; - return; - } +#if RECV_NTB_N == 1 + ncm_interface.recv_ready_ntb[0] = ready_ntb; +#else + if (ncm_interface.recv_ready_count >= RECV_NTB_N) { + TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen + return; } - TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen + ncm_interface.recv_ready_ntb[ncm_interface.recv_ready_head] = ready_ntb; + ncm_interface.recv_ready_head = (ncm_interface.recv_ready_head + 1) % RECV_NTB_N; + ncm_interface.recv_ready_count++; +#endif } // recv_put_ntb_into_ready_list /** -- cgit v1.3.1 From cab1b2f6f741ea151e87f139b2115d5234228bbd Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Sun, 4 Jan 2026 16:55:43 +0100 Subject: Implement Printer Device Class --- examples/device/printer_to_hid/CMakeLists.txt | 29 ++ examples/device/printer_to_hid/CMakePresets.json | 6 + examples/device/printer_to_hid/Makefile | 11 + examples/device/printer_to_hid/README.md | 0 examples/device/printer_to_hid/src/main.c | 219 +++++++++++++++ examples/device/printer_to_hid/src/tusb_config.h | 115 ++++++++ .../device/printer_to_hid/src/usb_descriptors.c | 247 +++++++++++++++++ .../device/printer_to_hid/src/usb_descriptors.h | 30 ++ src/CMakeLists.txt | 1 + src/class/printer/printer.h | 277 +++++++++++++++++++ src/class/printer/printer_device.c | 306 +++++++++++++++++++++ src/class/printer/printer_device.h | 79 ++++++ src/device/usbd.c | 13 + src/device/usbd.h | 15 + src/tinyusb.mk | 1 + src/tusb.h | 4 + src/tusb_option.h | 4 + tools/iar_template.ipcf | 5 + 18 files changed, 1362 insertions(+) create mode 100644 examples/device/printer_to_hid/CMakeLists.txt create mode 100644 examples/device/printer_to_hid/CMakePresets.json create mode 100644 examples/device/printer_to_hid/Makefile create mode 100644 examples/device/printer_to_hid/README.md create mode 100644 examples/device/printer_to_hid/src/main.c create mode 100644 examples/device/printer_to_hid/src/tusb_config.h create mode 100644 examples/device/printer_to_hid/src/usb_descriptors.c create mode 100644 examples/device/printer_to_hid/src/usb_descriptors.h create mode 100644 src/class/printer/printer.h create mode 100644 src/class/printer/printer_device.c create mode 100644 src/class/printer/printer_device.h (limited to 'src/class') diff --git a/examples/device/printer_to_hid/CMakeLists.txt b/examples/device/printer_to_hid/CMakeLists.txt new file mode 100644 index 000000000..f58759059 --- /dev/null +++ b/examples/device/printer_to_hid/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.20) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +project(printer_to_hid C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}) +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT_NAME}) + +# Example source +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT_NAME} noos) diff --git a/examples/device/printer_to_hid/CMakePresets.json b/examples/device/printer_to_hid/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/printer_to_hid/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/printer_to_hid/Makefile b/examples/device/printer_to_hid/Makefile new file mode 100644 index 000000000..1a4b428dc --- /dev/null +++ b/examples/device/printer_to_hid/Makefile @@ -0,0 +1,11 @@ +include ../../../hw/bsp/family_support.mk + +INC += \ + src \ + + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../../hw/bsp/family_rules.mk diff --git a/examples/device/printer_to_hid/README.md b/examples/device/printer_to_hid/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c new file mode 100644 index 000000000..f5012478c --- /dev/null +++ b/examples/device/printer_to_hid/src/main.c @@ -0,0 +1,219 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +#include "usb_descriptors.h" + +// -------------------------------------------------------------------+ +// Variables controlled with USB endpoint callbacks +// -------------------------------------------------------------------+ + +// usb interface pointer +uint8_t printer_itf = 0; +// pendings bytes in usb endpoint buffer ; must process these bytes +uint8_t pending_bytes_on_usb_ep = 0; + + +// -------------------------------------------------------------------+ +// Variables controlled locally +// -------------------------------------------------------------------+ + +// local data buffer (copy data from usb endpoint into this buffer) ; acts as fifo +uint8_t data_buffer[16] = {0}; +// write offset for usb/printer incoming data to data_buffer +size_t data_rx_offset = 0; +// read offset for usb/hid outgoing data from data_buffer +size_t data_tx_offset = 0; +// available space in data_buffer +size_t data_available = sizeof(data_buffer); + +// next keycode to send on usb/hid +uint8_t next_keycode = 0; +// next key modifiers to send on usb/hid +uint8_t next_modifiers = 0; +// whether the next usb/hid report must be NULL to release the last keystroke +uint8_t next_keycode_is_release = false; + + +// -------------------------------------------------------------------+ +// Tasks +// -------------------------------------------------------------------+ + +// Every 10ms, we will place HID data in the usb/hid endpoint, ready to +// sent to the host when required. The tasks will read the keycode in +// next_keycode and place it in the HID report, then set next_is_null +// such that the key is released by the next report. This seem to help +// stroking the same key twice when the character is repeated in the data. +void hid_tx_task(void) { + // Poll every 10ms + const uint32_t interval_ms = 10; + static uint32_t start_ms = 0; + + if (!tud_hid_ready()) { + return; + } + + if (board_millis() - start_ms < interval_ms) { + return; // not enough time + } + start_ms += interval_ms; + + if (next_keycode_is_release || next_keycode == 0) { + tud_hid_keyboard_report(1, 0, NULL); + next_keycode_is_release = false; + return; + } + + uint8_t keycode_array[6] = {0}; + keycode_array[0] = next_keycode; + tud_hid_keyboard_report(1, next_modifiers, keycode_array); + next_keycode_is_release = true; + next_keycode = 0; +} + +// Whenever there are pendings bytes on the USB endpoint, we will pull them from the +// endpoint buffer and write then in the local data buffer. We must take care to not +// overwrite local data that is not processed yet, so we use data_buffer as a fifo. +// We do not have to take care of reading correctly from the endpoint buffer, as all +// is done well by tud_printer_n_read(). +void printer_rx_task(void) { + if (pending_bytes_on_usb_ep > 0) { + size_t len1 = data_available; + size_t len2 = 0; + if (len1 < 0) { + len1 = 0; + } + if (data_rx_offset + len1 > sizeof(data_buffer)) { + len2 = len1 - (sizeof(data_buffer) - data_rx_offset); + len1 = sizeof(data_buffer) - data_rx_offset; + } + uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); + if (len2 > 0) { + count += tud_printer_n_read(printer_itf, data_buffer, len2); + } + + if (count > 0) { + data_available -= count; + pending_bytes_on_usb_ep -= count; + data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); + } + } +} + +// The HID keycodes are not binary mapped like UTF8 codes. If we want to send the +// data received as a usb/printer, we have to translate the binary data for the +// usb/hid interface. Note that the simple mapping below will be valid only for +// hosts expecting hid data from a QWERTY keyboard. Also note that only a-zA-Z0-9 +// characters are converted, for simplicity of the example. Other characters are +// converted to spaces. +void translation_task(void) { + if (data_tx_offset != data_rx_offset || data_available == 0) { + // If data_tx_offset and data_rx_offset have different values, then we + // can proceed: translate, prepare for TX, and advance data_tx_offset. + // + // If the buffer is full (data_available == 0), then we must also + // translate data and prepare it for TX. But the data_tx_offset and + // data_rx_offset will have the same value, since RX caught up to TX. + // Hence the OR. + + // Translate UTF8 to HID keystroke + char c = data_buffer[data_tx_offset]; + uint8_t m = 0; + if ('a' <= c && c <= 'z') { + c -= 'a'; + c += HID_KEY_A; + } else if ('A' <= c && c <= 'Z') { + c -= 'a'; + c += HID_KEY_A; + m = KEYBOARD_MODIFIER_LEFTSHIFT; + } else if ('1' <= c && c <= '9') { + c -= '1'; + c += HID_KEY_1; + } else if (c == '0') { + c = HID_KEY_0; + } else { + c = HID_KEY_SPACE; + } + + // Proceed only if there are no characters pending for TX + if (next_keycode == 0) { + // Prepare next keystroke with translated data + next_keycode = c; + next_modifiers = m; + // Increment read offset + data_tx_offset += 1; + data_tx_offset %= sizeof(data_buffer); + data_available += 1; + } + } +} + +int main(void) { + + board_init(); + tud_init(BOARD_TUD_RHPORT); // init device stack on configured roothub port + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + while (1) { + tud_task(); // tinyusb device task + printer_rx_task(); // read data sent by host on our printer interface + translation_task(); // translate printer's UTF8 to HID keycodes + hid_tx_task(); // send data to host with our HID interface + } +} + + +//--------------------------------------------------------------------+ +// Printer callbacks +//--------------------------------------------------------------------+ + +// Data was received on endpoint buffer +void tud_printer_rx_cb(uint8_t itf, size_t n) { + printer_itf = itf; // get interface from which to read endpoint buffer + pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer +} + + +//--------------------------------------------------------------------+ +// HID callbacks +//--------------------------------------------------------------------+ + +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, + uint16_t reqlen) { + return 0; +} + +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, + uint16_t bufsize) { + return; +} diff --git a/examples/device/printer_to_hid/src/tusb_config.h b/examples/device/printer_to_hid/src/tusb_config.h new file mode 100644 index 000000000..a8dbac89d --- /dev/null +++ b/examples/device/printer_to_hid/src/tusb_config.h @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +#define BOARD_DEVICE_RHPORT_NUM 3 + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT + #define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED + #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS + #define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION + #define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN + #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_PRINTER 1 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 16 + +// Printer buffer size Should be sufficient to hold data +#define CFG_TUD_PRINTER_RX_BUFSIZE 16 +#define CFG_TUD_PRINTER_TX_BUFSIZE 16 +#define CFG_TUD_PRINTER_EP_BUFSIZE 16 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c new file mode 100644 index 000000000..161bbb7c5 --- /dev/null +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -0,0 +1,247 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "bsp/board_api.h" +#include "tusb.h" +#include "usb_descriptors.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0) +#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \ + PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) ) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +static tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +uint8_t const desc_hid_report[] = +{ + TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )) +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) +{ + (void) instance; + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_HID, + ITF_NUM_PRINTER, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + +// HID interface endpoints +#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 +// Printer interface endpoints +#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 +#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // HID: + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPADDR_HID, CFG_TUD_HID_EP_BUFSIZE, 5), + + // Printer: + // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE) +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// other speed configuration +static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = +{ + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const* tud_descriptor_device_qualifier_cb(void) +{ + return (uint8_t const*) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + // other speed config is basically configuration with type = OTHER_SPEED_CONFIG + memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN); + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + // this example use the same configuration for both high and full speed mode + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + // This example use the same configuration for both high and full speed mode + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +}; + +// array of pointer to string descriptors +static char const *string_desc_arr[] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serials will use unique ID if possible +}; + +static uint16_t _desc_str[32 + 1]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + + switch ( index ) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) chr_count = max_count; + + // Convert ASCII string into UTF-16 + for ( size_t i = 0; i < chr_count; i++ ) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h new file mode 100644 index 000000000..53c4e1fbd --- /dev/null +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -0,0 +1,30 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef USB_DESCRIPTORS_H_ +#define USB_DESCRIPTORS_H_ + +REPORT_ID_KEYBOARD = 1 + +#endif /* USB_DESCRIPTORS_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48dc75e50..00f466007 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/mtp/mtp_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ncm_device.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/printer/printer_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h new file mode 100644 index 000000000..4cd1d62fc --- /dev/null +++ b/src/class/printer/printer.h @@ -0,0 +1,277 @@ +#include +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) + + #include "device/usbd.h" + #include "device/usbd_pvt.h" + +// #include "bsp/board_api.h" + + #include "printer_device.h" + #include "printer.h" + + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + /*------------- From this point, data is not cleared by bus reset -------------*/ + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); +} printer_interface_t; + + #define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, wanted_char) + +typedef struct { + TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); +} printer_epbuf_t; + +static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; +CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; + + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static tud_printer_configure_fifo_t _printer_fifo_cfg; + +static bool _prep_out_transaction(uint8_t itf) { + const uint8_t rhport = 0; + printer_interface_t *p_printer = &_printer_itf[itf]; + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready() && p_printer->ep_out); + + uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_printer->rx_ff); + + if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + } else { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_printer->ep_out); + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +uint32_t tud_printer_n_available(uint8_t itf) { + return tu_fifo_count(&_printer_itf[itf].rx_ff); +} + +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { + printer_interface_t *p_printer = &_printer_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { + return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); +} + +void tud_printer_n_read_flush(uint8_t itf) { + printer_interface_t *p_printer = &_printer_itf[itf]; + tu_fifo_clear(&p_printer->rx_ff); + _prep_out_transaction(itf); +} + + +//--------------------------------------------------------------------+ +// USBD PRINTER DRIVER API +//--------------------------------------------------------------------+ + +void printer_init(void) { + tu_memclr(_printer_itf, sizeof(_printer_itf)); + tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); + tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); + osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); + TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); + tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); + #endif + } +} + +bool printer_deinit(void) { + #if OSAL_MUTEX_REQUIRED + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + + if (mutex_rd) { + osal_mutex_delete(mutex_rd); + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); + } + + if (mutex_wr) { + osal_mutex_delete(mutex_wr); + tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); + } + } + #endif + + return true; +} + +void printer_reset(uint8_t rhport) { + (void)rhport; + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_memclr(p_printer, sizeof(p_printer)); + if (!_printer_fifo_cfg.rx_persistent) { + tu_fifo_clear(&p_printer->rx_ff); + } + if (!_printer_fifo_cfg.tx_persistent) { + tu_fifo_clear(&p_printer->tx_ff); + } + // tu_fifo_set_overwritable(&p_printer->rx_ff, true); + tu_fifo_set_overwritable(&p_printer->tx_ff, true); + } +} + +uint16_t printer_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); + + // Identify available interface to open + printer_interface_t *p_printer; + uint8_t printer_id; + for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { + p_printer = &_printer_itf[printer_id]; + if (p_printer->ep_out == 0) { + break; + } + } + TU_ASSERT(printer_id < CFG_TUD_PRINTER); + + //------------- Interface -------------// + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + //------------- Endpoints -------------// + TU_ASSERT(itf_desc->bNumEndpoints == 2); + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + p_printer->itf_num = 2; + const uint8_t *p_desc = tu_desc_next(itf_desc); + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); + + _prep_out_transaction(printer_id); + + return drv_len; +} + +bool printer_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { + //------------- STD Request -------------// + if (stage != CONTROL_STAGE_SETUP) { + return true; + } + } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { + switch (request->bRequest) { + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + case PRINTER_REQ_CONTROL_GET_DEVICE_ID: + if (stage == CONTROL_STAGE_SETUP) { + const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" + "MODEL:LaserBeam 9;" + "COMMAND SET:PS;" + "COMMENT:Anything you like;" + "ACTIVE COMMAND SET:PS;"; + char buffer[256]; + strcpy(buffer + 2, deviceId); + buffer[0] = 0x00; + buffer[1] = strlen(deviceId); + return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + } + break; + case PRINTER_REQ_CONTROL_GET_PORT_STATUS: + if (stage == CONTROL_STAGE_SETUP) { + static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); + } + break; + case PRINTER_REQ_CONTROL_SOFT_RESET: + if (stage == CONTROL_STAGE_SETUP) { + return false; // what to do ? + } + break; + default: + return false; + } + } else { + return false; + } + return true; +} + +bool printer_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + uint8_t itf; + printer_interface_t *p_printer; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { + p_printer = &_printer_itf[itf]; + if (ep_addr == p_printer->ep_out) { + break; + } + } + TU_ASSERT(itf < CFG_TUD_PRINTER); + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Received new data + if (ep_addr == p_printer->ep_out) { + tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); + // invoke receive callback (if there is still data) + if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + tud_printer_rx_cb(itf, xferred_bytes); + } + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + return true; +} + +#endif diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c new file mode 100644 index 000000000..efeae100f --- /dev/null +++ b/src/class/printer/printer_device.c @@ -0,0 +1,306 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) + + //--------------------------------------------------------------------+ + // INCLUDE + //--------------------------------------------------------------------+ + #include "device/usbd.h" + #include "device/usbd_pvt.h" + + #include "printer_device.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t itf_num; + uint8_t ep_out; // Bulk Out endpoint + uint8_t ep_in; // optional Bulk In endpoint + + /*------------- From this point, data is not cleared by bus reset -------------*/ + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); +} printer_interface_t; + +typedef struct { + TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); +} printer_epbuf_t; + +static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; +CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; + + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static tud_printer_configure_fifo_t _printer_fifo_cfg; + +static bool _prep_out_transaction(uint8_t itf) { + const uint8_t rhport = 0; + printer_interface_t *p_printer = &_printer_itf[itf]; + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready() && p_printer->ep_out); + + uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_printer->rx_ff); + + if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + } else { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_printer->ep_out); + return false; + } +} + +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n) { + (void)itf; + (void)n; +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +uint32_t tud_printer_n_available(uint8_t itf) { + return tu_fifo_count(&_printer_itf[itf].rx_ff); +} + +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { + printer_interface_t *p_printer = &_printer_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { + return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); +} + +void tud_printer_n_read_flush(uint8_t itf) { + printer_interface_t *p_printer = &_printer_itf[itf]; + tu_fifo_clear(&p_printer->rx_ff); + _prep_out_transaction(itf); +} + + +//--------------------------------------------------------------------+ +// USBD-CLASS API +//--------------------------------------------------------------------+ +void printerd_init(void) { + tu_memclr(_printer_itf, sizeof(_printer_itf)); + tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); + tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); + osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); + TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); + tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); + #endif + } +} + +bool printerd_deinit(void) { + #if OSAL_MUTEX_REQUIRED + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + + if (mutex_rd) { + osal_mutex_delete(mutex_rd); + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); + } + + if (mutex_wr) { + osal_mutex_delete(mutex_wr); + tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); + } + } + #endif + + return true; +} + +void printerd_reset(uint8_t rhport) { + (void)rhport; + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_memclr(p_printer, sizeof(p_printer)); + if (!_printer_fifo_cfg.rx_persistent) { + tu_fifo_clear(&p_printer->rx_ff); + } + if (!_printer_fifo_cfg.tx_persistent) { + tu_fifo_clear(&p_printer->tx_ff); + } + // tu_fifo_set_overwritable(&p_printer->rx_ff, true); + tu_fifo_set_overwritable(&p_printer->tx_ff, true); + } +} + +uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); + + // Identify available interface to open + printer_interface_t *p_printer; + uint8_t printer_id; + for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { + p_printer = &_printer_itf[printer_id]; + if (p_printer->ep_out == 0) { + break; + } + } + TU_ASSERT(printer_id < CFG_TUD_PRINTER); + + //------------- Interface -------------// + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + //------------- Endpoints -------------// + TU_ASSERT(itf_desc->bNumEndpoints == 2); + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + p_printer->itf_num = 2; + const uint8_t *p_desc = tu_desc_next(itf_desc); + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); + + _prep_out_transaction(printer_id); + + return drv_len; +} + +bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { + //------------- STD Request -------------// + if (stage != CONTROL_STAGE_SETUP) { + return true; + } + } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { + switch (request->bRequest) { + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + case PRINTER_REQ_CONTROL_GET_DEVICE_ID: + if (stage == CONTROL_STAGE_SETUP) { + const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" + "MODEL:LaserBeam 9;" + "COMMAND SET:PS;" + "COMMENT:Anything you like;" + "ACTIVE COMMAND SET:PS;"; + char buffer[256]; + strcpy(buffer + 2, deviceId); + buffer[0] = 0x00; + buffer[1] = strlen(deviceId); + return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + } + break; + case PRINTER_REQ_CONTROL_GET_PORT_STATUS: + if (stage == CONTROL_STAGE_SETUP) { + static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); + } + break; + case PRINTER_REQ_CONTROL_SOFT_RESET: + if (stage == CONTROL_STAGE_SETUP) { + return false; // what to do ? + } + break; + default: + return false; + } + } else { + return false; + } + return true; +} + +bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + uint8_t itf; + printer_interface_t *p_printer; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { + p_printer = &_printer_itf[itf]; + if (ep_addr == p_printer->ep_out) { + break; + } + } + TU_ASSERT(itf < CFG_TUD_PRINTER); + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Received new data + if (ep_addr == p_printer->ep_out) { + tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); + // invoke receive callback (if there is still data) + if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + tud_printer_rx_cb(itf, xferred_bytes); + } + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + return true; +} + +#endif diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h new file mode 100644 index 000000000..8c21e39ce --- /dev/null +++ b/src/class/printer/printer_device.h @@ -0,0 +1,79 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_PRINTER_DEVICE_H_ +#define TUSB_PRINTER_DEVICE_H_ + +#include "printer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct TU_ATTR_PACKED { + uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect + uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect +} tud_printer_configure_fifo_t; + +//--------------------------------------------------------------------+ +// Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1 +//--------------------------------------------------------------------+ + +// Get the number of bytes available for reading +uint32_t tud_printer_n_available(uint8_t itf); + +// Read received bytes +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize); + +// Clear the received FIFO +void tud_printer_n_read_flush(uint8_t itf); + +// Get a byte from FIFO without removing it +bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void printerd_init(void); +bool printerd_deinit(void); +void printerd_reset(uint8_t rhport); +uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len); +bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request); +bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/device/usbd.c b/src/device/usbd.c index 1e21c667a..32b3a3ee2 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -334,6 +334,19 @@ static const usbd_class_driver_t _usbd_driver[] = { .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) }; diff --git a/src/device/usbd.h b/src/device/usbd.h index bd5a3c395..3b296feea 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -312,6 +312,21 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 +//--------------------------------------------------------------------+ +// Printer Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints + +#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + //--------------------------------------------------------------------+ // HID Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tinyusb.mk b/src/tinyusb.mk index 8f9d52de9..e7d1c0b5b 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -15,6 +15,7 @@ TINYUSB_SRC_C += \ src/class/mtp/mtp_device.c \ src/class/net/ecm_rndis_device.c \ src/class/net/ncm_device.c \ + src/class/printer/printer_device.c \ src/class/usbtmc/usbtmc_device.c \ src/class/video/video_device.c \ src/class/vendor/vendor_device.c \ diff --git a/src/tusb.h b/src/tusb.h index 62b3b9783..256a239e7 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -92,6 +92,10 @@ #include "class/mtp/mtp_device.h" #endif + #if CFG_TUD_PRINTER + #include "class/printer/printer_device.h" + #endif + #if CFG_TUD_AUDIO #include "class/audio/audio_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 64fe899db..80cbeab36 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -614,6 +614,10 @@ #define CFG_TUD_NCM 0 #endif +#ifndef CFG_TUD_PRINTER + #define CFG_TUD_PRINTER 0 +#endif + #ifndef CFG_TUD_EDPT_DEDICATED_HWFIFO #define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 #endif diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf index caddda826..035e40b94 100644 --- a/tools/iar_template.ipcf +++ b/tools/iar_template.ipcf @@ -69,6 +69,11 @@ $TUSB_DIR$/src/class/net/ncm.h $TUSB_DIR$/src/class/net/net_device.h + + $TUSB_DIR$/src/class/printer/printer_device.c + $TUSB_DIR$/src/class/printer/printer.h + $TUSB_DIR$/src/class/printer/printer_device.h + $TUSB_DIR$/src/class/usbtmc/usbtmc_device.c $TUSB_DIR$/src/class/usbtmc/usbtmc.h -- cgit v1.3.1 From 473c2c7313f8d9d66951ae2a50b04eb5ccbf05b0 Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Mon, 5 Jan 2026 19:11:34 +0100 Subject: Add missing license to printer device class header --- src/class/printer/printer.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/class') diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index 4cd1d62fc..c637717b1 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -1,3 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + #include #include "tusb_option.h" -- cgit v1.3.1 From 158fe86b55ee4704b57ad07a95ceb546b82534af Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Mon, 5 Jan 2026 19:13:23 +0100 Subject: Fix content of printer device class header (.h) file I apparently made a mistake when copying/renaming this file, file content was that of printer_device.c. --- src/class/printer/printer.h | 289 +++----------------------------------------- 1 file changed, 17 insertions(+), 272 deletions(-) (limited to 'src/class') diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index c637717b1..27e95b997 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -24,280 +24,25 @@ * This file is part of the TinyUSB stack. */ -#include -#include "tusb_option.h" +#ifndef _TUSB_PRINTER_H_ +#define _TUSB_PRINTER_H_ -#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) +#include "common/tusb_common.h" - #include "device/usbd.h" - #include "device/usbd_pvt.h" - -// #include "bsp/board_api.h" - - #include "printer_device.h" - #include "printer.h" - - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -typedef struct { - uint8_t itf_num; - uint8_t ep_in; - uint8_t ep_out; - /*------------- From this point, data is not cleared by bus reset -------------*/ - - // FIFO - tu_fifo_t rx_ff; - tu_fifo_t tx_ff; - - uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; - uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; - - OSAL_MUTEX_DEF(rx_ff_mutex); - OSAL_MUTEX_DEF(tx_ff_mutex); -} printer_interface_t; - - #define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, wanted_char) - -typedef struct { - TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); - TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); -} printer_epbuf_t; - -static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; -CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; - - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ - -static tud_printer_configure_fifo_t _printer_fifo_cfg; - -static bool _prep_out_transaction(uint8_t itf) { - const uint8_t rhport = 0; - printer_interface_t *p_printer = &_printer_itf[itf]; - printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; - - // Skip if usb is not ready yet - TU_VERIFY(tud_ready() && p_printer->ep_out); - - uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); - - // Prepare for incoming data but only allow what we can store in the ring buffer. - // TODO Actually we can still carry out the transfer, keeping count of received bytes - // and slowly move it to the FIFO when read(). - // This pre-check reduces endpoint claiming - TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); - - // claim endpoint - TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); - - // fifo can be changed before endpoint is claimed - available = tu_fifo_remaining(&p_printer->rx_ff); - - if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { - return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); - } else { - // Release endpoint since we don't make any transfer - usbd_edpt_release(rhport, p_printer->ep_out); - return false; - } -} - -//--------------------------------------------------------------------+ -// APPLICATION API -//--------------------------------------------------------------------+ - -uint32_t tud_printer_n_available(uint8_t itf) { - return tu_fifo_count(&_printer_itf[itf].rx_ff); -} - -uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { - printer_interface_t *p_printer = &_printer_itf[itf]; - uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); - _prep_out_transaction(itf); - return num_read; -} - -bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { - return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); -} - -void tud_printer_n_read_flush(uint8_t itf) { - printer_interface_t *p_printer = &_printer_itf[itf]; - tu_fifo_clear(&p_printer->rx_ff); - _prep_out_transaction(itf); -} - - -//--------------------------------------------------------------------+ -// USBD PRINTER DRIVER API -//--------------------------------------------------------------------+ - -void printer_init(void) { - tu_memclr(_printer_itf, sizeof(_printer_itf)); - tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); - - for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - - tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); - tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); - - #if OSAL_MUTEX_REQUIRED - osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); - osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); - TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); - - tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); - tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); - #endif - } -} - -bool printer_deinit(void) { - #if OSAL_MUTEX_REQUIRED - for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; - osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; - - if (mutex_rd) { - osal_mutex_delete(mutex_rd); - tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); - } - - if (mutex_wr) { - osal_mutex_delete(mutex_wr); - tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); - } - } - #endif - - return true; -} - -void printer_reset(uint8_t rhport) { - (void)rhport; - - for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - - tu_memclr(p_printer, sizeof(p_printer)); - if (!_printer_fifo_cfg.rx_persistent) { - tu_fifo_clear(&p_printer->rx_ff); - } - if (!_printer_fifo_cfg.tx_persistent) { - tu_fifo_clear(&p_printer->tx_ff); - } - // tu_fifo_set_overwritable(&p_printer->rx_ff, true); - tu_fifo_set_overwritable(&p_printer->tx_ff, true); - } -} - -uint16_t printer_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { - TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); - - // Identify available interface to open - printer_interface_t *p_printer; - uint8_t printer_id; - for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { - p_printer = &_printer_itf[printer_id]; - if (p_printer->ep_out == 0) { - break; - } - } - TU_ASSERT(printer_id < CFG_TUD_PRINTER); - - //------------- Interface -------------// - uint16_t drv_len = sizeof(tusb_desc_interface_t); - - //------------- Endpoints -------------// - TU_ASSERT(itf_desc->bNumEndpoints == 2); - drv_len += 2 * sizeof(tusb_desc_endpoint_t); - p_printer->itf_num = 2; - const uint8_t *p_desc = tu_desc_next(itf_desc); - TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); - - _prep_out_transaction(printer_id); - - return drv_len; -} - -bool printer_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { - TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); - - if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { - //------------- STD Request -------------// - if (stage != CONTROL_STAGE_SETUP) { - return true; - } - } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - switch (request->bRequest) { - // https://www.usb.org/sites/default/files/usbprint11a021811.pdf - case PRINTER_REQ_CONTROL_GET_DEVICE_ID: - if (stage == CONTROL_STAGE_SETUP) { - const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" - "MODEL:LaserBeam 9;" - "COMMAND SET:PS;" - "COMMENT:Anything you like;" - "ACTIVE COMMAND SET:PS;"; - char buffer[256]; - strcpy(buffer + 2, deviceId); - buffer[0] = 0x00; - buffer[1] = strlen(deviceId); - return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); - } - break; - case PRINTER_REQ_CONTROL_GET_PORT_STATUS: - if (stage == CONTROL_STAGE_SETUP) { - static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError - return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); - } - break; - case PRINTER_REQ_CONTROL_SOFT_RESET: - if (stage == CONTROL_STAGE_SETUP) { - return false; // what to do ? - } - break; - default: - return false; - } - } else { - return false; - } - return true; -} - -bool printer_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - uint8_t itf; - printer_interface_t *p_printer; - - // Identify which interface to use - for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { - p_printer = &_printer_itf[itf]; - if (ep_addr == p_printer->ep_out) { - break; - } - } - TU_ASSERT(itf < CFG_TUD_PRINTER); - printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; - - // Received new data - if (ep_addr == p_printer->ep_out) { - tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); - // invoke receive callback (if there is still data) - if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { - tud_printer_rx_cb(itf, xferred_bytes); - } - // prepare for OUT transaction - _prep_out_transaction(itf); - } +#ifdef __cplusplus + extern "C" { +#endif - return true; -} +/// Printer Class Specific Control Request +typedef enum +{ + PRINTER_REQ_CONTROL_GET_DEVICE_ID = 0x01, ///< Get device ID + PRINTER_REQ_CONTROL_GET_PORT_STATUS = 0x02, ///< Get port status + PRINTER_REQ_CONTROL_SOFT_RESET = 0x03, ///< Soft reset +}printer_request_enum_t; +#ifdef __cplusplus + } #endif + +#endif /* _TUSB_PRINTER_H__ */ -- cgit v1.3.1 From 0e5241aa83c52792959ad8a2ef0535a3759530a7 Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Wed, 7 Jan 2026 17:04:48 +0100 Subject: Adjust printer device class to 0.20.0 and fix compilation warnings --- examples/device/printer_to_hid/src/main.c | 73 +++--- .../device/printer_to_hid/src/usb_descriptors.c | 278 +++++++-------------- .../device/printer_to_hid/src/usb_descriptors.h | 48 +++- hw/bsp/rp2040/family.cmake | 1 + src/class/printer/printer.h | 9 +- src/class/printer/printer_device.c | 13 +- src/device/usbd.h | 13 + src/tusb.h | 4 + 8 files changed, 208 insertions(+), 231 deletions(-) (limited to 'src/class') diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c index f5012478c..46203b9db 100644 --- a/examples/device/printer_to_hid/src/main.c +++ b/examples/device/printer_to_hid/src/main.c @@ -39,7 +39,7 @@ // usb interface pointer uint8_t printer_itf = 0; // pendings bytes in usb endpoint buffer ; must process these bytes -uint8_t pending_bytes_on_usb_ep = 0; +size_t pending_bytes_on_usb_ep = 0; // -------------------------------------------------------------------+ @@ -72,7 +72,7 @@ uint8_t next_keycode_is_release = false; // next_keycode and place it in the HID report, then set next_is_null // such that the key is released by the next report. This seem to help // stroking the same key twice when the character is repeated in the data. -void hid_tx_task(void) { +static void hid_tx_task(void) { // Poll every 10ms const uint32_t interval_ms = 10; static uint32_t start_ms = 0; @@ -104,28 +104,30 @@ void hid_tx_task(void) { // overwrite local data that is not processed yet, so we use data_buffer as a fifo. // We do not have to take care of reading correctly from the endpoint buffer, as all // is done well by tud_printer_n_read(). -void printer_rx_task(void) { - if (pending_bytes_on_usb_ep > 0) { - size_t len1 = data_available; - size_t len2 = 0; - if (len1 < 0) { - len1 = 0; - } - if (data_rx_offset + len1 > sizeof(data_buffer)) { - len2 = len1 - (sizeof(data_buffer) - data_rx_offset); - len1 = sizeof(data_buffer) - data_rx_offset; - } - uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); - if (len2 > 0) { - count += tud_printer_n_read(printer_itf, data_buffer, len2); - } +static void printer_rx_task(void) { - if (count > 0) { - data_available -= count; - pending_bytes_on_usb_ep -= count; - data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); - } + if (pending_bytes_on_usb_ep == 0) { + return; + } + + size_t len1 = data_available; + size_t len2 = 0; + if (data_rx_offset + len1 > sizeof(data_buffer)) { + len2 = len1 - (sizeof(data_buffer) - data_rx_offset); + len1 = sizeof(data_buffer) - data_rx_offset; + } + uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); + if (len2 > 0) { + count += tud_printer_n_read(printer_itf, data_buffer, len2); } + + if (count == 0) { + return; + } + + data_available -= count; + pending_bytes_on_usb_ep -= count; + data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); } // The HID keycodes are not binary mapped like UTF8 codes. If we want to send the @@ -134,7 +136,7 @@ void printer_rx_task(void) { // hosts expecting hid data from a QWERTY keyboard. Also note that only a-zA-Z0-9 // characters are converted, for simplicity of the example. Other characters are // converted to spaces. -void translation_task(void) { +static void translation_task(void) { if (data_tx_offset != data_rx_offset || data_available == 0) { // If data_tx_offset and data_rx_offset have different values, then we // can proceed: translate, prepare for TX, and advance data_tx_offset. @@ -177,18 +179,22 @@ void translation_task(void) { } int main(void) { - board_init(); - tud_init(BOARD_TUD_RHPORT); // init device stack on configured roothub port - if (board_init_after_tusb) { - board_init_after_tusb(); - } + // init device and host stack on configured roothub port + tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO}; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + board_init_after_tusb(); while (1) { tud_task(); // tinyusb device task printer_rx_task(); // read data sent by host on our printer interface translation_task(); // translate printer's UTF8 to HID keycodes hid_tx_task(); // send data to host with our HID interface + if (pending_bytes_on_usb_ep > 0) { + board_led_on(); + } else { + board_led_off(); + } } } @@ -197,7 +203,6 @@ int main(void) { // Printer callbacks //--------------------------------------------------------------------+ -// Data was received on endpoint buffer void tud_printer_rx_cb(uint8_t itf, size_t n) { printer_itf = itf; // get interface from which to read endpoint buffer pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer @@ -210,10 +215,20 @@ void tud_printer_rx_cb(uint8_t itf, size_t n) { uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; return 0; } void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)bufsize; return; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c index 161bbb7c5..7d529e724 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.c +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2026 Ha Thach (tinyusb.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,223 +25,119 @@ #include "bsp/board_api.h" #include "tusb.h" -#include "usb_descriptors.h" - -/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. - * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. - * - * Auto ProductID layout's Bitmap: - * [MSB] HID | MSC | CDC [LSB] - */ -#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0) -#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \ - PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) ) - -#define USB_VID 0xCafe -#define USB_BCD 0x0200 - -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ -static tusb_desc_device_t const desc_device = -{ - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 -}; - -// Invoked when received GET DEVICE DESCRIPTOR -// Application return pointer to descriptor -uint8_t const * tud_descriptor_device_cb(void) -{ - return (uint8_t const *) &desc_device; -} -//--------------------------------------------------------------------+ -// HID Report Descriptor -//--------------------------------------------------------------------+ - -uint8_t const desc_hid_report[] = -{ - TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )) -}; +#include "usb_descriptors.h" -// Invoked when received GET HID REPORT DESCRIPTOR -// Application return pointer to descriptor -// Descriptor contents must exist long enough for transfer to complete -uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) -{ - (void) instance; - return desc_hid_report; -} //--------------------------------------------------------------------+ -// Configuration Descriptor +// Report definitions //--------------------------------------------------------------------+ -enum -{ - ITF_NUM_HID, - ITF_NUM_PRINTER, - ITF_NUM_TOTAL +// Values of the string descriptors. Order must match the order defined by STRING_DESCRIPTOR_INDICES. +const char *STRING_DESCRIPTOR_VALUES[] = { + (const char[]){0x09, 0x04}, // 0: Supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serial number, will use unique ID from the Pi Pico board hardware + "Config1", // 4: Configuration + "Hid1", // 5: HID interface + "Print1", // 6: Printer interface }; -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) +uint8_t HID_REPORT_DESCRIPTOR[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD))}; -// HID interface endpoints -#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 -// Printer interface endpoints -#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 -#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 - -uint8_t const desc_configuration[] = -{ +uint8_t CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // HID: // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPADDR_HID, CFG_TUD_HID_EP_BUFSIZE, 5), + TUD_HID_DESCRIPTOR(ITF_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(HID_REPORT_DESCRIPTOR), EPADDR_HID, + CFG_TUD_HID_EP_BUFSIZE, 5), // Printer: // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size - TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE) + TUD_PRINTER_DESCRIPTOR(ITF_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE)}; + +static const tusb_desc_device_t DEVICE_DESCRIPTOR = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, // Define class at interface level + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + + .bNumConfigurations = 0x01 }; -#if TUD_OPT_HIGH_SPEED -// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration - -// other speed configuration -static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; - -// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed -static tusb_desc_device_qualifier_t const desc_device_qualifier = -{ - .bLength = sizeof(tusb_desc_device_qualifier_t), - .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .bNumConfigurations = 0x01, - .bReserved = 0x00 -}; +//--------------------------------------------------------------------+ +// TinyUSB callbacks (descriptor requests) +//--------------------------------------------------------------------+ -// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. -// device_qualifier descriptor describes information about a high-speed capable device that would -// change if the device were operating at the other speed. If not highspeed capable stall this request. -uint8_t const* tud_descriptor_device_qualifier_cb(void) -{ - return (uint8_t const*) &desc_device_qualifier; +// TinyUSB GET HID REPORT DESCRIPTOR callback. +const uint8_t *tud_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + return HID_REPORT_DESCRIPTOR; } -// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa -uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) -{ - (void) index; // for multiple configurations - - // other speed config is basically configuration with type = OTHER_SPEED_CONFIG - memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN); - desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; - - // this example use the same configuration for both high and full speed mode - return desc_other_speed_config; +// TinyUSB GET CONFIGURATION DESCRIPTOR callback. +const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR; } -#endif // highspeed - -// Invoked when received GET CONFIGURATION DESCRIPTOR -// Application return pointer to descriptor -// Descriptor contents must exist long enough for transfer to complete -uint8_t const * tud_descriptor_configuration_cb(uint8_t index) -{ - (void) index; // for multiple configurations - - // This example use the same configuration for both high and full speed mode - return desc_configuration; +// TinyUSB GET DEVICE DESCRIPTOR callback. +const uint8_t *tud_descriptor_device_cb(void) { + return (const uint8_t *)&DEVICE_DESCRIPTOR; } -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -// String Descriptor Index -enum { - STRID_LANGID = 0, - STRID_MANUFACTURER, - STRID_PRODUCT, - STRID_SERIAL, -}; - -// array of pointer to string descriptors -static char const *string_desc_arr[] = -{ - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "TinyUSB", // 1: Manufacturer - "TinyUSB Device", // 2: Product - NULL, // 3: Serials will use unique ID if possible -}; - -static uint16_t _desc_str[32 + 1]; - -// Invoked when received GET STRING DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void) langid; - size_t chr_count; - - switch ( index ) { - case STRID_LANGID: - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - break; - - case STRID_SERIAL: - chr_count = board_usb_get_serial(_desc_str + 1, 32); - break; - - default: - // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. - // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - - if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; - - const char *str = string_desc_arr[index]; - - // Cap at max char - chr_count = strlen(str); - size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; - - // Convert ASCII string into UTF-16 - for ( size_t i = 0; i < chr_count; i++ ) { - _desc_str[1 + i] = str[i]; - } - break; +// Storage buffer array for string descriptor to be sent to host. +static uint16_t string_descriptor_buffer[STRING_DESCRIPTOR_MAX_LENGTH + 1]; + +// TinyUSB GET STRING DESCRIPTOR callback. +const uint16_t *tud_descriptor_string_cb(uint8_t index, [[maybe_unused]] uint16_t langid) { + size_t utf16_string_length; + + if (index == LANGID) { + // langid is not a string as in a series of characters: language ID code is binary, 2 bytes + memcpy(string_descriptor_buffer + 1, STRING_DESCRIPTOR_VALUES[LANGID], 2); + utf16_string_length = 1; // 2 bytes = 1 UTF16 word + + } else if (index == STR_SERIAL) { + // serialnumber is generated from pi pico: see note in STRING_DESCRIPTOR_VALUES definition + utf16_string_length = board_usb_get_serial(string_descriptor_buffer + 1, STRING_DESCRIPTOR_MAX_LENGTH); + + } else if (index < STRING_COUNT) { + // Get adequate descriptor string + const char *str = STRING_DESCRIPTOR_VALUES[index]; + utf16_string_length = strlen(str); + if (utf16_string_length > STRING_DESCRIPTOR_MAX_LENGTH) { + utf16_string_length = STRING_DESCRIPTOR_MAX_LENGTH; + } + // Convert ASCII string from memory (char*) to UTF16 (for buffer), + // store in buffer with 1 UTF16 word offset (2 bytes, for buffer header) + for (size_t i = 0; i < utf16_string_length; i++) { + string_descriptor_buffer[i + 1] = str[i]; + } + + } else { + return NULL; } - // first byte is length (including header), second byte is string type - _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + // Set buffer header: + // byte 1 - buffer length in bytes (including header) + // byte 0 - string descriptor type (0x03). + string_descriptor_buffer[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * utf16_string_length + 2)); - return _desc_str; + return string_descriptor_buffer; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h index 53c4e1fbd..0d9793a9c 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.h +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -25,6 +25,52 @@ #ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ -REPORT_ID_KEYBOARD = 1 +#include "bsp/board_api.h" +#include "tusb.h" + +#define USB_VID 0xCafe // unassigned vendor id +#define USB_PID 0x4004 // random product id +#define USB_BCD 0x0200 // binary coded version: 2.00 + +// Configuration +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + +// HID interface endpoints +#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 +// Printer interface endpoints +#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 +#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 + +// HID report ID +#define REPORT_ID_KEYBOARD 1 + +// The maximum length of the string that will be sent to the host via the STRING DESCRIPTOR. Note that the +// string descriptor itself is two bytes wider than the string. +#define STRING_DESCRIPTOR_MAX_LENGTH 32 + +//--------------------------------------------------------------------+ +// Configuration, interface, endpoint descriptors +//--------------------------------------------------------------------+ + +enum { + ITF_HID, + ITF_PRINTER, + ITF_COUNT, +}; + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +enum { + LANGID = 0, + STR_MANUFACTURER, + STR_PRODUCT, + STR_SERIAL, + STR_CONFIGURATION, + STR_HID_INTERFACE, + STR_PRINTER_INTERFACE, + STRING_COUNT, +}; #endif /* USB_DESCRIPTORS_H_ */ diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 1602e35eb..55ce6e1f8 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -98,6 +98,7 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/class/mtp/mtp_device.c ${TOP}/src/class/net/ecm_rndis_device.c ${TOP}/src/class/net/ncm_device.c + ${TOP}/src/class/printer/printer_device.c ${TOP}/src/class/usbtmc/usbtmc_device.c ${TOP}/src/class/vendor/vendor_device.c ${TOP}/src/class/video/video_device.c diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index 27e95b997..c9ed3cebc 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -30,19 +30,18 @@ #include "common/tusb_common.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif /// Printer Class Specific Control Request -typedef enum -{ +typedef enum { PRINTER_REQ_CONTROL_GET_DEVICE_ID = 0x01, ///< Get device ID PRINTER_REQ_CONTROL_GET_PORT_STATUS = 0x02, ///< Get port status PRINTER_REQ_CONTROL_SOFT_RESET = 0x03, ///< Soft reset -}printer_request_enum_t; +} printer_request_enum_t; #ifdef __cplusplus - } +} #endif #endif /* _TUSB_PRINTER_H__ */ diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index efeae100f..76151949b 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -96,7 +96,7 @@ static bool _prep_out_transaction(uint8_t itf) { available = tu_fifo_remaining(&p_printer->rx_ff); if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { - return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE, false); } else { // Release endpoint since we don't make any transfer usbd_edpt_release(rhport, p_printer->ep_out); @@ -189,7 +189,7 @@ void printerd_reset(uint8_t rhport) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; - tu_memclr(p_printer, sizeof(p_printer)); + tu_memclr(p_printer, sizeof(&p_printer)); if (!_printer_fifo_cfg.rx_persistent) { tu_fifo_clear(&p_printer->rx_ff); } @@ -202,6 +202,7 @@ void printerd_reset(uint8_t rhport) { } uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + (void)max_len; TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); // Identify available interface to open @@ -257,13 +258,13 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ break; case PRINTER_REQ_CONTROL_GET_PORT_STATUS: if (stage == CONTROL_STAGE_SETUP) { - static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + static uint8_t port_status = 0b00011000; // paper not empty, selected, no error return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); } break; case PRINTER_REQ_CONTROL_SOFT_RESET: if (stage == CONTROL_STAGE_SETUP) { - return false; // what to do ? + return false; // TODO: reset buffers, reset Bulk In and Out endpoints, clear stall conditions } break; default: @@ -276,6 +277,8 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ } bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void)rhport; + (void)result; uint8_t itf; printer_interface_t *p_printer; @@ -293,7 +296,7 @@ bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uin if (ep_addr == p_printer->ep_out) { tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); // invoke receive callback (if there is still data) - if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + if (!tu_fifo_empty(&p_printer->rx_ff)) { tud_printer_rx_cb(itf, xferred_bytes); } // prepare for OUT transaction diff --git a/src/device/usbd.h b/src/device/usbd.h index 3b296feea..d473aea2a 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -292,6 +292,19 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ /* Endpoint In */\ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 +//--------------------------------------------------------------------+ +// Printer Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints + +#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 //--------------------------------------------------------------------+ // MTP Descriptor Templates diff --git a/src/tusb.h b/src/tusb.h index 256a239e7..1f4d4b73a 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -88,6 +88,10 @@ #include "class/msc/msc_device.h" #endif + #if CFG_TUD_PRINTER + #include "class/printer/printer_device.h" + #endif + #if CFG_TUD_MTP #include "class/mtp/mtp_device.h" #endif -- cgit v1.3.1 From dbfbfcf48b45ccc1e6534c454f7b2807b9ae88bd Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Wed, 14 Jan 2026 19:58:47 +0100 Subject: update printer_device.c accounting for upstream changes --- src/class/printer/printer_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/class') diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index 76151949b..aaea4c988 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -147,8 +147,8 @@ void printerd_init(void) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; - tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); - tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); + tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), false); + tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), true); #if OSAL_MUTEX_REQUIRED osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); -- cgit v1.3.1 From 697f6b313e82cb59d034664d2c8f258f087a1269 Mon Sep 17 00:00:00 2001 From: Zixun LI Date: Mon, 26 Jan 2026 16:11:24 +0100 Subject: device/msc: skip command stage if EP out is stalled Signed-off-by: Zixun LI --- src/class/msc/msc_device.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/class') diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 15bfafc35..be40c37e7 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -121,7 +121,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool send_csw(mscd_interface_t* p_msc) { TU_ATTR_ALWAYS_INLINE static inline bool prepare_cbw(mscd_interface_t* p_msc) { uint8_t rhport = p_msc->rhport; p_msc->stage = MSC_STAGE_CMD; - return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t), false); + // Skip command stage until Clear Stall request if endpoint is stalled + if (!usbd_edpt_stalled(rhport, p_msc->ep_out)) { + return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t), false); + } else { + return true; + } } static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) { -- cgit v1.3.1 From 25de49f8f6e8590eb548d4016e030d19166b9fb3 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 7 Feb 2026 18:01:23 +0700 Subject: minor update --- src/class/mtp/mtp_device.h | 2 +- src/class/video/video_device.h | 3 +-- tools/codespell/ignore-words.txt | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/class') diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index a33f1dc08..6cce7efbb 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -18,7 +18,7 @@ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN0 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * This file is part of the TinyUSB stack. diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h index f14555e4f..2750bb2fb 100644 --- a/src/class/video/video_device.h +++ b/src/class/video/video_device.h @@ -99,8 +99,7 @@ int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, * @param[in] stm_idx Destination streaming interface index * @param[out] payload_buf Payload storage buffer (target buffer for requested data) * @param[in] payload_size Size of payload_buf (requested data size) - * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize) - * @return video_error_code_t */ + * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize) */ void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request); //--------------------------------------------------------------------+ diff --git a/tools/codespell/ignore-words.txt b/tools/codespell/ignore-words.txt index 957cbd86b..5b6e2e98b 100644 --- a/tools/codespell/ignore-words.txt +++ b/tools/codespell/ignore-words.txt @@ -1,14 +1,15 @@ -synopsys -sie -tre -thre -hsi -fro -dout -mot -te attch +busses +dout endianess +fro +hsi +inout +mot pris -busses ser +sie +synopsys +te +thre +tre -- cgit v1.3.1 From b5c84aa140773009926fce97e207075fb11682ed Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 11 Feb 2026 23:44:39 +0700 Subject: device/msc: only defer prepare_cbw() from STATUS_SENT when EP OUT stalled --- src/class/msc/msc_device.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src/class') diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index be40c37e7..3766e3a25 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -121,12 +121,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool send_csw(mscd_interface_t* p_msc) { TU_ATTR_ALWAYS_INLINE static inline bool prepare_cbw(mscd_interface_t* p_msc) { uint8_t rhport = p_msc->rhport; p_msc->stage = MSC_STAGE_CMD; - // Skip command stage until Clear Stall request if endpoint is stalled - if (!usbd_edpt_stalled(rhport, p_msc->ep_out)) { - return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t), false); - } else { - return true; - } + return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t), false); } static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) { @@ -651,7 +646,11 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } - TU_ASSERT(prepare_cbw(p_msc)); + if (!usbd_edpt_stalled(rhport, p_msc->ep_out)) { + TU_ASSERT(prepare_cbw(p_msc)); + } else { + p_msc->stage = MSC_STAGE_CMD; + } } else { // Any xfer ended here is considered unknown error, ignore it TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n"); -- cgit v1.3.1 From 135a130ba51d2e41b30f13fbd998f1db32a47470 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 11 Feb 2026 22:04:44 +0100 Subject: fix rx transfer length when high speed capable device/host working at full speed Signed-off-by: HiFiPhile --- src/class/cdc/cdc_device.c | 12 ++++++------ src/class/cdc/cdc_device.h | 2 ++ src/class/cdc/cdc_host.c | 8 ++++---- src/class/midi/midi_device.c | 8 ++++---- src/class/midi/midi_host.c | 6 +++--- src/class/vendor/vendor_device.c | 36 +++++++++++++++++++++--------------- src/class/vendor/vendor_device.h | 17 ++++++++++++++++- src/common/tusb_private.h | 7 ++++--- src/tusb.c | 7 +++---- 9 files changed, 63 insertions(+), 40 deletions(-) (limited to 'src/class') diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index e2819ae4b..eca4d0ad6 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -74,7 +74,7 @@ typedef struct { } cdcd_epbuf_t; CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC]; -#endif + #endif //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available @@ -267,14 +267,13 @@ void cdcd_init(void) { uint8_t *epin_buf = _cdcd_epbuf[i].epin; #endif - tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf, - CFG_TUD_CDC_EP_BUFSIZE); + tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf); // TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not // know if data is actually polled by terminal. This way the most current data is prioritized. // Default: is overwritable tu_edpt_stream_init(&p_cdc->tx_stream, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected, p_cdc->tx_ff_buf, - CFG_TUD_CDC_TX_BUFSIZE, epin_buf, CFG_TUD_CDC_EP_BUFSIZE); + CFG_TUD_CDC_TX_BUFSIZE, epin_buf); } } @@ -349,7 +348,7 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream; - tu_edpt_stream_open(stream_tx, rhport, desc_ep); + tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_EP_BUFSIZE); if (_cdcd_cfg.tx_persistent) { tu_edpt_stream_write_xfer(stream_tx); // flush pending data } else { @@ -358,7 +357,8 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; - tu_edpt_stream_open(stream_rx, rhport, desc_ep); + tu_edpt_stream_open(stream_rx, rhport, desc_ep, + _cdcd_cfg.rx_multiple_packet_transfer ? CFG_TUD_CDC_EP_BUFSIZE : tu_edpt_packet_size(desc_ep)); if (!_cdcd_cfg.rx_persistent) { tu_edpt_stream_clear(stream_rx); } diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 0809b578f..2d3a81f60 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -64,6 +64,7 @@ typedef struct TU_ATTR_PACKED { bool rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect bool tx_persistent : 1; // keep tx fifo data even with reset or disconnect bool tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten + bool rx_multiple_packet_transfer : 1; // allow transfer more than one packet in a single transfer, increase throughput but requires host sending ZLP at the end of transfer } tud_cdc_configure_t; TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct"); @@ -71,6 +72,7 @@ TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct"); .rx_persistent = false, \ .tx_persistent = false, \ .tx_overwritabe_if_not_connected = true, \ + .rx_multiple_packet_transfer = false, \ } // Configure CDC driver behavior diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f19c4a327..8b5667aac 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -648,10 +648,10 @@ bool cdch_init(void) { for (size_t i = 0; i < CFG_TUH_CDC; i++) { cdch_interface_t *p_cdc = &cdch_data[i]; cdch_epbuf_t *epbuf = &cdch_epbuf[i]; - TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE, - epbuf->tx, CFG_TUH_CDC_TX_EPSIZE)); + TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf, + CFG_TUH_CDC_TX_BUFSIZE, epbuf->tx)); TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, p_cdc->stream.rx_ff_buf, - CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx, CFG_TUH_CDC_RX_EPSIZE)); + CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx)); } return true; @@ -735,7 +735,7 @@ static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t co TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep)); const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx; - tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep); + tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep, desc_ep->wMaxPacketSize); tu_edpt_stream_clear(stream); desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep); diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index 023a81595..173baf53e 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -324,10 +324,10 @@ void midid_init(void) { #endif tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf, - CFG_TUD_MIDI_RX_BUFSIZE, epout_buf, CFG_TUD_MIDI_EP_BUFSIZE); + CFG_TUD_MIDI_RX_BUFSIZE, epout_buf); tu_edpt_stream_init(&p_midi->ep_stream.tx, false, true, false, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, - epin_buf, CFG_TUD_MIDI_EP_BUFSIZE); + epin_buf); } } @@ -408,11 +408,11 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint1 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx; - tu_edpt_stream_open(stream_tx, rhport, desc_ep); + tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_MIDI_EP_BUFSIZE); tu_edpt_stream_clear(stream_tx); } else { tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx; - tu_edpt_stream_open(stream_rx, rhport, desc_ep); + tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep)); tu_edpt_stream_clear(stream_rx); TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare to receive data } diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 5548a0ba8..a3d61a02f 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -121,9 +121,9 @@ bool midih_init(void) { for (int inst = 0; inst < CFG_TUH_MIDI; inst++) { midih_interface_t *p_midi_host = &_midi_host[inst]; tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false, - p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS); + p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx); tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false, - p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS); + p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx); } return true; } @@ -306,7 +306,7 @@ uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_ ep_stream = &p_midi->ep_stream.rx; } TU_ASSERT(tuh_edpt_open(dev_addr, p_ep), 0); - tu_edpt_stream_open(ep_stream, dev_addr, p_ep); + tu_edpt_stream_open(ep_stream, dev_addr, p_ep, p_ep->wMaxPacketSize); tu_edpt_stream_clear(ep_stream); break; diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index b917c8dc7..b2dd8f394 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -49,8 +49,7 @@ typedef struct { #else uint8_t ep_in; uint8_t ep_out; - uint16_t ep_in_mps; - uint16_t ep_out_mps; + uint16_t rx_xfer_len; #endif } vendord_interface_t; @@ -72,6 +71,8 @@ typedef struct { CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; #endif +static tud_vendor_configure_t _vendord_cfg = TUD_VENDOR_CONFIGURE_DEFAULT(); + //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ @@ -89,6 +90,12 @@ TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) { //-------------------------------------------------------------------- // Application API //-------------------------------------------------------------------- +bool tud_vendor_configure(const tud_vendor_configure_t* driver_cfg) { + TU_VERIFY(driver_cfg != NULL); + _vendord_cfg = *driver_cfg; + return true; +} + bool tud_vendor_n_mounted(uint8_t idx) { TU_VERIFY(idx < CFG_TUD_VENDOR); vendord_interface_t *p_itf = &_vendord_itf[idx]; @@ -128,9 +135,9 @@ void tud_vendor_n_read_flush(uint8_t idx) { tu_edpt_stream_clear(&p_itf->rx_stream); tu_edpt_stream_read_xfer(&p_itf->rx_stream); } -#endif + #endif -#if CFG_TUD_VENDOR_RX_MANUAL_XFER + #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]; @@ -141,7 +148,7 @@ bool tud_vendor_n_read_xfer(uint8_t idx) { #else // Non-FIFO mode TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_out)); - return usbd_edpt_xfer(p_itf->rhport, p_itf->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false); + return usbd_edpt_xfer(p_itf->rhport, p_itf->ep_out, _vendord_epbuf[idx].epout, p_itf->rx_xfer_len, false); #endif } #endif @@ -215,12 +222,10 @@ void vendord_init(void) { #endif uint8_t *rx_ff_buf = p_itf->rx_ff_buf; - tu_edpt_stream_init(&p_itf->rx_stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf, - CFG_TUD_VENDOR_EPSIZE); + tu_edpt_stream_init(&p_itf->rx_stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf); uint8_t *tx_ff_buf = p_itf->tx_ff_buf; - tu_edpt_stream_init(&p_itf->tx_stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf, - CFG_TUD_VENDOR_EPSIZE); + tu_edpt_stream_init(&p_itf->tx_stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf); } #endif } @@ -302,30 +307,31 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc; TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); + uint16_t rx_xfer_len = _vendord_cfg.rx_multiple_packet_transfer ? CFG_TUD_VENDOR_EPSIZE : tu_edpt_packet_size(desc_ep); + #if CFG_TUD_VENDOR_TXRX_BUFFERED // open endpoint stream if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *tx_stream = &p_vendor->tx_stream; - tu_edpt_stream_open(tx_stream, rhport, desc_ep); + tu_edpt_stream_open(tx_stream, rhport, desc_ep, CFG_TUD_VENDOR_EPSIZE); tu_edpt_stream_write_xfer(tx_stream); // flush pending data } else { tu_edpt_stream_t *rx_stream = &p_vendor->rx_stream; - tu_edpt_stream_open(rx_stream, rhport, desc_ep); + tu_edpt_stream_open(rx_stream, rhport, desc_ep, rx_xfer_len); #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0 TU_ASSERT(tu_edpt_stream_read_xfer(rx_stream) > 0, 0); // prepare for incoming data #endif } #else + p_vendor->rx_xfer_len = rx_xfer_len; // Non-FIFO mode: store endpoint info if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { p_vendor->ep_in = desc_ep->bEndpointAddress; - p_vendor->ep_in_mps = tu_edpt_packet_size(desc_ep); } else { p_vendor->ep_out = desc_ep->bEndpointAddress; - p_vendor->ep_out_mps = tu_edpt_packet_size(desc_ep); #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0 // Prepare for incoming data - TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, rx_xfer_len, false), 0); #endif } #endif @@ -367,7 +373,7 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint // Non-FIFO mode: invoke callback with buffer tud_vendor_rx_cb(idx, _vendord_epbuf[idx].epout, xferred_bytes); #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0 - usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false); + usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, p_vendor->rx_xfer_len, false); #endif } else if (ep_addr == p_vendor->ep_in) { // Send complete diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 101765bb1..594da19cb 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -37,7 +37,7 @@ extern "C" { // Configuration //--------------------------------------------------------------------+ #ifndef CFG_TUD_VENDOR_EPSIZE - #define CFG_TUD_VENDOR_EPSIZE 64 + #define CFG_TUD_VENDOR_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #endif // RX FIFO can be disabled by setting this value to 0 @@ -62,6 +62,21 @@ extern "C" { #define CFG_TUD_VENDOR_RX_MANUAL_XFER 0 #endif +//--------------------------------------------------------------------+ +// Driver Configuration +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED { + bool rx_multiple_packet_transfer : 1; // allow transfer more than one packet in a single transfer, increase throughput but requires host sending ZLP at the end of transfer +} tud_vendor_configure_t; +TU_VERIFY_STATIC(sizeof(tud_vendor_configure_t) == 1, "size is not correct"); + +#define TUD_VENDOR_CONFIGURE_DEFAULT() { \ + .rx_multiple_packet_transfer = false, \ +} + +// Configure CDC driver behavior +bool tud_vendor_configure(const tud_vendor_configure_t* driver_cfg); + //--------------------------------------------------------------------+ // Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1 //--------------------------------------------------------------------+ diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 43ce7a1df..7795d7122 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -62,7 +62,7 @@ typedef struct { uint8_t ep_addr; uint16_t mps; - uint16_t ep_bufsize; + uint16_t xfer_len; uint8_t *ep_buf; // set to NULL to use xfer_fifo when CFG_TUD_EDPT_DEDICATED_HWFIFO = 1 tu_fifo_t ff; @@ -101,7 +101,7 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); // Init an endpoint stream bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool overwritable, void *ff_buf, - uint16_t ff_bufsize, uint8_t *ep_buf, uint16_t ep_bufsize); + uint16_t ff_bufsize, uint8_t *ep_buf); // Deinit an endpoint stream TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_deinit(tu_edpt_stream_t *s) { @@ -118,10 +118,11 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_deinit(tu_edpt_stream_t // Open an endpoint stream TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_open(tu_edpt_stream_t *s, uint8_t hwid, - const tusb_desc_endpoint_t *desc_ep) { + const tusb_desc_endpoint_t *desc_ep, uint16_t xfer_len) { s->hwid = hwid; s->ep_addr = desc_ep->bEndpointAddress; s->mps = tu_edpt_packet_size(desc_ep); + s->xfer_len = xfer_len; } TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_is_opened(const tu_edpt_stream_t *s) { diff --git a/src/tusb.c b/src/tusb.c index 6075e9db4..27864cb51 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -306,7 +306,7 @@ bool tu_bind_driver_to_ep_itf(uint8_t driver_id, uint8_t ep2drv[][2], uint8_t it //--------------------------------------------------------------------+ bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool overwritable, void *ff_buf, - uint16_t ff_bufsize, uint8_t *ep_buf, uint16_t ep_bufsize) { + uint16_t ff_bufsize, uint8_t *ep_buf) { (void) is_tx; if (ff_buf == NULL || ff_bufsize == 0) { @@ -324,7 +324,6 @@ bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool ove #endif s->ep_buf = ep_buf; - s->ep_bufsize = ep_bufsize; return true; } @@ -394,7 +393,7 @@ uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t *s) { if (s->ep_buf == NULL) { count = tu_fifo_count(&s->ff); // re-get count since fifo can be changed } else { - count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + count = tu_fifo_read_n(&s->ff, s->ep_buf, s->xfer_len); } if (count > 0) { @@ -441,7 +440,7 @@ uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t *s) { if (available >= s->mps) { // multiple of packet size limit by ep bufsize uint16_t count = (uint16_t) (available & ~(s->mps - 1)); - count = tu_min16(count, s->ep_bufsize); + count = tu_min16(count, s->xfer_len); TU_ASSERT(stream_xfer(s, count), 0); return count; } else { -- cgit v1.3.1 From 5efa72cd57325615efae6a26ff8c9fe8a9c7b3b5 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 11 Feb 2026 22:24:34 +0100 Subject: fix ep size and midi buffer Signed-off-by: HiFiPhile --- src/class/cdc/cdc_host.c | 2 +- src/class/midi/midi_host.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/class') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 8b5667aac..57d497950 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -735,7 +735,7 @@ static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t co TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep)); const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx; - tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep, desc_ep->wMaxPacketSize); + tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep, tu_edpt_packet_size(desc_ep)); tu_edpt_stream_clear(stream); desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep); diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index a3d61a02f..bef4d46bf 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -121,9 +121,9 @@ bool midih_init(void) { for (int inst = 0; inst < CFG_TUH_MIDI; inst++) { midih_interface_t *p_midi_host = &_midi_host[inst]; tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false, - p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx); + p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf[inst].rx); tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false, - p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx); + p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf[inst].tx); } return true; } @@ -306,7 +306,7 @@ uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_ ep_stream = &p_midi->ep_stream.rx; } TU_ASSERT(tuh_edpt_open(dev_addr, p_ep), 0); - tu_edpt_stream_open(ep_stream, dev_addr, p_ep, p_ep->wMaxPacketSize); + tu_edpt_stream_open(ep_stream, dev_addr, p_ep, tu_edpt_packet_size(p_ep)); tu_edpt_stream_clear(ep_stream); break; -- cgit v1.3.1 From da21dab358a06106188e2bb06fa71ccd5390fa1a Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 24 Feb 2026 23:37:07 +0700 Subject: fix CDC host FTDI multiple channel loop fix typos for ftdi_process_set_config() add J-Link GDB + RTT logging instructions --- AGENTS.md | 16 +++++ src/class/cdc/cdc_host.c | 171 ++++++++++++++++++++++++----------------------- 2 files changed, 103 insertions(+), 84 deletions(-) (limited to 'src/class') diff --git a/AGENTS.md b/AGENTS.md index ef8baec5a..a5159aa4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -91,6 +91,22 @@ make BOARD=raspberry_pi_pico all - Make: `make BOARD=raspberry_pi_pico all uf2` - **List all targets** (CMake/Ninja): `ninja -t targets` +## J-Link GDB Server + RTT Logging + +- Build with RTT logging enabled (example): + `cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt all` +- Flash with J-Link: + `cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt flash-jlink` +- Launch GDB server (keep this running in terminal 1): + `JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -swoport 2332 -telnetport 2333 -RTTTelnetPort 19021 -nogui` +- Read RTT output (terminal 2): + `JLinkRTTClient` +- Capture RTT to file (optional): + `JLinkRTTClient | tee rtt.log` +- For non-interactive capture: + `timeout 20s JLinkRTTClient > rtt.log` +- Use the board-specific `JLINK_DEVICE` from `hw/bsp/*/boards/*/board.mk` if you are not using `stm32h743eval`. + ## Unit Testing - Install Ceedling: `sudo gem install ceedling` diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f19c4a327..8f6dd7200 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -127,7 +127,7 @@ static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_ static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); -static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer); +static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer); static void ftdi_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer); static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); @@ -216,82 +216,84 @@ typedef struct { #define DRIVER_NAME_DECLARE(_str) #endif +// clang-format off // Note driver list must be in the same order as SERIAL_DRIVER enum static const cdch_serial_driver_t serial_drivers[] = { { - .vid_pid_list = NULL, - .vid_pid_count = 0, - .open = acm_open, - .process_set_config = acm_process_set_config, - .request_complete = acm_internal_control_complete, - .set_control_line_state = acm_set_control_line_state, - .set_baudrate = acm_set_line_coding, - .set_data_format = acm_set_line_coding, - .set_line_coding = acm_set_line_coding, - DRIVER_NAME_DECLARE("ACM") + .vid_pid_list = NULL, + .vid_pid_count = 0, + .open = acm_open, + .process_set_config = acm_process_set_config, + .request_complete = acm_internal_control_complete, + .set_control_line_state = acm_set_control_line_state, + .set_baudrate = acm_set_line_coding, + .set_data_format = acm_set_line_coding, + .set_line_coding = acm_set_line_coding, + DRIVER_NAME_DECLARE("ACM") }, #if CFG_TUH_CDC_FTDI { - .vid_pid_list = ftdi_vid_pid_list, - .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list), - .open = ftdi_open, - .process_set_config = ftdi_proccess_set_config, - .request_complete = ftdi_internal_control_complete, - .set_control_line_state = ftdi_set_modem_ctrl, - .set_baudrate = ftdi_set_baudrate, - .set_data_format = ftdi_set_data_format, - .set_line_coding = NULL, // 2 stage set line coding - DRIVER_NAME_DECLARE("FTDI") + .vid_pid_list = ftdi_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list), + .open = ftdi_open, + .process_set_config = ftdi_process_set_config, + .request_complete = ftdi_internal_control_complete, + .set_control_line_state = ftdi_set_modem_ctrl, + .set_baudrate = ftdi_set_baudrate, + .set_data_format = ftdi_set_data_format, + .set_line_coding = NULL, // 2 stage set line coding + DRIVER_NAME_DECLARE("FTDI") }, #endif #if CFG_TUH_CDC_CP210X { - .vid_pid_list = cp210x_vid_pid_list, - .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list), - .open = cp210x_open, - .process_set_config = cp210x_process_set_config, - .request_complete = cp210x_internal_control_complete, - .set_control_line_state = cp210x_set_modem_ctrl, - .set_baudrate = cp210x_set_baudrate, - .set_data_format = cp210x_set_data_format, - .set_line_coding = NULL, // 2 stage set line coding - DRIVER_NAME_DECLARE("CP210x") + .vid_pid_list = cp210x_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list), + .open = cp210x_open, + .process_set_config = cp210x_process_set_config, + .request_complete = cp210x_internal_control_complete, + .set_control_line_state = cp210x_set_modem_ctrl, + .set_baudrate = cp210x_set_baudrate, + .set_data_format = cp210x_set_data_format, + .set_line_coding = NULL, // 2 stage set line coding + DRIVER_NAME_DECLARE("CP210x") }, #endif #if CFG_TUH_CDC_CH34X { - .vid_pid_list = ch34x_vid_pid_list, - .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list), - .open = ch34x_open, - .process_set_config = ch34x_process_set_config, - .request_complete = ch34x_internal_control_complete, - - .set_control_line_state = ch34x_set_modem_ctrl, - .set_baudrate = ch34x_set_baudrate, - .set_data_format = ch34x_set_data_format, - .set_line_coding = NULL, // 2 stage set line coding - DRIVER_NAME_DECLARE("CH34x") + .vid_pid_list = ch34x_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list), + .open = ch34x_open, + .process_set_config = ch34x_process_set_config, + .request_complete = ch34x_internal_control_complete, + + .set_control_line_state = ch34x_set_modem_ctrl, + .set_baudrate = ch34x_set_baudrate, + .set_data_format = ch34x_set_data_format, + .set_line_coding = NULL, // 2 stage set line coding + DRIVER_NAME_DECLARE("CH34x") }, #endif #if CFG_TUH_CDC_PL2303 { - .vid_pid_list = pl2303_vid_pid_list, - .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list), - .open = pl2303_open, - .process_set_config = pl2303_process_set_config, - .request_complete = pl2303_internal_control_complete, - .set_control_line_state = pl2303_set_modem_ctrl, - .set_baudrate = pl2303_set_line_coding, - .set_data_format = pl2303_set_line_coding, - .set_line_coding = pl2303_set_line_coding, - DRIVER_NAME_DECLARE("PL2303") + .vid_pid_list = pl2303_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list), + .open = pl2303_open, + .process_set_config = pl2303_process_set_config, + .request_complete = pl2303_internal_control_complete, + .set_control_line_state = pl2303_set_modem_ctrl, + .set_baudrate = pl2303_set_line_coding, + .set_data_format = pl2303_set_line_coding, + .set_line_coding = pl2303_set_line_coding, + DRIVER_NAME_DECLARE("PL2303") } #endif }; +// clang-format on TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch"); @@ -761,7 +763,8 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i for (size_t i = 0; i < driver->vid_pid_count; i++) { if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) { const uint16_t drv_len = driver->open(daddr, itf_desc, max_len); - TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, drv_len > 0 ? "OK" : "FAILED"); + TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, + drv_len > 0 ? "OK" : "FAILED"); return drv_len; } } @@ -773,35 +776,16 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i return 0; } -bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { - tusb_control_request_t request; - request.wIndex = tu_htole16((uint16_t) itf_num); - uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); - cdch_interface_t *p_cdc = get_itf(idx); - TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_CDC(p_cdc, "set config"); - - // fake transfer to kick-off process_set_config() - tuh_xfer_t xfer; - xfer.daddr = daddr; - xfer.result = XFER_RESULT_SUCCESS; - xfer.setup = &request; - xfer.user_data = 0; // initial state 0 - cdch_process_set_config(&xfer); - - return true; -} - static void set_config_complete(cdch_interface_t *p_cdc, bool success) { if (success) { const uint8_t idx = get_idx_by_ptr(p_cdc); - p_cdc->mounted = true; + p_cdc->mounted = true; tuh_cdc_mount_cb(idx); // Prepare for incoming data tu_edpt_stream_read_xfer(&p_cdc->stream.rx); } else { // clear the interface entry - p_cdc->daddr = 0; + p_cdc->daddr = 0; p_cdc->bInterfaceNumber = 0; } @@ -810,6 +794,33 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) { usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset); } +bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { + const uint8_t idx = tuh_cdc_itf_get_index(daddr, itf_num); + cdch_interface_t *p_cdc = get_itf(idx); + TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_CDC(p_cdc, "set config"); + + // fake transfer to kick-off process_set_config() + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t)itf_num); + + tuh_xfer_t xfer; + xfer.daddr = daddr; + xfer.ep_addr = 0; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.complete_cb = NULL; + xfer.buffer = NULL; + xfer.user_data = 0; // initial state 0 + + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; + if (!driver->process_set_config(p_cdc, &xfer)) { + set_config_complete(p_cdc, false); + } + + return true; +} + static void cdch_process_set_config(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf_by_xfer(xfer); TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,); @@ -1215,22 +1226,14 @@ static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, return drv_len; } -static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) { +static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) { TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS); const uintptr_t state = xfer->user_data; switch (state) { // from here sequence overtaken from Linux Kernel function ftdi_port_probe() case CONFIG_FTDI_DETERMINE_TYPE: // determine type - if (p_cdc->bInterfaceNumber == 0) { - TU_ASSERT(ftdi_determine_type(p_cdc)); - } else { - // other interfaces have same type as interface 0 - uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0); - cdch_interface_t const *p_cdc_itf0 = get_itf(idx_itf0); - TU_ASSERT(p_cdc_itf0); - p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; - } + TU_ASSERT(ftdi_determine_type(p_cdc)); TU_ATTR_FALLTHROUGH; case CONFIG_FTDI_WRITE_LATENCY: -- cgit v1.3.1 From 4d402194dc506a98ed574233cd542ca7f27ff991 Mon Sep 17 00:00:00 2001 From: Saulo Veríssimo Date: Wed, 25 Feb 2026 12:38:44 -0300 Subject: midi device: add cable-aware stream read (tud_midi_n_demux_stream_read) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing tud_midi_n_stream_read() accepts a cable_num parameter but ignores it — all cables share a single FIFO and stream parser state, so data from different virtual cables is silently mixed together. Add tud_midi_n_demux_stream_read() which returns the cable number of the data that was actually read. It peeks at each USB-MIDI event packet header before consuming it and stops when the next packet belongs to a different cable, allowing callers to dispatch per-cable without losing data. Implementation details: - Mirrors the host-side tuh_midi_stream_read() approach: tu_edpt_stream_peek for cable inspection, CIN-based byte count (USB MIDI 1.0 Table 4-1), leftover handling via existing midi_driver_stream_t - *p_cable_num initialized to 0xff sentinel so callers can detect "no data" even when return value is 0 - Cable-change check (total_read > 0 guard) covers both leftover-originated reads and freshly consumed packets - TU_VERIFY uses explicit != NULL comparisons, consistent with codebase style - Note: shares stream->buffer with tud_midi_n_stream_read(); do not mix calls on the same interface - Adds single-interface convenience wrapper tud_midi_demux_stream_read() Closes #1838 --- src/class/midi/midi_device.c | 102 +++++++++++++++++++++++++++++++++++++++++++ src/class/midi/midi_device.h | 12 +++++ 2 files changed, 114 insertions(+) (limited to 'src/class') diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index 023a81595..a49cd725b 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -168,6 +168,108 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, ui return total_read; } +// Note: this function shares stream->buffer with tud_midi_n_stream_read(). +// Do not mix calls to both functions on the same interface. +uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize) { + TU_VERIFY(p_cable_num != NULL && buffer != NULL && bufsize > 0, 0); + + midid_interface_t *p_midi = &_midid_itf[itf]; + midi_driver_stream_t *stream = &p_midi->stream_read; + tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx; + + uint8_t *buf8 = (uint8_t *)buffer; + uint32_t total_read = 0; + + // Initialize to invalid cable so callers can detect "no data" even when + // the return value is 0. + *p_cable_num = 0xff; + + // If there are leftover bytes from a previous partial read, return them first + if (stream->total > 0) { + *p_cable_num = (stream->buffer[0] >> 4) & 0x0f; + const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize); + TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count)); + + total_read += count; + stream->index += count; + buf8 += count; + bufsize -= count; + + if (stream->total == stream->index) { + stream->index = 0; + stream->total = 0; + } + + if (bufsize == 0) { + return total_read; + } + } + + while (bufsize > 0) { + // Peek at next packet header to get cable number without consuming + uint8_t one_byte; + if (!tu_edpt_stream_peek(ep_str, &one_byte)) { + break; + } + + const uint8_t next_cable = (one_byte >> 4) & 0x0f; + + // Stop if cable changed (covers both leftover-originated reads and + // freshly consumed packets — total_read > 0 in either case) + if (total_read > 0 && next_cable != *p_cable_num) { + break; + } + *p_cable_num = next_cable; + + // Consume the packet + if (!tud_midi_n_packet_read(itf, stream->buffer)) { + break; + } + + const uint8_t code_index = stream->buffer[0] & 0x0f; + uint8_t msg_bytes; + + // MIDI 1.0 Table 4-1: Code Index Number Classifications + switch (code_index) { + case MIDI_CIN_MISC: + case MIDI_CIN_CABLE_EVENT: + // Reserved and unused, skip this packet + continue; + + case MIDI_CIN_SYSEX_END_1BYTE: + case MIDI_CIN_1BYTE_DATA: + msg_bytes = 1; + break; + + case MIDI_CIN_SYSCOM_2BYTE: + case MIDI_CIN_SYSEX_END_2BYTE: + case MIDI_CIN_PROGRAM_CHANGE: + case MIDI_CIN_CHANNEL_PRESSURE: + msg_bytes = 2; + break; + + default: + msg_bytes = 3; + break; + } + + const uint8_t count = (uint8_t)tu_min32((uint32_t)msg_bytes, bufsize); + TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1, count)); + + total_read += count; + buf8 += count; + bufsize -= count; + + if (count < msg_bytes) { + // Output buffer full, save remaining for next call + stream->total = msg_bytes; + stream->index = count; + } + } + + return total_read; +} + bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) { midid_interface_t *p_midi = &_midid_itf[itf]; tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx; diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h index ddbc2f9f0..b80ad544a 100644 --- a/src/class/midi/midi_device.h +++ b/src/class/midi/midi_device.h @@ -66,6 +66,13 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num); // Read byte stream (legacy) uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize); +// Read byte stream with cable demultiplexing: returns the cable number of the +// data that was read. Reads from a single cable per call; stops when the next +// packet belongs to a different cable so the caller can dispatch per-cable. +// Note: shares internal state with tud_midi_n_stream_read(); do not mix both +// on the same interface. +uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize); + // Write byte Stream (legacy) uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize); @@ -96,6 +103,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_read(void *buffer, return tud_midi_n_stream_read(0, 0, buffer, bufsize); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t +tud_midi_demux_stream_read(uint8_t *p_cable_num, void *buffer, uint32_t bufsize) { + return tud_midi_n_demux_stream_read(0, p_cable_num, buffer, bufsize); +} + TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_write(uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) { return tud_midi_n_stream_write(0, cable_num, buffer, bufsize); -- cgit v1.3.1 From 61e4b9ce3fba2fea731396c56eee1c7b5a2f5338 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 5 Mar 2026 23:24:14 +0700 Subject: add IAR warning flags to cmake build and fix them --- examples/device/cdc_msc/src/msc_disk.c | 2 +- examples/device/cdc_msc_freertos/src/msc_disk.c | 15 +++++-------- examples/device/cdc_uac2/src/main.c | 2 -- .../device/dynamic_configuration/src/msc_disk.c | 15 +++++-------- examples/device/hid_boot_interface/src/main.c | 2 -- examples/device/msc_dual_lun/src/main.c | 2 -- examples/device/net_lwip_webserver/src/main.c | 2 -- examples/device/uac2_speaker_fb/src/main.c | 6 +++-- examples/dual/host_hid_to_device_cdc/src/main.c | 2 -- examples/host/bare_api/src/main.c | 2 -- examples/host/device_info/src/main.c | 1 - examples/host/midi_rx/src/main.c | 2 -- examples/host/msc_file_explorer/src/main.c | 2 -- examples/host/msc_file_explorer/src/msc_app.c | 2 -- hw/bsp/family_support.cmake | 7 ++++++ src/class/dfu/dfu_device.c | 6 ++--- src/common/tusb_fifo.c | 19 ++++++++-------- src/common/tusb_fifo.h | 26 +++++++++------------- src/osal/osal_freertos.h | 18 ++++++++------- 19 files changed, 58 insertions(+), 75 deletions(-) (limited to 'src/class') diff --git a/examples/device/cdc_msc/src/msc_disk.c b/examples/device/cdc_msc/src/msc_disk.c index e091c2985..017acd039 100644 --- a/examples/device/cdc_msc/src/msc_disk.c +++ b/examples/device/cdc_msc/src/msc_disk.c @@ -238,7 +238,7 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, u (void) buffer; (void) bufsize; - // currently no other commands is supported + // currently no other commands are supported // Set Sense = Invalid Command Operation (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index 29ff86281..ff918205e 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -324,20 +324,17 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { - // read10 & write10 has their own callback and MUST not be handled here + (void) lun; + (void) scsi_cmd; (void) buffer; (void) bufsize; - switch (scsi_cmd[0]) { - default: - // Set Sense = Invalid Command Operation - tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // currently no other commands are supported - // negative means error -> tinyusb could stall and/or response with failed status - return -1; - } + // Set Sense = Invalid Command Operation + (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - return -1; + return -1; // stall/failed command request; } #endif diff --git a/examples/device/cdc_uac2/src/main.c b/examples/device/cdc_uac2/src/main.c index 22c462be7..cb7b3a142 100644 --- a/examples/device/cdc_uac2/src/main.c +++ b/examples/device/cdc_uac2/src/main.c @@ -65,8 +65,6 @@ int main(void) // printf("Hello, world!\r\n"); #endif } - - return 0; } //--------------------------------------------------------------------+ diff --git a/examples/device/dynamic_configuration/src/msc_disk.c b/examples/device/dynamic_configuration/src/msc_disk.c index e95b2e197..b545e4652 100644 --- a/examples/device/dynamic_configuration/src/msc_disk.c +++ b/examples/device/dynamic_configuration/src/msc_disk.c @@ -215,20 +215,17 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { - // read10 & write10 has their own callback and MUST not be handled here + (void) lun; + (void) scsi_cmd; (void) buffer; (void) bufsize; - switch (scsi_cmd[0]) { - default: - // Set Sense = Invalid Command Operation - tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // currently no other commands are supported - // negative means error -> tinyusb could stall and/or response with failed status - return -1; - } + // Set Sense = Invalid Command Operation + (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - return -1; + return -1; // stall/failed command request; } #endif diff --git a/examples/device/hid_boot_interface/src/main.c b/examples/device/hid_boot_interface/src/main.c index 4de319f52..7f2153ae9 100644 --- a/examples/device/hid_boot_interface/src/main.c +++ b/examples/device/hid_boot_interface/src/main.c @@ -67,8 +67,6 @@ int main(void) { hid_task(); } - - return 0; } //--------------------------------------------------------------------+ diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c index 74a60aa6b..a4ade6f9b 100644 --- a/examples/device/msc_dual_lun/src/main.c +++ b/examples/device/msc_dual_lun/src/main.c @@ -103,8 +103,6 @@ int main(void) { led_blinking_task(NULL); } #endif - - return 0; } #ifdef ESP_PLATFORM diff --git a/examples/device/net_lwip_webserver/src/main.c b/examples/device/net_lwip_webserver/src/main.c index 9f26da2ba..8bd8a8c21 100644 --- a/examples/device/net_lwip_webserver/src/main.c +++ b/examples/device/net_lwip_webserver/src/main.c @@ -276,8 +276,6 @@ int main(void) { sys_check_timeouts(); // service lwip handle_link_state_switch(); } - - return 0; } /* lwip has provision for using a mutex, when applicable */ diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index 8323d82e8..c3e97bb28 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -534,8 +534,9 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); - if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) + if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) { blink_interval_ms = BLINK_MOUNTED; + } return true; } @@ -569,7 +570,8 @@ bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t fu fifo_count = tud_audio_available(); // Same averaging method used in UAC2 class - fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ((uint32_t) fifo_count << 16)) >> 6); + const uint32_t ff_count32 = (uint32_t) fifo_count << 16; + fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ff_count32) >> 6); return true; } diff --git a/examples/dual/host_hid_to_device_cdc/src/main.c b/examples/dual/host_hid_to_device_cdc/src/main.c index ba8ba019a..c8fca48f8 100644 --- a/examples/dual/host_hid_to_device_cdc/src/main.c +++ b/examples/dual/host_hid_to_device_cdc/src/main.c @@ -98,8 +98,6 @@ int main(void) { tuh_task(); // tinyusb host task led_blinking_task(); } - - return 0; } //--------------------------------------------------------------------+ diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index ced2eaa32..81d4d8731 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -74,8 +74,6 @@ int main(void) { tuh_task(); led_blinking_task(); } - - return 0; } /*------------- TinyUSB Callbacks -------------*/ diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c index fd4e9c3ed..b0e38dd6b 100644 --- a/examples/host/device_info/src/main.c +++ b/examples/host/device_info/src/main.c @@ -105,7 +105,6 @@ int main(void) { tuh_task(); // tinyusb host task led_blinking_task(NULL); } - return 0; #endif } diff --git a/examples/host/midi_rx/src/main.c b/examples/host/midi_rx/src/main.c index f189e0864..fb36906c6 100644 --- a/examples/host/midi_rx/src/main.c +++ b/examples/host/midi_rx/src/main.c @@ -58,8 +58,6 @@ int main(void) { led_blinking_task(); midi_host_rx_task(); } - - return 0; } //--------------------------------------------------------------------+ diff --git a/examples/host/msc_file_explorer/src/main.c b/examples/host/msc_file_explorer/src/main.c index 0a8967380..f6bf9a60a 100644 --- a/examples/host/msc_file_explorer/src/main.c +++ b/examples/host/msc_file_explorer/src/main.c @@ -92,8 +92,6 @@ int main(void) { msc_app_task(); led_blinking_task(); } - - return 0; } //--------------------------------------------------------------------+ diff --git a/examples/host/msc_file_explorer/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c index 40a9ef57e..7e019818a 100644 --- a/examples/host/msc_file_explorer/src/msc_app.c +++ b/examples/host/msc_file_explorer/src/msc_app.c @@ -282,8 +282,6 @@ DRESULT disk_ioctl ( default: return RES_PARERR; } - - return RES_OK; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake index 4299ad44e..c166a0618 100644 --- a/hw/bsp/family_support.cmake +++ b/hw/bsp/family_support.cmake @@ -105,6 +105,12 @@ set(WARN_FLAGS_GNU ) set(WARN_FLAGS_Clang ${WARN_FLAGS_GNU}) +set(WARN_FLAGS_IAR + --warnings_are_errors + --diag_suppress=Pa089 + --diag_suppress=Pe236 + ) + # Optimization if (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE) @@ -467,6 +473,7 @@ function(family_configure_common TARGET RTOS) target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments") endif () elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR") + target_compile_options(${TARGET} PRIVATE $<$,$>:${WARN_FLAGS_IAR}>) target_link_options(${TARGET} PUBLIC "LINKER:--map=$.map") if (IAR_CSTAT) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index d3cc53918..a09c53b7e 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -327,7 +327,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control default: if (stage == CONTROL_STAGE_SETUP) { - return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0); + return reply_getstatus(rhport, request, (dfu_state_t) _dfu_ctx.state, (dfu_status_t) _dfu_ctx.status, 0); } break; } @@ -376,7 +376,7 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus timeout = 0; } - return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout); + return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout); } else if (stage == CONTROL_STAGE_ACK) { if (_dfu_ctx.flashing_in_progress) { _dfu_ctx.state = DFU_DNBUSY; @@ -405,7 +405,7 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus timeout = 0; } - return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout); + return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout); } else if (stage == CONTROL_STAGE_ACK) { if (_dfu_ctx.flashing_in_progress) { _dfu_ctx.state = DFU_MANIFEST; diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 9f188f296..8bd79e56d 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -30,11 +30,6 @@ #define TU_FIFO_DBG 0 -// Suppress IAR warning -// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement -#if defined(__ICCARM__) - #pragma diag_suppress = Pa082 -#endif #if OSAL_MUTEX_REQUIRED @@ -496,7 +491,9 @@ uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, ui // 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, NULL); + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, wr_idx, rd_idx, NULL); ff_unlock(f->mutex_rd); return ret; } @@ -506,7 +503,8 @@ uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, cons 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); + const uint16_t wr_idx = f->wr_idx; + n = tu_fifo_peek_n_access_mode(f, buffer, n, wr_idx, f->rd_idx, access_mode); f->rd_idx = advance_index(f->depth, f->rd_idx, n); ff_unlock(f->mutex_rd); @@ -633,7 +631,8 @@ static bool ff_peek_local(tu_fifo_t *f, void *buf, uint16_t wr_idx, uint16_t rd_ 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 - const bool ret = ff_peek_local(f, buffer, f->wr_idx, f->rd_idx); + const uint16_t wr_idx = f->wr_idx; + const bool ret = ff_peek_local(f, buffer, wr_idx, f->rd_idx); if (ret) { ff_lock(f->mutex_rd); f->rd_idx = advance_index(f->depth, f->rd_idx, 1); @@ -645,7 +644,9 @@ bool tu_fifo_read(tu_fifo_t *f, void *buffer) { // 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); + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + return ff_peek_local(f, p_buffer, wr_idx, rd_idx); } // Write one element into the buffer diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index a3829e38e..b31a0802e 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -120,9 +120,9 @@ typedef struct { uint8_t *buffer; // buffer pointer uint16_t depth; // max items bool overwritable; // overwritable when full - // 1 byte padding here + // 1 byte padding here - volatile uint16_t wr_idx; // write index TODO maybe can drop volatile + volatile uint16_t wr_idx; // write index volatile uint16_t rd_idx; // read index #if OSAL_MUTEX_REQUIRED @@ -289,30 +289,26 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) { return wr_idx == rd_idx; } -// Suppress IAR warning -// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement -#if defined(__ICCARM__) -#pragma diag_suppress = Pa082 -#endif - // return number of items in fifo, capped to fifo's depth TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_count(const tu_fifo_t *f) { - return tu_min16(tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx), f->depth); + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + return tu_min16(tu_ff_overflow_count(f->depth, wr_idx, rd_idx), f->depth); } // check if fifo is full TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_full(const tu_fifo_t *f) { - return tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + return tu_ff_overflow_count(f->depth, wr_idx, rd_idx) >= f->depth; } TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_remaining(const tu_fifo_t *f) { - return tu_ff_remaining_local(f->depth, f->wr_idx, f->rd_idx); + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + return tu_ff_remaining_local(f->depth, wr_idx, rd_idx); } -#if defined(__ICCARM__) - #pragma diag_default=Pa082 -#endif - #ifdef __cplusplus } #endif diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h index 32ee2d55c..db724179d 100644 --- a/src/osal/osal_freertos.h +++ b/src/osal/osal_freertos.h @@ -141,11 +141,12 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) { TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) { if (in_isr) { - if (TUP_MCU_MULTIPLE_CORE == 0) { - (void) ctx; - return; // single core MCU does not need to lock in ISR - } + #if TUP_MCU_MULTIPLE_CORE *ctx = taskENTER_CRITICAL_FROM_ISR(); + #else + (void) ctx; + return; // single core MCU does not need to lock in ISR + #endif } else { taskENTER_CRITICAL(); } @@ -153,11 +154,12 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bo TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) { if (in_isr) { - if (TUP_MCU_MULTIPLE_CORE == 0) { - (void) ctx; - return; // single core MCU does not need to lock in ISR - } + #if TUP_MCU_MULTIPLE_CORE taskEXIT_CRITICAL_FROM_ISR(*ctx); + #else + (void) ctx; + return; // single core MCU does not need to lock in ISR + #endif } else { taskEXIT_CRITICAL(); } -- cgit v1.3.1 From 5838c7f09dda8921fe6f350871211f8bd5a97208 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 15:11:59 +0700 Subject: update printer class: enhance descriptors, buffer sizes, and callbacks --- AGENTS.md | 3 + examples/device/CMakeLists.txt | 1 + examples/device/printer_to_hid/src/main.c | 18 +- examples/device/printer_to_hid/src/tusb_config.h | 10 +- .../device/printer_to_hid/src/usb_descriptors.c | 243 +++++++++++++-------- .../device/printer_to_hid/src/usb_descriptors.h | 47 +--- src/class/printer/printer.h | 29 ++- src/class/printer/printer_device.c | 105 +++++---- src/class/printer/printer_device.h | 22 +- src/device/usbd.h | 15 -- src/tusb.h | 4 - 11 files changed, 284 insertions(+), 213 deletions(-) (limited to 'src/class') diff --git a/AGENTS.md b/AGENTS.md index 34fc57cb8..bbbd7c36d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -332,6 +332,9 @@ take 2-5 minutes. NEVER CANCEL. Set timeout to 20+ minutes. - `examples/device/cdc_msc/`: Most commonly used example for testing - `test/unit-test/project.yml`: Ceedling test configuration +#### MCU Reference Manuals and Datasheets +- Look in `$HOME/Documents/Calibre Library` for all MCU reference manuals, datasheets and board schematics. + #### Debugging Build Issues - **Missing compiler**: Install `gcc-arm-none-eabi` package - **Missing dependencies**: Run `python3 tools/get_deps.py FAMILY` diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index 660df67cb..dbcb8df6a 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -30,6 +30,7 @@ set(EXAMPLE_LIST msc_dual_lun mtp net_lwip_webserver + printer_to_hid uac2_headset uac2_speaker_fb usbtmc diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c index 765d1e653..02b54c7d0 100644 --- a/examples/device/printer_to_hid/src/main.c +++ b/examples/device/printer_to_hid/src/main.c @@ -81,7 +81,7 @@ static void hid_tx_task(void) { return; } - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return; // not enough time } start_ms += interval_ms; @@ -208,6 +208,22 @@ void tud_printer_rx_cb(uint8_t itf, size_t n) { pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer } +// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). +// The rest is the Device ID string using standard abbreviated keys. +static const char printer_device_id[] = + "\x00\x34" // total length = 52 = 0x0034 (big-endian) + "MFG:TinyUSB;" + "MDL:Printer to HID;" + "CMD:PS;" + "CLS:PRINTER;"; + +TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch"); + +uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return (uint8_t const *)printer_device_id; +} + //--------------------------------------------------------------------+ // HID callbacks diff --git a/examples/device/printer_to_hid/src/tusb_config.h b/examples/device/printer_to_hid/src/tusb_config.h index a8dbac89d..0988be166 100644 --- a/examples/device/printer_to_hid/src/tusb_config.h +++ b/examples/device/printer_to_hid/src/tusb_config.h @@ -34,8 +34,6 @@ extern "C" { // Board Specific Configuration //--------------------------------------------------------------------+ -#define BOARD_DEVICE_RHPORT_NUM 3 - // RHPort number used for device can be defined by board.mk, default to port 0 #ifndef BOARD_TUD_RHPORT #define BOARD_TUD_RHPORT 0 @@ -103,10 +101,10 @@ extern "C" { // HID buffer size Should be sufficient to hold ID (if any) + Data #define CFG_TUD_HID_EP_BUFSIZE 16 -// Printer buffer size Should be sufficient to hold data -#define CFG_TUD_PRINTER_RX_BUFSIZE 16 -#define CFG_TUD_PRINTER_TX_BUFSIZE 16 -#define CFG_TUD_PRINTER_EP_BUFSIZE 16 +// Printer buffer sizes +#define CFG_TUD_PRINTER_RX_BUFSIZE 512 +#define CFG_TUD_PRINTER_TX_BUFSIZE 512 +#define CFG_TUD_PRINTER_EP_BUFSIZE 512 #ifdef __cplusplus } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c index 9a88cb891..1cbabb02e 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.c +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2026 Ha Thach (tinyusb.org) + * Copyright (c) 2019 Ha Thach (tinyusb.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,117 +28,188 @@ #include "usb_descriptors.h" +#define USB_VID 0xCafe +#define USB_PID 0x4004 +#define USB_BCD 0x0200 //--------------------------------------------------------------------+ -// Report definitions +// Device Descriptors //--------------------------------------------------------------------+ +static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, -// Values of the string descriptors. Order must match the order defined by STRING_DESCRIPTOR_INDICES. -const char *STRING_DESCRIPTOR_VALUES[] = { - (const char[]){0x09, 0x04}, // 0: Supported language is English (0x0409) - "TinyUSB", // 1: Manufacturer - "TinyUSB Device", // 2: Product - NULL, // 3: Serial number, will use unique ID from the Pi Pico board hardware - "Config1", // 4: Configuration - "Hid1", // 5: HID interface - "Print1", // 6: Printer interface + .bNumConfigurations = 0x01 }; -uint8_t HID_REPORT_DESCRIPTOR[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD))}; +uint8_t const *tud_descriptor_device_cb(void) { + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ +static uint8_t const desc_hid_report[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)) +}; -uint8_t CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR[] = { +uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +// Endpoint numbers +#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + #define EPNUM_HID 0x81 + #define EPNUM_PRINTER_OUT 0x02 + #define EPNUM_PRINTER_IN 0x83 +#else + #define EPNUM_HID 0x81 + #define EPNUM_PRINTER_OUT 0x02 + #define EPNUM_PRINTER_IN 0x82 +#endif + +// full speed configuration +static uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - // HID: // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(HID_REPORT_DESCRIPTOR), EPADDR_HID, + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5), - // Printer: // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size - TUD_PRINTER_DESCRIPTOR(ITF_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE)}; - -static const tusb_desc_device_t DEVICE_DESCRIPTOR = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, // Define class at interface level - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64), +}; - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, +#if TUD_OPT_HIGH_SPEED +// high speed configuration +static uint8_t const desc_hs_configuration[] = { + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIAL, + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, + CFG_TUD_HID_EP_BUFSIZE, 5), - .bNumConfigurations = 0x01 + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 512), }; +// other speed configuration +static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; -//--------------------------------------------------------------------+ -// TinyUSB callbacks (descriptor requests) -//--------------------------------------------------------------------+ +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, -// TinyUSB GET HID REPORT DESCRIPTOR callback. -const uint8_t *tud_hid_descriptor_report_cb(uint8_t instance) { - (void)instance; - return HID_REPORT_DESCRIPTOR; + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; } -// TinyUSB GET CONFIGURATION DESCRIPTOR callback. -const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - (void)index; - return CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR; +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; + + // if link speed is high return fullspeed config, and vice versa + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; } -// TinyUSB GET DEVICE DESCRIPTOR callback. -const uint8_t *tud_descriptor_device_cb(void) { - return (const uint8_t *)&DEVICE_DESCRIPTOR; +#endif // TUD_OPT_HIGH_SPEED + +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void) index; + +#if TUD_OPT_HIGH_SPEED + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif } -// Storage buffer array for string descriptor to be sent to host. -static uint16_t string_descriptor_buffer[STRING_DESCRIPTOR_MAX_LENGTH + 1]; - -// TinyUSB GET STRING DESCRIPTOR callback. -const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void)langid; - size_t utf16_string_length; - - if (index == LANGID) { - // langid is not a string as in a series of characters: language ID code is binary, 2 bytes - memcpy(string_descriptor_buffer + 1, STRING_DESCRIPTOR_VALUES[LANGID], 2); - utf16_string_length = 1; // 2 bytes = 1 UTF16 word - - } else if (index == STR_SERIAL) { - // serialnumber is generated from pi pico: see note in STRING_DESCRIPTOR_VALUES definition - utf16_string_length = board_usb_get_serial(string_descriptor_buffer + 1, STRING_DESCRIPTOR_MAX_LENGTH); - - } else if (index < STRING_COUNT) { - // Get adequate descriptor string - const char *str = STRING_DESCRIPTOR_VALUES[index]; - utf16_string_length = strlen(str); - if (utf16_string_length > STRING_DESCRIPTOR_MAX_LENGTH) { - utf16_string_length = STRING_DESCRIPTOR_MAX_LENGTH; - } - // Convert ASCII string from memory (char*) to UTF16 (for buffer), - // store in buffer with 1 UTF16 word offset (2 bytes, for buffer header) - for (size_t i = 0; i < utf16_string_length; i++) { - string_descriptor_buffer[i + 1] = str[i]; - } - - } else { - return NULL; - } +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_HID, + STRID_PRINTER, +}; - // Set buffer header: - // byte 1 - buffer length in bytes (including header) - // byte 0 - string descriptor type (0x03). - string_descriptor_buffer[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * utf16_string_length + 2)); +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serial, use unique ID if possible + "TinyUSB HID", // 4: HID Interface + "TinyUSB Printer", // 5: Printer Interface +}; + +static uint16_t _desc_str[32 + 1]; + +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + + switch (index) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; } + + const char *str = string_desc_arr[index]; + + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; + if (chr_count > max_count) { chr_count = max_count; } + + for (size_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + break; + } - return string_descriptor_buffer; + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + return _desc_str; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h index 0d9793a9c..20c34f151 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.h +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -25,52 +25,17 @@ #ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ -#include "bsp/board_api.h" -#include "tusb.h" - -#define USB_VID 0xCafe // unassigned vendor id -#define USB_PID 0x4004 // random product id -#define USB_BCD 0x0200 // binary coded version: 2.00 - -// Configuration -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) - -// HID interface endpoints -#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 -// Printer interface endpoints -#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 -#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 - // HID report ID -#define REPORT_ID_KEYBOARD 1 - -// The maximum length of the string that will be sent to the host via the STRING DESCRIPTOR. Note that the -// string descriptor itself is two bytes wider than the string. -#define STRING_DESCRIPTOR_MAX_LENGTH 32 - -//--------------------------------------------------------------------+ -// Configuration, interface, endpoint descriptors -//--------------------------------------------------------------------+ - enum { - ITF_HID, - ITF_PRINTER, - ITF_COUNT, + REPORT_ID_KEYBOARD = 1, }; -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - enum { - LANGID = 0, - STR_MANUFACTURER, - STR_PRODUCT, - STR_SERIAL, - STR_CONFIGURATION, - STR_HID_INTERFACE, - STR_PRINTER_INTERFACE, - STRING_COUNT, + ITF_NUM_HID, + ITF_NUM_PRINTER, + ITF_NUM_TOTAL, }; +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + #endif /* USB_DESCRIPTORS_H_ */ diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index c9ed3cebc..b32543077 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_PRINTER_H_ -#define _TUSB_PRINTER_H_ +#ifndef TUSB_PRINTER_H_ +#define TUSB_PRINTER_H_ #include "common/tusb_common.h" @@ -35,13 +35,28 @@ extern "C" { /// Printer Class Specific Control Request typedef enum { - PRINTER_REQ_CONTROL_GET_DEVICE_ID = 0x01, ///< Get device ID - PRINTER_REQ_CONTROL_GET_PORT_STATUS = 0x02, ///< Get port status - PRINTER_REQ_CONTROL_SOFT_RESET = 0x03, ///< Soft reset -} printer_request_enum_t; + TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x01, ///< Get device ID + TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x02, ///< Get port status + TUSB_PRINTER_REQUEST_SOFT_RESET = 0x03, ///< Soft reset +} tusb_printer_request_type_t; + +/// Printer Port Status (returned by GET_PORT_STATUS request) +/// USB Printer Class spec 1.1, Section 4.2 +typedef union TU_ATTR_PACKED { + uint8_t status; + struct TU_ATTR_PACKED { + uint8_t reserved0 : 3; ///< Reserved (bits 0-2) + uint8_t not_error : 1; ///< 1 = no error, 0 = error + uint8_t selected : 1; ///< 1 = selected (online), 0 = not selected + uint8_t paper_empty : 1; ///< 1 = paper empty, 0 = paper not empty + uint8_t reserved6 : 2; ///< Reserved (bits 6-7) + } status_bm; +} tusb_printer_port_status_t; + +TU_VERIFY_STATIC(sizeof(tusb_printer_port_status_t) == 1, "size is not correct"); #ifdef __cplusplus } #endif -#endif /* _TUSB_PRINTER_H__ */ +#endif diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index aaea4c988..d288014a1 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -28,13 +28,13 @@ #if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) - //--------------------------------------------------------------------+ - // INCLUDE - //--------------------------------------------------------------------+ - #include "device/usbd.h" - #include "device/usbd_pvt.h" +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "device/usbd.h" +#include "device/usbd_pvt.h" - #include "printer_device.h" +#include "printer_device.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -63,6 +63,8 @@ typedef struct { TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); } printer_epbuf_t; +#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_ff) + static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; @@ -70,9 +72,6 @@ CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ - -static tud_printer_configure_fifo_t _printer_fifo_cfg; - static bool _prep_out_transaction(uint8_t itf) { const uint8_t rhport = 0; printer_interface_t *p_printer = &_printer_itf[itf]; @@ -112,6 +111,25 @@ TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n) { (void)n; } +TU_ATTR_WEAK void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request) { + (void)itf; + (void)request; +} + +TU_ATTR_WEAK uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return NULL; +} + +TU_ATTR_WEAK uint8_t tud_printer_get_port_status_cb(uint8_t itf) { + (void)itf; + return 0x18; // not error, selected, paper not empty +} + +TU_ATTR_WEAK void tud_printer_soft_reset_cb(uint8_t itf) { + (void)itf; +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -142,7 +160,6 @@ void tud_printer_n_read_flush(uint8_t itf) { //--------------------------------------------------------------------+ void printerd_init(void) { tu_memclr(_printer_itf, sizeof(_printer_itf)); - tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; @@ -166,7 +183,7 @@ bool printerd_deinit(void) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; - osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_wr; if (mutex_rd) { osal_mutex_delete(mutex_rd); @@ -189,15 +206,9 @@ void printerd_reset(uint8_t rhport) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; - tu_memclr(p_printer, sizeof(&p_printer)); - if (!_printer_fifo_cfg.rx_persistent) { - tu_fifo_clear(&p_printer->rx_ff); - } - if (!_printer_fifo_cfg.tx_persistent) { - tu_fifo_clear(&p_printer->tx_ff); - } - // tu_fifo_set_overwritable(&p_printer->rx_ff, true); - tu_fifo_set_overwritable(&p_printer->tx_ff, true); + tu_memclr(p_printer, ITF_MEM_RESET_SIZE); + tu_fifo_clear(&p_printer->rx_ff); + tu_fifo_clear(&p_printer->tx_ff); } } @@ -222,7 +233,7 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui //------------- Endpoints -------------// TU_ASSERT(itf_desc->bNumEndpoints == 2); drv_len += 2 * sizeof(tusb_desc_endpoint_t); - p_printer->itf_num = 2; + p_printer->itf_num = itf_desc->bInterfaceNumber; const uint8_t *p_desc = tu_desc_next(itf_desc); TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); @@ -233,6 +244,7 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + uint8_t const itf_num = (uint8_t)request->wIndex; if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { //------------- STD Request -------------// @@ -240,39 +252,38 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ return true; } } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - switch (request->bRequest) { - // https://www.usb.org/sites/default/files/usbprint11a021811.pdf - case PRINTER_REQ_CONTROL_GET_DEVICE_ID: - if (stage == CONTROL_STAGE_SETUP) { - const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" - "MODEL:LaserBeam 9;" - "COMMAND SET:PS;" - "COMMENT:Anything you like;" - "ACTIVE COMMAND SET:PS;"; - char buffer[256]; - strcpy(buffer + 2, deviceId); - buffer[0] = 0x00; - buffer[1] = strlen(deviceId); - return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + if (stage == CONTROL_STAGE_SETUP) { + switch (request->bRequest) { + case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: { + // App provides buffer with IEEE 1284 format (first 2 bytes = big-endian length) + const uint8_t *device_id = tud_printer_get_device_id_cb(itf_num); + TU_VERIFY(device_id); + const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]); + return tud_control_xfer(rhport, request, (void *)(uintptr_t)device_id, total_len); } - break; - case PRINTER_REQ_CONTROL_GET_PORT_STATUS: - if (stage == CONTROL_STAGE_SETUP) { - static uint8_t port_status = 0b00011000; // paper not empty, selected, no error + + case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: { + static uint8_t port_status; + port_status = tud_printer_get_port_status_cb(itf_num); return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); } - break; - case PRINTER_REQ_CONTROL_SOFT_RESET: - if (stage == CONTROL_STAGE_SETUP) { - return false; // TODO: reset buffers, reset Bulk In and Out endpoints, clear stall conditions - } - break; - default: - return false; + + case TUSB_PRINTER_REQUEST_SOFT_RESET: + tud_printer_soft_reset_cb(itf_num); + tud_control_status(rhport, request); + return true; + + default: + return false; + } + } else if (stage == CONTROL_STAGE_ACK) { + tud_printer_request_complete_cb(itf_num, request); } } else { return false; } + return true; } diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h index 8c21e39ce..aeff1ffc5 100644 --- a/src/class/printer/printer_device.h +++ b/src/class/printer/printer_device.h @@ -33,11 +33,6 @@ extern "C" { #endif -typedef struct TU_ATTR_PACKED { - uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect - uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect -} tud_printer_configure_fifo_t; - //--------------------------------------------------------------------+ // Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1 //--------------------------------------------------------------------+ @@ -59,7 +54,22 @@ bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8); //--------------------------------------------------------------------+ // Invoked when received new data -TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n); +void tud_printer_rx_cb(uint8_t itf, size_t n); + +// Invoked when host requests device ID string (IEEE 1284). +// Application returns pointer to device ID buffer (must remain valid until transfer completes). +// First 2 bytes of returned buffer must contain big-endian length (including the 2 length bytes). +const uint8_t *tud_printer_get_device_id_cb(uint8_t itf); + +// Invoked when host requests port status. +uint8_t tud_printer_get_port_status_cb(uint8_t itf); + +// Invoked when host requests soft reset. +void tud_printer_soft_reset_cb(uint8_t itf); + +// Invoked when a control request is completed (GET_DEVICE_ID, GET_PORT_STATUS, etc.) +void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request); + //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/device/usbd.h b/src/device/usbd.h index c6e7d31c8..d3a6dccbb 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -330,21 +330,6 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 -//--------------------------------------------------------------------+ -// Printer Descriptor Templates -//--------------------------------------------------------------------+ - -#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints - -#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 - - //--------------------------------------------------------------------+ // HID Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tusb.h b/src/tusb.h index 4f76397cc..c80c8433c 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -96,10 +96,6 @@ #include "class/mtp/mtp_device.h" #endif - #if CFG_TUD_PRINTER - #include "class/printer/printer_device.h" - #endif - #if CFG_TUD_AUDIO #include "class/audio/audio_device.h" #endif -- cgit v1.3.1 From 988b18a40a4276eb731c80bc4cec3bf5750523f9 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 16:06:42 +0700 Subject: add full read/write() API, use edpt stream for printer class --- examples/device/printer_to_hid/src/main.c | 137 ++++------------ src/class/printer/printer_device.c | 250 +++++++++++++++--------------- src/class/printer/printer_device.h | 55 ++++++- 3 files changed, 208 insertions(+), 234 deletions(-) (limited to 'src/class') diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c index 02b54c7d0..0d14147b4 100644 --- a/examples/device/printer_to_hid/src/main.c +++ b/examples/device/printer_to_hid/src/main.c @@ -33,34 +33,15 @@ #include "usb_descriptors.h" // -------------------------------------------------------------------+ -// Variables controlled with USB endpoint callbacks +// Variables // -------------------------------------------------------------------+ -// usb interface pointer -uint8_t printer_itf = 0; -// pendings bytes in usb endpoint buffer ; must process these bytes -size_t pending_bytes_on_usb_ep = 0; - - -// -------------------------------------------------------------------+ -// Variables controlled locally -// -------------------------------------------------------------------+ - -// local data buffer (copy data from usb endpoint into this buffer) ; acts as fifo -uint8_t data_buffer[16] = {0}; -// write offset for usb/printer incoming data to data_buffer -size_t data_rx_offset = 0; -// read offset for usb/hid outgoing data from data_buffer -size_t data_tx_offset = 0; -// available space in data_buffer -size_t data_available = sizeof(data_buffer); - // next keycode to send on usb/hid -uint8_t next_keycode = 0; +static uint8_t next_keycode = 0; // next key modifiers to send on usb/hid -uint8_t next_modifiers = 0; +static uint8_t next_modifiers = 0; // whether the next usb/hid report must be NULL to release the last keystroke -uint8_t next_keycode_is_release = false; +static bool next_keycode_is_release = false; // -------------------------------------------------------------------+ @@ -99,83 +80,34 @@ static void hid_tx_task(void) { next_keycode = 0; } -// Whenever there are pendings bytes on the USB endpoint, we will pull them from the -// endpoint buffer and write then in the local data buffer. We must take care to not -// overwrite local data that is not processed yet, so we use data_buffer as a fifo. -// We do not have to take care of reading correctly from the endpoint buffer, as all -// is done well by tud_printer_n_read(). -static void printer_rx_task(void) { - - if (pending_bytes_on_usb_ep == 0) { - return; +// Read one byte from printer FIFO and translate to HID keycode. +// Only a-zA-Z0-9 are translated; everything else becomes space. +static void printer_to_hid_task(void) { + if (next_keycode != 0) { + return; // previous key not yet sent } - size_t len1 = data_available; - size_t len2 = 0; - if (data_rx_offset + len1 > sizeof(data_buffer)) { - len2 = len1 - (sizeof(data_buffer) - data_rx_offset); - len1 = sizeof(data_buffer) - data_rx_offset; - } - uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); - if (len2 > 0) { - count += tud_printer_n_read(printer_itf, data_buffer, len2); + uint8_t ch; + if (tud_printer_read(&ch, 1) == 0) { + return; // no data available } - if (count == 0) { - return; + uint8_t m = 0; + if ('a' <= ch && ch <= 'z') { + ch = (uint8_t)(ch - 'a' + HID_KEY_A); + } else if ('A' <= ch && ch <= 'Z') { + ch = (uint8_t)(ch - 'A' + HID_KEY_A); + m = KEYBOARD_MODIFIER_LEFTSHIFT; + } else if ('1' <= ch && ch <= '9') { + ch = (uint8_t)(ch - '1' + HID_KEY_1); + } else if (ch == '0') { + ch = HID_KEY_0; + } else { + ch = HID_KEY_SPACE; } - data_available -= count; - pending_bytes_on_usb_ep -= count; - data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); -} - -// The HID keycodes are not binary mapped like UTF8 codes. If we want to send the -// data received as a usb/printer, we have to translate the binary data for the -// usb/hid interface. Note that the simple mapping below will be valid only for -// hosts expecting hid data from a QWERTY keyboard. Also note that only a-zA-Z0-9 -// characters are converted, for simplicity of the example. Other characters are -// converted to spaces. -static void translation_task(void) { - if (data_tx_offset != data_rx_offset || data_available == 0) { - // If data_tx_offset and data_rx_offset have different values, then we - // can proceed: translate, prepare for TX, and advance data_tx_offset. - // - // If the buffer is full (data_available == 0), then we must also - // translate data and prepare it for TX. But the data_tx_offset and - // data_rx_offset will have the same value, since RX caught up to TX. - // Hence the OR. - - // Translate UTF8 to HID keystroke - char c = data_buffer[data_tx_offset]; - uint8_t m = 0; - if ('a' <= c && c <= 'z') { - c -= 'a'; - c += HID_KEY_A; - } else if ('A' <= c && c <= 'Z') { - c -= 'A'; - c += HID_KEY_A; - m = KEYBOARD_MODIFIER_LEFTSHIFT; - } else if ('1' <= c && c <= '9') { - c -= '1'; - c += HID_KEY_1; - } else if (c == '0') { - c = HID_KEY_0; - } else { - c = HID_KEY_SPACE; - } - - // Proceed only if there are no characters pending for TX - if (next_keycode == 0) { - // Prepare next keystroke with translated data - next_keycode = c; - next_modifiers = m; - // Increment read offset - data_tx_offset += 1; - data_tx_offset %= sizeof(data_buffer); - data_available += 1; - } - } + next_keycode = ch; + next_modifiers = m; } int main(void) { @@ -186,15 +118,9 @@ int main(void) { board_init_after_tusb(); while (1) { - tud_task(); // tinyusb device task - printer_rx_task(); // read data sent by host on our printer interface - translation_task(); // translate printer's UTF8 to HID keycodes - hid_tx_task(); // send data to host with our HID interface - if (pending_bytes_on_usb_ep > 0) { - board_led_on(); - } else { - board_led_off(); - } + tud_task(); // tinyusb device task + printer_to_hid_task(); // read printer data and translate to HID keycodes + hid_tx_task(); // send keycodes to host via HID } } @@ -203,9 +129,8 @@ int main(void) { // Printer callbacks //--------------------------------------------------------------------+ -void tud_printer_rx_cb(uint8_t itf, size_t n) { - printer_itf = itf; // get interface from which to read endpoint buffer - pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer +void tud_printer_rx_cb(uint8_t itf) { + (void)itf; } // IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index d288014a1..05d3f28eb 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -28,9 +28,6 @@ #if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -42,73 +39,52 @@ typedef struct { uint8_t itf_num; - uint8_t ep_out; // Bulk Out endpoint - uint8_t ep_in; // optional Bulk In endpoint /*------------- From this point, data is not cleared by bus reset -------------*/ - // FIFO - tu_fifo_t rx_ff; - tu_fifo_t tx_ff; + tu_edpt_stream_t rx_stream; + tu_edpt_stream_t tx_stream; uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; - - OSAL_MUTEX_DEF(rx_ff_mutex); - OSAL_MUTEX_DEF(tx_ff_mutex); } printer_interface_t; +#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_stream) + +#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 typedef struct { TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); } printer_epbuf_t; -#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_ff) - -static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; +#endif +static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; //--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION +// INTERNAL HELPERS //--------------------------------------------------------------------+ -static bool _prep_out_transaction(uint8_t itf) { - const uint8_t rhport = 0; - printer_interface_t *p_printer = &_printer_itf[itf]; - printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; - - // Skip if usb is not ready yet - TU_VERIFY(tud_ready() && p_printer->ep_out); - uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); - - // Prepare for incoming data but only allow what we can store in the ring buffer. - // TODO Actually we can still carry out the transfer, keeping count of received bytes - // and slowly move it to the FIFO when read(). - // This pre-check reduces endpoint claiming - TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); - - // claim endpoint - TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); - - // fifo can be changed before endpoint is claimed - available = tu_fifo_remaining(&p_printer->rx_ff); - - if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { - return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE, false); - } else { - // Release endpoint since we don't make any transfer - usbd_edpt_release(rhport, p_printer->ep_out); - return false; +TU_ATTR_ALWAYS_INLINE static inline uint8_t _find_itf(uint8_t ep_addr) { + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + const printer_interface_t *p = &_printer_itf[i]; + if (ep_addr == p->rx_stream.ep_addr || ep_addr == p->tx_stream.ep_addr) { + return i; + } } + return TUSB_INDEX_INVALID_8; } //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ -TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n) { +TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf) { + (void)itf; +} + +TU_ATTR_WEAK void tud_printer_tx_complete_cb(uint8_t itf) { (void)itf; - (void)n; } TU_ATTR_WEAK void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request) { @@ -131,29 +107,53 @@ TU_ATTR_WEAK void tud_printer_soft_reset_cb(uint8_t itf) { } //--------------------------------------------------------------------+ -// APPLICATION API +// READ API //--------------------------------------------------------------------+ -uint32_t tud_printer_n_available(uint8_t itf) { - return tu_fifo_count(&_printer_itf[itf].rx_ff); +uint32_t tud_printer_n_read_available(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_PRINTER, 0); + return tu_edpt_stream_read_available(&_printer_itf[itf].rx_stream); } uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { - printer_interface_t *p_printer = &_printer_itf[itf]; - uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); - _prep_out_transaction(itf); - return num_read; + TU_VERIFY(itf < CFG_TUD_PRINTER, 0); + return tu_edpt_stream_read(&_printer_itf[itf].rx_stream, buffer, bufsize); } bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { - return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); + TU_VERIFY(itf < CFG_TUD_PRINTER); + return tu_edpt_stream_peek(&_printer_itf[itf].rx_stream, chr); } void tud_printer_n_read_flush(uint8_t itf) { - printer_interface_t *p_printer = &_printer_itf[itf]; - tu_fifo_clear(&p_printer->rx_ff); - _prep_out_transaction(itf); + TU_VERIFY(itf < CFG_TUD_PRINTER, ); + printer_interface_t *p = &_printer_itf[itf]; + tu_edpt_stream_clear(&p->rx_stream); + tu_edpt_stream_read_xfer(&p->rx_stream); } +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize) { + TU_VERIFY(itf < CFG_TUD_PRINTER, 0); + return tu_edpt_stream_write(&_printer_itf[itf].tx_stream, buffer, bufsize); +} + +uint32_t tud_printer_n_write_flush(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_PRINTER, 0); + return tu_edpt_stream_write_xfer(&_printer_itf[itf].tx_stream); +} + +uint32_t tud_printer_n_write_available(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_PRINTER, 0); + return tu_edpt_stream_write_available(&_printer_itf[itf].tx_stream); +} + +bool tud_printer_n_write_clear(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_PRINTER); + tu_edpt_stream_clear(&_printer_itf[itf].tx_stream); + return true; +} //--------------------------------------------------------------------+ // USBD-CLASS API @@ -162,41 +162,32 @@ void printerd_init(void) { tu_memclr(_printer_itf, sizeof(_printer_itf)); for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - - tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), false); - tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), true); + printer_interface_t *p = &_printer_itf[i]; + + #if CFG_TUD_EDPT_DEDICATED_HWFIFO + uint8_t *epout_buf = NULL; + uint8_t *epin_buf = NULL; + #else + uint8_t *epout_buf = _printer_epbuf[i].epout; + uint8_t *epin_buf = _printer_epbuf[i].epin; + #endif - #if OSAL_MUTEX_REQUIRED - osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); - osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); - TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + tu_edpt_stream_init(&p->rx_stream, false, false, false, + p->rx_ff_buf, CFG_TUD_PRINTER_RX_BUFSIZE, + epout_buf, CFG_TUD_PRINTER_EP_BUFSIZE); - tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); - tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); - #endif + tu_edpt_stream_init(&p->tx_stream, false, true, true, + p->tx_ff_buf, CFG_TUD_PRINTER_TX_BUFSIZE, + epin_buf, CFG_TUD_PRINTER_EP_BUFSIZE); } } bool printerd_deinit(void) { - #if OSAL_MUTEX_REQUIRED for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; - osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_wr; - - if (mutex_rd) { - osal_mutex_delete(mutex_rd); - tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); - } - - if (mutex_wr) { - osal_mutex_delete(mutex_wr); - tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); - } + printer_interface_t *p = &_printer_itf[i]; + tu_edpt_stream_deinit(&p->rx_stream); + tu_edpt_stream_deinit(&p->tx_stream); } - #endif - return true; } @@ -204,40 +195,48 @@ void printerd_reset(uint8_t rhport) { (void)rhport; for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { - printer_interface_t *p_printer = &_printer_itf[i]; - - tu_memclr(p_printer, ITF_MEM_RESET_SIZE); - tu_fifo_clear(&p_printer->rx_ff); - tu_fifo_clear(&p_printer->tx_ff); + printer_interface_t *p = &_printer_itf[i]; + tu_memclr(p, ITF_MEM_RESET_SIZE); + tu_edpt_stream_close(&p->rx_stream); + tu_edpt_stream_close(&p->tx_stream); } } uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { - (void)max_len; TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); - // Identify available interface to open - printer_interface_t *p_printer; - uint8_t printer_id; - for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { - p_printer = &_printer_itf[printer_id]; - if (p_printer->ep_out == 0) { - break; - } - } - TU_ASSERT(printer_id < CFG_TUD_PRINTER); + // Find available interface slot + uint8_t const printer_id = _find_itf(0); + TU_ASSERT(printer_id < CFG_TUD_PRINTER, 0); + printer_interface_t *p = &_printer_itf[printer_id]; - //------------- Interface -------------// - uint16_t drv_len = sizeof(tusb_desc_interface_t); + p->itf_num = itf_desc->bInterfaceNumber; //------------- Endpoints -------------// - TU_ASSERT(itf_desc->bNumEndpoints == 2); - drv_len += 2 * sizeof(tusb_desc_endpoint_t); - p_printer->itf_num = itf_desc->bInterfaceNumber; - const uint8_t *p_desc = tu_desc_next(itf_desc); - TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); + const uint8_t *p_desc = (const uint8_t *)itf_desc; + const uint8_t *desc_end = p_desc + max_len; + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + p_desc = tu_desc_next(itf_desc); + for (uint8_t e = 0; e < itf_desc->bNumEndpoints; e++) { + TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0); + const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0); + + TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); + + if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { + tu_edpt_stream_open(&p->tx_stream, rhport, desc_ep); + tu_edpt_stream_clear(&p->tx_stream); + } else { + tu_edpt_stream_open(&p->rx_stream, rhport, desc_ep); + tu_edpt_stream_clear(&p->rx_stream); + TU_ASSERT(tu_edpt_stream_read_xfer(&p->rx_stream) > 0, 0); + } - _prep_out_transaction(printer_id); + drv_len += sizeof(tusb_desc_endpoint_t); + p_desc = tu_desc_next(p_desc); + } return drv_len; } @@ -247,7 +246,6 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ uint8_t const itf_num = (uint8_t)request->wIndex; if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { - //------------- STD Request -------------// if (stage != CONTROL_STAGE_SETUP) { return true; } @@ -256,7 +254,6 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ if (stage == CONTROL_STAGE_SETUP) { switch (request->bRequest) { case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: { - // App provides buffer with IEEE 1284 format (first 2 bytes = big-endian length) const uint8_t *device_id = tud_printer_get_device_id_cb(itf_num); TU_VERIFY(device_id); const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]); @@ -290,28 +287,29 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void)rhport; (void)result; - uint8_t itf; - printer_interface_t *p_printer; - - // Identify which interface to use - for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { - p_printer = &_printer_itf[itf]; - if (ep_addr == p_printer->ep_out) { - break; - } - } + + uint8_t const itf = _find_itf(ep_addr); TU_ASSERT(itf < CFG_TUD_PRINTER); - printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + printer_interface_t *p = &_printer_itf[itf]; // Received new data - if (ep_addr == p_printer->ep_out) { - tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); - // invoke receive callback (if there is still data) - if (!tu_fifo_empty(&p_printer->rx_ff)) { - tud_printer_rx_cb(itf, xferred_bytes); + if (ep_addr == p->rx_stream.ep_addr) { + tu_edpt_stream_read_xfer_complete(&p->rx_stream, xferred_bytes); + + if (!tu_edpt_stream_empty(&p->rx_stream)) { + tud_printer_rx_cb(itf); + } + + tu_edpt_stream_read_xfer(&p->rx_stream); + } + + // Data sent to host + if (ep_addr == p->tx_stream.ep_addr) { + tud_printer_tx_complete_cb(itf); + + if (0 == tu_edpt_stream_write_xfer(&p->tx_stream)) { + tu_edpt_stream_write_zlp_if_needed(&p->tx_stream, xferred_bytes); } - // prepare for OUT transaction - _prep_out_transaction(itf); } return true; diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h index aeff1ffc5..a6b7052cb 100644 --- a/src/class/printer/printer_device.h +++ b/src/class/printer/printer_device.h @@ -38,23 +38,74 @@ extern "C" { //--------------------------------------------------------------------+ // Get the number of bytes available for reading -uint32_t tud_printer_n_available(uint8_t itf); +uint32_t tud_printer_n_read_available(uint8_t itf); // Read received bytes uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize); +// Get the number of bytes available for writing +uint32_t tud_printer_n_write_available(uint8_t itf); + // Clear the received FIFO void tud_printer_n_read_flush(uint8_t itf); // Get a byte from FIFO without removing it bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8); +// Write data to host +uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize); + +// Force sending data in the TX FIFO +uint32_t tud_printer_n_write_flush(uint8_t itf); + +// Clear the transmit FIFO +bool tud_printer_n_write_clear(uint8_t itf); + +//--------------------------------------------------------------------+ +// Application API (Single Port) +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read_available(void) { + return tud_printer_n_read_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_available(void) { + return tud_printer_n_write_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read(void *buffer, uint32_t bufsize) { + return tud_printer_n_read(0, buffer, bufsize); +} + +TU_ATTR_ALWAYS_INLINE static inline void tud_printer_read_flush(void) { + tud_printer_n_read_flush(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_peek(uint8_t *ui8) { + return tud_printer_n_peek(0, ui8); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write(const void *buffer, uint32_t bufsize) { + return tud_printer_n_write(0, buffer, bufsize); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_flush(void) { + return tud_printer_n_write_flush(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_write_clear(void) { + return tud_printer_n_write_clear(0); +} + //--------------------------------------------------------------------+ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when received new data -void tud_printer_rx_cb(uint8_t itf, size_t n); +void tud_printer_rx_cb(uint8_t itf); + +// Invoked when last write transfer is completed +void tud_printer_tx_complete_cb(uint8_t itf); // Invoked when host requests device ID string (IEEE 1284). // Application returns pointer to device ID buffer (must remain valid until transfer completes). -- cgit v1.3.1 From 73cd53129529f2dce70072953bfd50ec9dc6b8ea Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 17:22:38 +0700 Subject: replace printer_to_hid example with printer_to_cdc example fix printer GET_DEVICE_ID request weird wIndex (interface high, alt low) --- examples/device/CMakeLists.txt | 2 +- examples/device/printer_to_cdc/CMakeLists.txt | 29 +++ examples/device/printer_to_cdc/Makefile | 11 ++ examples/device/printer_to_cdc/src/main.c | 115 +++++++++++ examples/device/printer_to_cdc/src/tusb_config.h | 117 +++++++++++ .../device/printer_to_cdc/src/usb_descriptors.c | 208 ++++++++++++++++++++ .../device/printer_to_cdc/src/usb_descriptors.h | 37 ++++ examples/device/printer_to_hid/CMakeLists.txt | 29 --- examples/device/printer_to_hid/CMakePresets.json | 6 - examples/device/printer_to_hid/Makefile | 11 -- examples/device/printer_to_hid/README.md | 0 examples/device/printer_to_hid/src/main.c | 175 ----------------- examples/device/printer_to_hid/src/tusb_config.h | 113 ----------- .../device/printer_to_hid/src/usb_descriptors.c | 215 --------------------- .../device/printer_to_hid/src/usb_descriptors.h | 41 ---- src/class/printer/printer.h | 6 +- src/class/printer/printer_device.c | 77 ++++---- src/device/usbd.c | 24 ++- 18 files changed, 587 insertions(+), 629 deletions(-) create mode 100644 examples/device/printer_to_cdc/CMakeLists.txt create mode 100644 examples/device/printer_to_cdc/Makefile create mode 100644 examples/device/printer_to_cdc/src/main.c create mode 100644 examples/device/printer_to_cdc/src/tusb_config.h create mode 100644 examples/device/printer_to_cdc/src/usb_descriptors.c create mode 100644 examples/device/printer_to_cdc/src/usb_descriptors.h delete mode 100644 examples/device/printer_to_hid/CMakeLists.txt delete mode 100644 examples/device/printer_to_hid/CMakePresets.json delete mode 100644 examples/device/printer_to_hid/Makefile delete mode 100644 examples/device/printer_to_hid/README.md delete mode 100644 examples/device/printer_to_hid/src/main.c delete mode 100644 examples/device/printer_to_hid/src/tusb_config.h delete mode 100644 examples/device/printer_to_hid/src/usb_descriptors.c delete mode 100644 examples/device/printer_to_hid/src/usb_descriptors.h (limited to 'src/class') diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index dbcb8df6a..7173f455e 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -30,7 +30,7 @@ set(EXAMPLE_LIST msc_dual_lun mtp net_lwip_webserver - printer_to_hid + printer_to_cdc uac2_headset uac2_speaker_fb usbtmc diff --git a/examples/device/printer_to_cdc/CMakeLists.txt b/examples/device/printer_to_cdc/CMakeLists.txt new file mode 100644 index 000000000..3c8ab3653 --- /dev/null +++ b/examples/device/printer_to_cdc/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.20) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +project(printer_to_cdc C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}) +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT_NAME}) + +# Example source +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT_NAME} noos) diff --git a/examples/device/printer_to_cdc/Makefile b/examples/device/printer_to_cdc/Makefile new file mode 100644 index 000000000..1a4b428dc --- /dev/null +++ b/examples/device/printer_to_cdc/Makefile @@ -0,0 +1,11 @@ +include ../../../hw/bsp/family_support.mk + +INC += \ + src \ + + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../../hw/bsp/family_rules.mk diff --git a/examples/device/printer_to_cdc/src/main.c b/examples/device/printer_to_cdc/src/main.c new file mode 100644 index 000000000..aba79025c --- /dev/null +++ b/examples/device/printer_to_cdc/src/main.c @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/* This example demonstrates a USB Printer + CDC composite device. + * Data received on the Printer interface is forwarded to the CDC serial port, + * and data received on the CDC serial port is forwarded back to the Printer interface. + * + * To test: + * 1. Flash the device + * 2. Open a serial terminal on the CDC port (e.g. /dev/ttyACM0) + * 3. Send data to the printer: echo "hello" > /dev/usb/lp0 + * 4. The data appears on the CDC serial terminal + * 5. Type in the serial terminal to send data back through the printer TX + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +#include "usb_descriptors.h" + +// -------------------------------------------------------------------+ +// Tasks +// -------------------------------------------------------------------+ + +// Forward data from Printer RX to CDC TX +static void printer_to_cdc_task(void) { + if (tud_printer_read_available() == 0 || !tud_cdc_write_available()) { + return; + } + + uint8_t buf[64]; + uint32_t count = tud_printer_read(buf, sizeof(buf)); + if (count > 0) { + tud_cdc_write(buf, count); + tud_cdc_write_flush(); + } +} + +// Forward data from CDC RX to Printer TX +static void cdc_to_printer_task(void) { + if (tud_cdc_available() == 0 || !tud_printer_write_available()) { + return; + } + + uint8_t buf[64]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + if (count > 0) { + tud_printer_write(buf, count); + tud_printer_write_flush(); + } +} + +int main(void) { + board_init(); + // init device stack on configured roothub port + tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO}; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + board_init_after_tusb(); + + while (1) { + tud_task(); // tinyusb device task + printer_to_cdc_task(); // forward printer data to CDC + cdc_to_printer_task(); // forward CDC data to printer + } +} + +//--------------------------------------------------------------------+ +// Printer callbacks +//--------------------------------------------------------------------+ + +void tud_printer_rx_cb(uint8_t itf) { + (void)itf; +} + +// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). +// The rest is the Device ID string using standard abbreviated keys. +static const char printer_device_id[] = + "\x00\x34" // total length = 52 = 0x0034 (big-endian) + "MFG:TinyUSB;" + "MDL:Printer to CDC;" + "CMD:PS;" + "CLS:PRINTER;"; + +TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch"); + +uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return (uint8_t const *)printer_device_id; +} diff --git a/examples/device/printer_to_cdc/src/tusb_config.h b/examples/device/printer_to_cdc/src/tusb_config.h new file mode 100644 index 000000000..50ef41762 --- /dev/null +++ b/examples/device/printer_to_cdc/src/tusb_config.h @@ -0,0 +1,117 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT + #define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED + #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS + #define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION + #define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN + #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 0 +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_PRINTER 1 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// Printer buffer sizes +#define CFG_TUD_PRINTER_RX_BUFSIZE 512 +#define CFG_TUD_PRINTER_TX_BUFSIZE 512 +#define CFG_TUD_PRINTER_EP_BUFSIZE 512 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c new file mode 100644 index 000000000..30d309ed4 --- /dev/null +++ b/examples/device/printer_to_cdc/src/usb_descriptors.c @@ -0,0 +1,208 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "bsp/board_api.h" +#include "tusb.h" + +#include "usb_descriptors.h" + +#define USB_VID 0xCafe +#define USB_PID 0x4005 +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +uint8_t const *tud_descriptor_device_cb(void) { + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +// Endpoint numbers +#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x83 + #define EPNUM_PRINTER_OUT 0x04 + #define EPNUM_PRINTER_IN 0x85 +#else + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + #define EPNUM_PRINTER_OUT 0x03 + #define EPNUM_PRINTER_IN 0x83 +#endif + +// full speed configuration +static uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), + + // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// high speed configuration +static uint8_t const desc_hs_configuration[] = { + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), + + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 512), +}; + +// other speed configuration +static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; +} + +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; + + // if link speed is high return fullspeed config, and vice versa + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // TUD_OPT_HIGH_SPEED + +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void) index; + +#if TUD_OPT_HIGH_SPEED + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_CDC, + STRID_PRINTER, +}; + +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serial, use unique ID if possible + "TinyUSB CDC", // 4: CDC Interface + "TinyUSB Printer", // 5: Printer Interface +}; + +static uint16_t _desc_str[32 + 1]; + +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + + switch (index) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; } + + const char *str = string_desc_arr[index]; + + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; + if (chr_count > max_count) { chr_count = max_count; } + + for (size_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + break; + } + + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + return _desc_str; +} diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.h b/examples/device/printer_to_cdc/src/usb_descriptors.h new file mode 100644 index 000000000..830593b4d --- /dev/null +++ b/examples/device/printer_to_cdc/src/usb_descriptors.h @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef USB_DESCRIPTORS_H_ +#define USB_DESCRIPTORS_H_ + +enum { + ITF_NUM_CDC, + ITF_NUM_CDC_DATA, + ITF_NUM_PRINTER, + ITF_NUM_TOTAL, +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_PRINTER_DESC_LEN) + +#endif /* USB_DESCRIPTORS_H_ */ diff --git a/examples/device/printer_to_hid/CMakeLists.txt b/examples/device/printer_to_hid/CMakeLists.txt deleted file mode 100644 index f58759059..000000000 --- a/examples/device/printer_to_hid/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) - -project(printer_to_hid C CXX ASM) - -# Checks this example is valid for the family and initializes the project -family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}) -# Espressif has its own cmake build system -if(FAMILY STREQUAL "espressif") - return() -endif() - -add_executable(${PROJECT_NAME}) - -# Example source -target_sources(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c - ) - -# Example include -target_include_directories(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ) - -# Configure compilation flags and libraries for the example without RTOS. -# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. -family_configure_device_example(${PROJECT_NAME} noos) diff --git a/examples/device/printer_to_hid/CMakePresets.json b/examples/device/printer_to_hid/CMakePresets.json deleted file mode 100644 index 5cd8971e9..000000000 --- a/examples/device/printer_to_hid/CMakePresets.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": 6, - "include": [ - "../../../hw/bsp/BoardPresets.json" - ] -} diff --git a/examples/device/printer_to_hid/Makefile b/examples/device/printer_to_hid/Makefile deleted file mode 100644 index 1a4b428dc..000000000 --- a/examples/device/printer_to_hid/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -include ../../../hw/bsp/family_support.mk - -INC += \ - src \ - - -# Example source -EXAMPLE_SOURCE += $(wildcard src/*.c) -SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE)) - -include ../../../hw/bsp/family_rules.mk diff --git a/examples/device/printer_to_hid/README.md b/examples/device/printer_to_hid/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c deleted file mode 100644 index 0d14147b4..000000000 --- a/examples/device/printer_to_hid/src/main.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2026 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include -#include -#include - -#include "bsp/board_api.h" -#include "tusb.h" - -#include "usb_descriptors.h" - -// -------------------------------------------------------------------+ -// Variables -// -------------------------------------------------------------------+ - -// next keycode to send on usb/hid -static uint8_t next_keycode = 0; -// next key modifiers to send on usb/hid -static uint8_t next_modifiers = 0; -// whether the next usb/hid report must be NULL to release the last keystroke -static bool next_keycode_is_release = false; - - -// -------------------------------------------------------------------+ -// Tasks -// -------------------------------------------------------------------+ - -// Every 10ms, we will place HID data in the usb/hid endpoint, ready to -// sent to the host when required. The tasks will read the keycode in -// next_keycode and place it in the HID report, then set next_is_null -// such that the key is released by the next report. This seem to help -// stroking the same key twice when the character is repeated in the data. -static void hid_tx_task(void) { - // Poll every 10ms - const uint32_t interval_ms = 10; - static uint32_t start_ms = 0; - - if (!tud_hid_ready()) { - return; - } - - if (tusb_time_millis_api() - start_ms < interval_ms) { - return; // not enough time - } - start_ms += interval_ms; - - if (next_keycode_is_release || next_keycode == 0) { - tud_hid_keyboard_report(1, 0, NULL); - next_keycode_is_release = false; - return; - } - - uint8_t keycode_array[6] = {0}; - keycode_array[0] = next_keycode; - tud_hid_keyboard_report(1, next_modifiers, keycode_array); - next_keycode_is_release = true; - next_keycode = 0; -} - -// Read one byte from printer FIFO and translate to HID keycode. -// Only a-zA-Z0-9 are translated; everything else becomes space. -static void printer_to_hid_task(void) { - if (next_keycode != 0) { - return; // previous key not yet sent - } - - uint8_t ch; - if (tud_printer_read(&ch, 1) == 0) { - return; // no data available - } - - uint8_t m = 0; - if ('a' <= ch && ch <= 'z') { - ch = (uint8_t)(ch - 'a' + HID_KEY_A); - } else if ('A' <= ch && ch <= 'Z') { - ch = (uint8_t)(ch - 'A' + HID_KEY_A); - m = KEYBOARD_MODIFIER_LEFTSHIFT; - } else if ('1' <= ch && ch <= '9') { - ch = (uint8_t)(ch - '1' + HID_KEY_1); - } else if (ch == '0') { - ch = HID_KEY_0; - } else { - ch = HID_KEY_SPACE; - } - - next_keycode = ch; - next_modifiers = m; -} - -int main(void) { - board_init(); - // init device and host stack on configured roothub port - tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO}; - tusb_init(BOARD_TUD_RHPORT, &dev_init); - board_init_after_tusb(); - - while (1) { - tud_task(); // tinyusb device task - printer_to_hid_task(); // read printer data and translate to HID keycodes - hid_tx_task(); // send keycodes to host via HID - } -} - - -//--------------------------------------------------------------------+ -// Printer callbacks -//--------------------------------------------------------------------+ - -void tud_printer_rx_cb(uint8_t itf) { - (void)itf; -} - -// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). -// The rest is the Device ID string using standard abbreviated keys. -static const char printer_device_id[] = - "\x00\x34" // total length = 52 = 0x0034 (big-endian) - "MFG:TinyUSB;" - "MDL:Printer to HID;" - "CMD:PS;" - "CLS:PRINTER;"; - -TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch"); - -uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { - (void)itf; - return (uint8_t const *)printer_device_id; -} - - -//--------------------------------------------------------------------+ -// HID callbacks -//--------------------------------------------------------------------+ - -uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, - uint16_t reqlen) { - (void)instance; - (void)report_id; - (void)report_type; - (void)buffer; - (void)reqlen; - return 0; -} - -void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, - uint16_t bufsize) { - (void)instance; - (void)report_id; - (void)report_type; - (void)buffer; - (void)bufsize; - return; -} diff --git a/examples/device/printer_to_hid/src/tusb_config.h b/examples/device/printer_to_hid/src/tusb_config.h deleted file mode 100644 index 0988be166..000000000 --- a/examples/device/printer_to_hid/src/tusb_config.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2026 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Board Specific Configuration -//--------------------------------------------------------------------+ - -// RHPort number used for device can be defined by board.mk, default to port 0 -#ifndef BOARD_TUD_RHPORT - #define BOARD_TUD_RHPORT 0 -#endif - -// RHPort max operational speed can defined by board.mk -#ifndef BOARD_TUD_MAX_SPEED - #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED -#endif - -//-------------------------------------------------------------------- -// COMMON CONFIGURATION -//-------------------------------------------------------------------- - -// defined by compiler flags for flexibility -#ifndef CFG_TUSB_MCU - #error CFG_TUSB_MCU must be defined -#endif - -#ifndef CFG_TUSB_OS - #define CFG_TUSB_OS OPT_OS_NONE -#endif - -#ifndef CFG_TUSB_DEBUG - #define CFG_TUSB_DEBUG 0 -#endif - -// Enable Device stack -#define CFG_TUD_ENABLED 1 - -// Default is max speed that hardware controller could support with on-chip PHY -#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED - -/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. - * Tinyusb use follows macros to declare transferring memory so that they can be put - * into those specific section. - * e.g - * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) - * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) - */ -#ifndef CFG_TUSB_MEM_SECTION - #define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN - #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) -#endif - -//-------------------------------------------------------------------- -// DEVICE CONFIGURATION -//-------------------------------------------------------------------- - -#ifndef CFG_TUD_ENDPOINT0_SIZE - #define CFG_TUD_ENDPOINT0_SIZE 64 -#endif - -//------------- CLASS -------------// -#define CFG_TUD_HID 1 -#define CFG_TUD_CDC 0 -#define CFG_TUD_MSC 0 -#define CFG_TUD_MIDI 0 -#define CFG_TUD_VENDOR 0 -#define CFG_TUD_PRINTER 1 - -// HID buffer size Should be sufficient to hold ID (if any) + Data -#define CFG_TUD_HID_EP_BUFSIZE 16 - -// Printer buffer sizes -#define CFG_TUD_PRINTER_RX_BUFSIZE 512 -#define CFG_TUD_PRINTER_TX_BUFSIZE 512 -#define CFG_TUD_PRINTER_EP_BUFSIZE 512 - -#ifdef __cplusplus -} -#endif - -#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c deleted file mode 100644 index 1cbabb02e..000000000 --- a/examples/device/printer_to_hid/src/usb_descriptors.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "bsp/board_api.h" -#include "tusb.h" - -#include "usb_descriptors.h" - -#define USB_VID 0xCafe -#define USB_PID 0x4004 -#define USB_BCD 0x0200 - -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ -static tusb_desc_device_t const desc_device = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 -}; - -uint8_t const *tud_descriptor_device_cb(void) { - return (uint8_t const *) &desc_device; -} - -//--------------------------------------------------------------------+ -// HID Report Descriptor -//--------------------------------------------------------------------+ -static uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)) -}; - -uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { - (void)instance; - return desc_hid_report; -} - -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ - -// Endpoint numbers -#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) - #define EPNUM_HID 0x81 - #define EPNUM_PRINTER_OUT 0x02 - #define EPNUM_PRINTER_IN 0x83 -#else - #define EPNUM_HID 0x81 - #define EPNUM_PRINTER_OUT 0x02 - #define EPNUM_PRINTER_IN 0x82 -#endif - -// full speed configuration -static uint8_t const desc_fs_configuration[] = { - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - - // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, - CFG_TUD_HID_EP_BUFSIZE, 5), - - // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size - TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64), -}; - -#if TUD_OPT_HIGH_SPEED -// high speed configuration -static uint8_t const desc_hs_configuration[] = { - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, - CFG_TUD_HID_EP_BUFSIZE, 5), - - TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 512), -}; - -// other speed configuration -static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; - -// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed -static tusb_desc_device_qualifier_t const desc_device_qualifier = { - .bLength = sizeof(tusb_desc_device_qualifier_t), - .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, - .bcdUSB = USB_BCD, - - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .bNumConfigurations = 0x01, - .bReserved = 0x00 -}; - -uint8_t const *tud_descriptor_device_qualifier_cb(void) { - return (uint8_t const *) &desc_device_qualifier; -} - -uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { - (void) index; - - // if link speed is high return fullspeed config, and vice versa - memcpy(desc_other_speed_config, - (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, - CONFIG_TOTAL_LEN); - - desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; - - return desc_other_speed_config; -} - -#endif // TUD_OPT_HIGH_SPEED - -uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { - (void) index; - -#if TUD_OPT_HIGH_SPEED - return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; -#else - return desc_fs_configuration; -#endif -} - -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -enum { - STRID_LANGID = 0, - STRID_MANUFACTURER, - STRID_PRODUCT, - STRID_SERIAL, - STRID_HID, - STRID_PRINTER, -}; - -static char const *string_desc_arr[] = { - (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409) - "TinyUSB", // 1: Manufacturer - "TinyUSB Device", // 2: Product - NULL, // 3: Serial, use unique ID if possible - "TinyUSB HID", // 4: HID Interface - "TinyUSB Printer", // 5: Printer Interface -}; - -static uint16_t _desc_str[32 + 1]; - -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void) langid; - size_t chr_count; - - switch (index) { - case STRID_LANGID: - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - break; - - case STRID_SERIAL: - chr_count = board_usb_get_serial(_desc_str + 1, 32); - break; - - default: - if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; } - - const char *str = string_desc_arr[index]; - - chr_count = strlen(str); - size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; - if (chr_count > max_count) { chr_count = max_count; } - - for (size_t i = 0; i < chr_count; i++) { - _desc_str[1 + i] = str[i]; - } - break; - } - - _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); - return _desc_str; -} diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h deleted file mode 100644 index 20c34f151..000000000 --- a/examples/device/printer_to_hid/src/usb_descriptors.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef USB_DESCRIPTORS_H_ -#define USB_DESCRIPTORS_H_ - -// HID report ID -enum { - REPORT_ID_KEYBOARD = 1, -}; - -enum { - ITF_NUM_HID, - ITF_NUM_PRINTER, - ITF_NUM_TOTAL, -}; - -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) - -#endif /* USB_DESCRIPTORS_H_ */ diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index b32543077..09d1a8956 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -35,9 +35,9 @@ extern "C" { /// Printer Class Specific Control Request typedef enum { - TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x01, ///< Get device ID - TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x02, ///< Get port status - TUSB_PRINTER_REQUEST_SOFT_RESET = 0x03, ///< Soft reset + TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x00, ///< Get device ID + TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x01, ///< Get port status + TUSB_PRINTER_REQUEST_SOFT_RESET = 0x02, ///< Soft reset } tusb_printer_request_type_t; /// Printer Port Status (returned by GET_PORT_STATUS request) diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index 05d3f28eb..f5bb33795 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -242,43 +242,54 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui } bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { - TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); - uint8_t const itf_num = (uint8_t)request->wIndex; + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { - if (stage != CONTROL_STAGE_SETUP) { - return true; + // GET_DEVICE_ID: wIndex = (interface_number << 8) | alt_setting + // GET_PORT_STATUS / SOFT_RESET: wIndex = interface_number + uint8_t itf_num; + if (TUSB_PRINTER_REQUEST_GET_DEVICE_ID == request->bRequest) { + itf_num = tu_u16_high(request->wIndex); + } else { + itf_num = tu_u16_low(request->wIndex); + } + + // Find the printer instance index from the USB interface number + uint8_t itf = TUSB_INDEX_INVALID_8; + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + if (_printer_itf[i].itf_num == itf_num) { + itf = i; + break; } - } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - // https://www.usb.org/sites/default/files/usbprint11a021811.pdf - if (stage == CONTROL_STAGE_SETUP) { - switch (request->bRequest) { - case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: { - const uint8_t *device_id = tud_printer_get_device_id_cb(itf_num); - TU_VERIFY(device_id); - const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]); - return tud_control_xfer(rhport, request, (void *)(uintptr_t)device_id, total_len); - } - - case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: { - static uint8_t port_status; - port_status = tud_printer_get_port_status_cb(itf_num); - return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); - } - - case TUSB_PRINTER_REQUEST_SOFT_RESET: - tud_printer_soft_reset_cb(itf_num); - tud_control_status(rhport, request); - return true; - - default: - return false; + } + TU_VERIFY(itf < CFG_TUD_PRINTER); + + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + if (stage == CONTROL_STAGE_SETUP) { + switch (request->bRequest) { + case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: { + const uint8_t *device_id = tud_printer_get_device_id_cb(itf); + TU_VERIFY(device_id); + const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]); + return tud_control_xfer(rhport, request, (void *)(uintptr_t)device_id, total_len); + } + + case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: { + static uint8_t port_status; + port_status = tud_printer_get_port_status_cb(itf); + return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); } - } else if (stage == CONTROL_STAGE_ACK) { - tud_printer_request_complete_cb(itf_num, request); + + case TUSB_PRINTER_REQUEST_SOFT_RESET: + tud_printer_soft_reset_cb(itf); + tud_control_status(rhport, request); + return true; + + default: + return false; } - } else { - return false; + } else if (stage == CONTROL_STAGE_ACK) { + tud_printer_request_complete_cb(itf, request); } return true; diff --git a/src/device/usbd.c b/src/device/usbd.c index 8d5f94313..42903576c 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -836,7 +836,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) { TU_LOG_USBD(" %s", tu_str_std_request[p_request->bRequest]); - if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG_USBD("\r\n"); + if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) { + TU_LOG_USBD("\r\n"); + } } #endif @@ -972,7 +974,25 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const //------------- Class/Interface Specific Request -------------// case TUSB_REQ_RCPT_INTERFACE: { - uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t itf; + #if CFG_TUD_PRINTER + // Printer GET_DEVICE_ID has a weird wIndex = interface (high) | alt (low) + // attempt to interpret this as a printer request if matched + if (TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type && + TUSB_DIR_IN == p_request->bmRequestType_bit.direction && + TUSB_PRINTER_REQUEST_GET_DEVICE_ID == p_request->bRequest) { + itf = tu_u16_high(p_request->wIndex); + if (itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)) { + const usbd_class_driver_t * driver = get_driver(_usbd_dev.itf2drv[itf]); + if (driver != NULL && driver->control_xfer_cb == printerd_control_xfer_cb) { + if (invoke_class_control(rhport, driver, p_request)) { + return true; + } + } + } + } + #endif + itf = tu_u16_low(p_request->wIndex); TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); -- cgit v1.3.1 From fe26e45e64dc0c356ddba3470343244c7544b3ac Mon Sep 17 00:00:00 2001 From: YixingShen Date: Sat, 7 Mar 2026 02:03:50 +0800 Subject: fixed _open_vc_itf parsing Standard Interface Descriptor (Video Control) Video Control Header Descriptor Video Control Camera Terminal Descriptor Video Control Output Terminal Descriptor Standard Endpoint Descriptor Class-Specific VC Interrupt Endpoint Descriptor Video Control Header Descriptor's wTotalLength = Header Descriptor + Camera Terminal Descriptor + Output Terminal Descriptor _end_of_control_descriptor is Output Terminal Descriptor End the "end" should +7 for _find_desc searchig Standard Endpoint Descriptor --- src/class/video/video_device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index b2375def9..24f31c6ad 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -771,7 +771,10 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING); /* Update to point the end of the video control interface descriptor. */ - end = _end_of_control_descriptor(cur); + end = _end_of_control_descriptor(cur) + 7; + //tusb_desc_video_control_header wTotalLength = + // tusb_desc_video_control_header + tusb_desc_video_control_camera_terminal_t + tusb_desc_video_control_output_terminal_t + // has no status Interrupt EP desc, then "end" should +7 for _find_desc searchig EP desc /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; -- cgit v1.3.1 From e4a55152be9ae5fb8efebbb5ef197a2d3097b6cc Mon Sep 17 00:00:00 2001 From: YixingShen Date: Sat, 7 Mar 2026 02:05:12 +0800 Subject: cleanup --- src/class/video/video_device.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 24f31c6ad..0f7d6b53b 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -772,9 +772,6 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t /* Update to point the end of the video control interface descriptor. */ end = _end_of_control_descriptor(cur) + 7; - //tusb_desc_video_control_header wTotalLength = - // tusb_desc_video_control_header + tusb_desc_video_control_camera_terminal_t + tusb_desc_video_control_output_terminal_t - // has no status Interrupt EP desc, then "end" should +7 for _find_desc searchig EP desc /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; -- cgit v1.3.1 From 0a45308a296251e4376815b21f89f0038c75a60e Mon Sep 17 00:00:00 2001 From: 沈玴興 Date: Sat, 7 Mar 2026 10:46:38 +0800 Subject: Update src/class/video/video_device.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/class/video/video_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 0f7d6b53b..85ffcd627 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -771,7 +771,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING); /* Update to point the end of the video control interface descriptor. */ - end = _end_of_control_descriptor(cur) + 7; + end = (uint8_t const *) _end_of_control_descriptor(cur) + sizeof(tusb_desc_endpoint_t); /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; -- cgit v1.3.1 From 2a0802c6a56dc3849fe21d376908aff07131a111 Mon Sep 17 00:00:00 2001 From: YixingShen Date: Sat, 7 Mar 2026 22:56:28 +0800 Subject: fixed _end_of_control_descriptor logic. wTotalLength does not include Standard Endpoint Descriptor and Class-specific VC Interrupt Endpoint Descriptor, so fix that _end_of_control_descriptor include Standard Endpoint Descriptor and Class-specific VC Interrupt Endpoint Descriptor. It will also fix _close_vc_itf, _open_vc_itf. _find_desc_entity parsing. --- src/class/video/video_device.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 85ffcd627..a65d83d8f 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -392,7 +392,13 @@ static void const* _find_desc_ep(void const *beg, void const *end) static inline void const* _end_of_control_descriptor(void const *desc) { tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)desc; - return ((uint8_t const*) desc) + vc->std.bLength + tu_le16toh(vc->ctl.wTotalLength); + uint8_t const *end = (uint8_t const*)desc + vc->std.bLength + + tu_le16toh(vc->ctl.wTotalLength); + if (vc->std.bNumEndpoints) { + end += sizeof(tusb_desc_endpoint_t); // standard EP descriptor + end += 5; // class-specific EP descriptor (fixed 5 bytes per UVC spec) + } + return end; } /** Find the first entity descriptor with the entity ID @@ -771,7 +777,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING); /* Update to point the end of the video control interface descriptor. */ - end = (uint8_t const *) _end_of_control_descriptor(cur) + sizeof(tusb_desc_endpoint_t); + end = _end_of_control_descriptor(cur); /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; -- cgit v1.3.1 From 3e22de0a4782d6062959aec27e5304f1b4d4a557 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Mar 2026 10:49:04 +0700 Subject: extend video control parsing to include class-specific VC endpoint descriptor --- src/class/video/video_device.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index a65d83d8f..d1c699940 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -70,6 +70,13 @@ typedef struct TU_ATTR_PACKED { uint8_t bEntityId; } tusb_desc_cs_video_entity_itf_t; +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint16_t wMaxTransferSize; +} tusb_desc_cs_video_vc_ep_t; + typedef union { struct TU_ATTR_PACKED { uint8_t bLength; @@ -746,6 +753,9 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self) /* The end of the video control interface descriptor. */ void const *end = _end_of_control_descriptor(vc); if (vc->std.bNumEndpoints != 0) { + /* Extend end to cover the standard endpoint and class-specific endpoint descriptors + * that follow wTotalLength */ + end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t); /* Find the notification endpoint descriptor. */ cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); TU_ASSERT(cur < end); @@ -786,6 +796,9 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t if (vc->std.bNumEndpoints != 0) { /* Support for 1 endpoint only. */ TU_VERIFY(1 == vc->std.bNumEndpoints); + /* Extend end to cover the standard endpoint and class-specific endpoint descriptors + * that follow wTotalLength */ + end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t); /* Find the notification endpoint descriptor. */ cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); TU_VERIFY(cur < end); -- cgit v1.3.1 From ce305af6534e098ee98ab2d8bc2229e2300dc2f0 Mon Sep 17 00:00:00 2001 From: YixingShen Date: Mon, 9 Mar 2026 22:13:59 +0800 Subject: revert _end_of_control_descriptor to Revision: 0a45308a296251e4376815b21f89f0038c75a60e --- src/class/video/video_device.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/class') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index d1c699940..bbcfe45d5 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -399,13 +399,7 @@ static void const* _find_desc_ep(void const *beg, void const *end) static inline void const* _end_of_control_descriptor(void const *desc) { tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)desc; - uint8_t const *end = (uint8_t const*)desc + vc->std.bLength - + tu_le16toh(vc->ctl.wTotalLength); - if (vc->std.bNumEndpoints) { - end += sizeof(tusb_desc_endpoint_t); // standard EP descriptor - end += 5; // class-specific EP descriptor (fixed 5 bytes per UVC spec) - } - return end; + return ((uint8_t const*) desc) + vc->std.bLength + tu_le16toh(vc->ctl.wTotalLength); } /** Find the first entity descriptor with the entity ID -- cgit v1.3.1 From 2e29388051b2d92d2d254e3ce5967bfec2b9720f Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 11 Mar 2026 15:14:09 +0700 Subject: refactor(usbd): replace `_usbd_ctrl_epbuf` with `usbd_get_ctrl_buf()` for cleaner abstraction and consistency across modules --- src/class/audio/audio_device.c | 2 +- src/class/dfu/dfu_device.c | 2 +- src/device/usbd_control.c | 18 ++++++++++++------ src/device/usbd_pvt.h | 6 +----- 4 files changed, 15 insertions(+), 13 deletions(-) (limited to 'src/class') diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 6cb05307b..995bf8a3e 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -430,7 +430,7 @@ static inline uint8_t* get_ctrl_buffer(void) { #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE return ctrl_buf; #else - return _usbd_ctrl_epbuf.buf; + return usbd_get_ctrl_buf(); #endif } diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 65332eae3..ee57621b8 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -75,7 +75,7 @@ static inline uint8_t* get_xfer_buffer(void) { #if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE return _transfer_buf; #else - return _usbd_ctrl_epbuf.buf; + return usbd_get_ctrl_buf(); #endif } diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 1064185d4..87593d4a7 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -59,7 +59,13 @@ typedef struct { static usbd_control_xfer_t _ctrl_xfer; -CFG_TUD_MEM_SECTION usbd_ctrl_epbuf_t _usbd_ctrl_epbuf; +CFG_TUD_MEM_SECTION static struct { + TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); +} _ctrl_epbuf; + +uint8_t* usbd_get_ctrl_buf(void) { + return _ctrl_epbuf.buf; +} //--------------------------------------------------------------------+ // Application API @@ -91,12 +97,12 @@ static bool data_stage_xact(uint8_t rhport) { if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { ep_addr = EDPT_CTRL_IN; - if (0u != xact_len && _ctrl_xfer.buffer != _usbd_ctrl_epbuf.buf) { - TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); + if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { + TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); } } - return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_epbuf.buf : NULL, xact_len, false); + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); } // Transmit data to/from the control endpoint. @@ -167,8 +173,8 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { TU_VERIFY(_ctrl_xfer.buffer); - if (_ctrl_xfer.buffer != _usbd_ctrl_epbuf.buf) { - memcpy(_ctrl_xfer.buffer, _usbd_ctrl_epbuf.buf, xferred_bytes); + if (_ctrl_xfer.buffer != _ctrl_epbuf.buf) { + memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes); } TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); } diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index bc9a737b2..5f11ea481 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -72,11 +72,7 @@ void usbd_int_set(bool enabled); void usbd_spin_lock(bool in_isr); void usbd_spin_unlock(bool in_isr); -typedef struct { - TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); -} usbd_ctrl_epbuf_t; - -extern usbd_ctrl_epbuf_t _usbd_ctrl_epbuf; +uint8_t* usbd_get_ctrl_buf(void); //--------------------------------------------------------------------+ // USBD Endpoint API -- cgit v1.3.1 From d74559ab70c7c4804ee8cf65d9c65d910af06870 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 11 Mar 2026 20:11:41 +0700 Subject: rename .rx_multiple_packet_transfer to .rx_need_zlp --- examples/dual/host_info_to_device_cdc/src/main.c | 2 +- src/class/cdc/cdc_device.c | 6 ++---- src/class/cdc/cdc_device.h | 26 +++++++++++++----------- src/class/vendor/vendor_device.c | 4 ++-- src/class/vendor/vendor_device.h | 11 ++++++---- src/common/tusb_private.h | 3 ++- 6 files changed, 28 insertions(+), 24 deletions(-) (limited to 'src/class') diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c index 7cf43aef3..fe2199a69 100644 --- a/examples/dual/host_info_to_device_cdc/src/main.c +++ b/examples/dual/host_info_to_device_cdc/src/main.c @@ -106,7 +106,7 @@ static void usb_device_init(void) { .speed = TUSB_SPEED_AUTO }; tusb_init(BOARD_TUD_RHPORT, &dev_init); - tud_cdc_configure_t cdc_cfg = TUD_CDC_CONFIGURE_DEFAULT(); + tud_cdc_configure_t cdc_cfg = CFG_TUD_CDC_CONFIGURE_DEFAULT(); cdc_cfg.tx_persistent = true; cdc_cfg.tx_overwritabe_if_not_connected = false; tud_cdc_configure(&cdc_cfg); diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index eca4d0ad6..2be3b8ade 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -116,7 +116,7 @@ TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; -static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT(); +static tud_cdc_configure_t _cdcd_cfg = CFG_TUD_CDC_CONFIGURE_DEFAULT(); TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) { for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) { @@ -347,7 +347,6 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream; - tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_EP_BUFSIZE); if (_cdcd_cfg.tx_persistent) { tu_edpt_stream_write_xfer(stream_tx); // flush pending data @@ -356,9 +355,8 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 } } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; - tu_edpt_stream_open(stream_rx, rhport, desc_ep, - _cdcd_cfg.rx_multiple_packet_transfer ? CFG_TUD_CDC_EP_BUFSIZE : tu_edpt_packet_size(desc_ep)); + _cdcd_cfg.rx_need_zlp ? CFG_TUD_CDC_EP_BUFSIZE : tu_edpt_packet_size(desc_ep)); if (!_cdcd_cfg.rx_persistent) { tu_edpt_stream_clear(stream_rx); } diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 2d3a81f60..64e58e5ec 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -64,16 +64,19 @@ typedef struct TU_ATTR_PACKED { bool rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect bool tx_persistent : 1; // keep tx fifo data even with reset or disconnect bool tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten - bool rx_multiple_packet_transfer : 1; // allow transfer more than one packet in a single transfer, increase throughput but requires host sending ZLP at the end of transfer + bool rx_need_zlp : 1; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. } tud_cdc_configure_t; TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct"); -#define TUD_CDC_CONFIGURE_DEFAULT() { \ - .rx_persistent = false, \ - .tx_persistent = false, \ - .tx_overwritabe_if_not_connected = true, \ - .rx_multiple_packet_transfer = false, \ -} +#ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT + #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \ + { \ + .rx_persistent = false, \ + .tx_persistent = false, \ + .tx_overwritabe_if_not_connected = true, \ + .rx_need_zlp = false \ + } +#endif // Configure CDC driver behavior bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg); @@ -86,13 +89,13 @@ bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg); // Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1 //--------------------------------------------------------------------+ -// Check if interface is ready +// Check if the interface is ready bool tud_cdc_n_ready(uint8_t itf); -// Check if terminal is connected to this port +// Check if the terminal is connected to this port bool tud_cdc_n_connected(uint8_t itf); -// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) +// Get the current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) uint8_t tud_cdc_n_get_line_state(uint8_t itf); // Get current line encoding: bit rate, stop bits parity etc .. @@ -138,10 +141,9 @@ uint32_t tud_cdc_n_write_flush(uint8_t itf); // Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation. uint32_t tud_cdc_n_write_available(uint8_t itf); -// Clear the transmit FIFO +// Clear the TX FIFO bool tud_cdc_n_write_clear(uint8_t itf); - #if CFG_TUD_CDC_NOTIFY bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg); diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index b2dd8f394..c7ad8bd8c 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -71,7 +71,7 @@ typedef struct { CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; #endif -static tud_vendor_configure_t _vendord_cfg = TUD_VENDOR_CONFIGURE_DEFAULT(); +static tud_vendor_configure_t _vendord_cfg = CFG_TUD_VENDOR_CONFIGURE_DEFAULT(); //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available @@ -307,7 +307,7 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc; TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); - uint16_t rx_xfer_len = _vendord_cfg.rx_multiple_packet_transfer ? CFG_TUD_VENDOR_EPSIZE : tu_edpt_packet_size(desc_ep); + uint16_t rx_xfer_len = _vendord_cfg.rx_need_zlp ? CFG_TUD_VENDOR_EPSIZE : tu_edpt_packet_size(desc_ep); #if CFG_TUD_VENDOR_TXRX_BUFFERED // open endpoint stream diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 594da19cb..54f3548c7 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -66,13 +66,16 @@ extern "C" { // Driver Configuration //--------------------------------------------------------------------+ typedef struct TU_ATTR_PACKED { - bool rx_multiple_packet_transfer : 1; // allow transfer more than one packet in a single transfer, increase throughput but requires host sending ZLP at the end of transfer + bool rx_need_zlp : 1; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. } tud_vendor_configure_t; TU_VERIFY_STATIC(sizeof(tud_vendor_configure_t) == 1, "size is not correct"); -#define TUD_VENDOR_CONFIGURE_DEFAULT() { \ - .rx_multiple_packet_transfer = false, \ -} +#ifndef CFG_TUD_VENDOR_CONFIGURE_DEFAULT + #define CFG_TUD_VENDOR_CONFIGURE_DEFAULT() \ + { \ + .rx_need_zlp = false, \ + } +#endif // Configure CDC driver behavior bool tud_vendor_configure(const tud_vendor_configure_t* driver_cfg); diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 543921553..91d213755 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -62,8 +62,9 @@ typedef struct { uint8_t hwid; // device: rhport, host: daddr bool is_host; // 1: host, 0: device uint8_t ep_addr; - uint16_t mps; + // 1 byte padding + uint16_t mps; uint16_t xfer_len; uint8_t *ep_buf; // set to NULL to use xfer_fifo when CFG_TUD_EDPT_DEDICATED_HWFIFO = 1 tu_fifo_t ff; -- cgit v1.3.1 From 78bbc7dc2e80daba79351bc37e11b13243fd6f8c Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 11:43:32 +0700 Subject: refactor(config): separate endpoint buffer sizes into RX and TX definitions for clarity and flexibility --- examples/device/cdc_dual_ports/src/tusb_config.h | 6 +-- examples/device/cdc_msc/src/tusb_config.h | 6 +-- examples/device/cdc_msc_freertos/src/tusb_config.h | 6 +-- examples/device/cdc_uac2/src/tusb_config.h | 6 +-- examples/device/printer_to_cdc/src/tusb_config.h | 6 ++- examples/dual/dynamic_switch/src/tusb_config.h | 3 +- .../dual/host_hid_to_device_cdc/src/tusb_config.h | 3 +- .../dual/host_info_to_device_cdc/src/tusb_config.h | 3 +- src/class/cdc/cdc_device.c | 14 +++--- src/class/cdc/cdc_device.h | 57 ++++++++++++---------- src/class/cdc/cdc_host.h | 8 +-- src/class/midi/midi_device.c | 6 +-- src/class/midi/midi_device.h | 17 +++++-- src/class/midi/midi_host.c | 4 +- src/class/midi/midi_host.h | 6 +-- src/class/printer/printer_device.c | 22 ++++----- src/class/printer/printer_device.h | 15 +++++- src/class/vendor/vendor_device.c | 18 +++---- src/class/vendor/vendor_device.h | 16 +++++- src/common/tusb_types.h | 4 ++ src/host/usbh.h | 3 +- test/fuzz/device/cdc/src/tusb_config.h | 3 +- test/fuzz/device/msc/src/tusb_config.h | 3 +- test/fuzz/device/net/src/tusb_config.h | 3 +- 24 files changed, 143 insertions(+), 95 deletions(-) (limited to 'src/class') diff --git a/examples/device/cdc_dual_ports/src/tusb_config.h b/examples/device/cdc_dual_ports/src/tusb_config.h index 710c01ee2..f8c36a90d 100644 --- a/examples/device/cdc_dual_ports/src/tusb_config.h +++ b/examples/device/cdc_dual_ports/src/tusb_config.h @@ -104,9 +104,9 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -// Leave it as default size (512 for HS, 64 for FS) unless your host application -// is able to send ZLP (Zero Length Packet) to terminate transfer ! -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index 3f2f05f20..c4f4374a2 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -104,9 +104,9 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -// Leave it as default size (512 for HS, 64 for FS) unless your host application -// is able to send ZLP (Zero Length Packet) to terminate transfer ! -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index 8277b1604..33342819e 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -111,9 +111,9 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -// Leave it as default size (512 for HS, 64 for FS) unless your host application -// is able to send ZLP (Zero Length Packet) to terminate transfer ! -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h index 5eb2e8f74..6724b83b3 100644 --- a/examples/device/cdc_uac2/src/tusb_config.h +++ b/examples/device/cdc_uac2/src/tusb_config.h @@ -160,9 +160,9 @@ extern "C" { #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -// Leave it as default size (512 for HS, 64 for FS) unless your host application -// is able to send ZLP (Zero Length Packet) to terminate transfer ! -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/examples/device/printer_to_cdc/src/tusb_config.h b/examples/device/printer_to_cdc/src/tusb_config.h index c38e8e1ee..d12313ce5 100644 --- a/examples/device/printer_to_cdc/src/tusb_config.h +++ b/examples/device/printer_to_cdc/src/tusb_config.h @@ -103,12 +103,14 @@ extern "C" { #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // Printer buffer sizes #define CFG_TUD_PRINTER_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_PRINTER_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -#define CFG_TUD_PRINTER_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_PRINTER_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_PRINTER_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/examples/dual/dynamic_switch/src/tusb_config.h b/examples/dual/dynamic_switch/src/tusb_config.h index af0d55f14..f9500f923 100644 --- a/examples/dual/dynamic_switch/src/tusb_config.h +++ b/examples/dual/dynamic_switch/src/tusb_config.h @@ -130,7 +130,8 @@ extern "C" { #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) //-------------------------------------------------------------------- // HOST CONFIGURATION diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h index 2843e0b83..0a7137d29 100644 --- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h @@ -115,7 +115,8 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) //-------------------------------------------------------------------- // HOST CONFIGURATION diff --git a/examples/dual/host_info_to_device_cdc/src/tusb_config.h b/examples/dual/host_info_to_device_cdc/src/tusb_config.h index 601c27dae..8f3ed6357 100644 --- a/examples/dual/host_info_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_info_to_device_cdc/src/tusb_config.h @@ -115,7 +115,8 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 256) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) //-------------------------------------------------------------------- // HOST CONFIGURATION diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 2be3b8ade..3f207462e 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -63,10 +63,10 @@ typedef struct { #define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding) // Skip local EP buffer if dedicated hw FIFO is supported - #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 +#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 typedef struct { - TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE); - TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE); + TUD_EPBUF_DEF(epout, CFG_TUD_CDC_RX_EPSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_CDC_TX_EPSIZE); #if CFG_TUD_CDC_NOTIFY TUD_EPBUF_TYPE_DEF(cdc_notify_msg_t, epnotify); @@ -74,7 +74,7 @@ typedef struct { } cdcd_epbuf_t; CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC]; - #endif +#endif //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available @@ -347,7 +347,7 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream; - tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_EP_BUFSIZE); + tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_TX_EPSIZE); if (_cdcd_cfg.tx_persistent) { tu_edpt_stream_write_xfer(stream_tx); // flush pending data } else { @@ -356,7 +356,7 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; tu_edpt_stream_open(stream_rx, rhport, desc_ep, - _cdcd_cfg.rx_need_zlp ? CFG_TUD_CDC_EP_BUFSIZE : tu_edpt_packet_size(desc_ep)); + _cdcd_cfg.rx_need_zlp ? CFG_TUD_CDC_RX_EPSIZE : tu_edpt_packet_size(desc_ep)); if (!_cdcd_cfg.rx_persistent) { tu_edpt_stream_clear(stream_rx); } @@ -511,7 +511,7 @@ 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) { tud_cdc_tx_complete_cb(itf); // invoke callback to possibly refill tx fifo diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 64e58e5ec..3baf84d00 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -29,6 +29,10 @@ #include "cdc.h" +#ifdef __cplusplus + extern "C" { +#endif + //--------------------------------------------------------------------+ // Class Driver Configuration //--------------------------------------------------------------------+ @@ -37,46 +41,49 @@ #endif #ifndef CFG_TUD_CDC_TX_BUFSIZE - #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + #define CFG_TUD_CDC_TX_BUFSIZE TUD_EPSIZE_BULK_MAX #endif #ifndef CFG_TUD_CDC_RX_BUFSIZE - #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + #define CFG_TUD_CDC_RX_BUFSIZE TUD_EPSIZE_BULK_MAX #endif -#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE) - #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name - #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE +// EP_BUFSIZE is separated to RX_EPSIZE and TX_EPSIZE +#ifndef CFG_TUD_CDC_RX_EPSIZE + #ifdef CFG_TUD_CDC_EP_BUFSIZE + #define CFG_TUD_CDC_RX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE + #else + #define CFG_TUD_CDC_RX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif #endif -#ifndef CFG_TUD_CDC_EP_BUFSIZE - #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#ifndef CFG_TUD_CDC_TX_EPSIZE + #ifdef CFG_TUD_CDC_EP_BUFSIZE + #define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE + #else + #define CFG_TUD_CDC_TX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif #endif -#ifdef __cplusplus - extern "C" { +#ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT + #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \ + { \ + .rx_persistent = false, \ + .tx_persistent = false, \ + .tx_overwritabe_if_not_connected = true, \ + .rx_need_zlp = false \ + } #endif //--------------------------------------------------------------------+ // Driver Configuration //--------------------------------------------------------------------+ -typedef struct TU_ATTR_PACKED { - bool rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect - bool tx_persistent : 1; // keep tx fifo data even with reset or disconnect - bool tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten - bool rx_need_zlp : 1; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. +typedef struct { + bool rx_persistent; // keep rx fifo data even with bus reset or disconnect + bool tx_persistent; // keep tx fifo data even with reset or disconnect + bool tx_overwritabe_if_not_connected; // if not connected, tx fifo can be overwritten + bool rx_need_zlp; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. } tud_cdc_configure_t; -TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct"); - -#ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT - #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \ - { \ - .rx_persistent = false, \ - .tx_persistent = false, \ - .tx_overwritabe_if_not_connected = true, \ - .rx_need_zlp = false \ - } -#endif // Configure CDC driver behavior bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg); diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 57919c7ff..1b1709b18 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -39,22 +39,22 @@ extern "C" { // RX FIFO size #ifndef CFG_TUH_CDC_RX_BUFSIZE - #define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif // RX Endpoint size #ifndef CFG_TUH_CDC_RX_EPSIZE - #define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MAX #endif // TX FIFO size #ifndef CFG_TUH_CDC_TX_BUFSIZE - #define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif // TX Endpoint size #ifndef CFG_TUH_CDC_TX_EPSIZE - #define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MAX #endif //--------------------------------------------------------------------+ diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index e5e0f52a5..de4ff5dd8 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -74,8 +74,8 @@ static midid_interface_t _midid_itf[CFG_TUD_MIDI]; #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 // Endpoint Transfer buffer: not used if dedicated hw FIFO is available typedef struct { - TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE); - TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_TX_EPSIZE); + TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_RX_EPSIZE); } midid_epbuf_t; CFG_TUD_MEM_SECTION static midid_epbuf_t _midid_epbuf[CFG_TUD_MIDI]; @@ -510,7 +510,7 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint1 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx; - tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_MIDI_EP_BUFSIZE); + tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_MIDI_TX_EPSIZE); tu_edpt_stream_clear(stream_tx); } else { tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx; diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h index b80ad544a..57eabec1f 100644 --- a/src/class/midi/midi_device.h +++ b/src/class/midi/midi_device.h @@ -34,13 +34,20 @@ // Class Driver Configuration //--------------------------------------------------------------------+ -#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE) - #warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name - #define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE +#ifndef CFG_TUD_MIDI_RX_EPSIZE + #ifdef CFG_TUD_MIDI_EP_BUFSIZE + #define CFG_TUD_MIDI_RX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE + #else + #define CFG_TUD_MIDI_RX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif #endif -#ifndef CFG_TUD_MIDI_EP_BUFSIZE - #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#ifndef CFG_TUD_MIDI_TX_EPSIZE + #ifdef CFG_TUD_MIDI_EP_BUFSIZE + #define CFG_TUD_MIDI_TX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE + #else + #define CFG_TUD_MIDI_TX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif #endif #ifdef __cplusplus diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index bef4d46bf..0feb5106d 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -83,8 +83,8 @@ typedef struct { }midih_interface_t; typedef struct { - TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS); - TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS); + TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MAX); + TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MAX); } midih_epbuf_t; static midih_interface_t _midi_host[CFG_TUH_MIDI]; diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h index 8a8dccab4..b9ab0130d 100644 --- a/src/class/midi/midi_host.h +++ b/src/class/midi/midi_host.h @@ -38,15 +38,15 @@ extern "C" { // Class Driver Configuration //--------------------------------------------------------------------+ #ifndef CFG_TUH_MIDI_RX_BUFSIZE - #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif #ifndef CFG_TUH_MIDI_TX_BUFSIZE - #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif #ifndef CFG_TUH_MIDI_EP_BUFSIZE - #define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS + #define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MAX #endif // Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index f5bb33795..d2dc9b163 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -53,8 +53,8 @@ typedef struct { #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 typedef struct { - TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); - TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); + TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_RX_EPSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_TX_EPSIZE); } printer_epbuf_t; CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; @@ -173,12 +173,10 @@ void printerd_init(void) { #endif tu_edpt_stream_init(&p->rx_stream, false, false, false, - p->rx_ff_buf, CFG_TUD_PRINTER_RX_BUFSIZE, - epout_buf, CFG_TUD_PRINTER_EP_BUFSIZE); + p->rx_ff_buf, CFG_TUD_PRINTER_RX_BUFSIZE, epout_buf); tu_edpt_stream_init(&p->tx_stream, false, true, true, - p->tx_ff_buf, CFG_TUD_PRINTER_TX_BUFSIZE, - epin_buf, CFG_TUD_PRINTER_EP_BUFSIZE); + p->tx_ff_buf, CFG_TUD_PRINTER_TX_BUFSIZE, epin_buf); } } @@ -226,12 +224,14 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { - tu_edpt_stream_open(&p->tx_stream, rhport, desc_ep); - tu_edpt_stream_clear(&p->tx_stream); + tu_edpt_stream_t *stream_tx = &p->tx_stream; + tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_PRINTER_TX_EPSIZE); + tu_edpt_stream_clear(stream_tx); } else { - tu_edpt_stream_open(&p->rx_stream, rhport, desc_ep); - tu_edpt_stream_clear(&p->rx_stream); - TU_ASSERT(tu_edpt_stream_read_xfer(&p->rx_stream) > 0, 0); + tu_edpt_stream_t *stream_rx = &p->rx_stream; + tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep)); + tu_edpt_stream_clear(stream_rx); + TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); } drv_len += sizeof(tusb_desc_endpoint_t); diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h index a6b7052cb..afde1f022 100644 --- a/src/class/printer/printer_device.h +++ b/src/class/printer/printer_device.h @@ -27,12 +27,23 @@ #ifndef TUSB_PRINTER_DEVICE_H_ #define TUSB_PRINTER_DEVICE_H_ -#include "printer.h" - #ifdef __cplusplus extern "C" { #endif +#include "printer.h" + +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_PRINTER_RX_EPSIZE + #define CFG_TUD_PRINTER_RX_EPSIZE TUD_EPSIZE_BULK_MAX +#endif + +#ifndef CFG_TUD_PRINTER_TX_EPSIZE + #define CFG_TUD_PRINTER_TX_EPSIZE TUD_EPSIZE_BULK_MAX +#endif + //--------------------------------------------------------------------+ // Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1 //--------------------------------------------------------------------+ diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index c7ad8bd8c..c55cad627 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -61,15 +61,15 @@ typedef struct { static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR]; - // Skip local EP buffer if dedicated hw FIFO is supported or no fifo mode - #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || !CFG_TUD_VENDOR_TXRX_BUFFERED +// Skip local EP buffer if dedicated hw FIFO is supported or no fifo mode +#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || !CFG_TUD_VENDOR_TXRX_BUFFERED typedef struct { - TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE); - TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE); + TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_RX_EPSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_TX_EPSIZE); } vendord_epbuf_t; CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; - #endif +#endif static tud_vendor_configure_t _vendord_cfg = CFG_TUD_VENDOR_CONFIGURE_DEFAULT(); @@ -167,7 +167,7 @@ uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) { #else // non-fifo mode: direct transfer TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_in), 0); - const uint32_t xact_len = tu_min32(bufsize, CFG_TUD_VENDOR_EPSIZE); + const uint32_t xact_len = tu_min32(bufsize, CFG_TUD_VENDOR_TX_EPSIZE); memcpy(_vendord_epbuf[idx].epin, buffer, xact_len); TU_ASSERT(usbd_edpt_xfer(p_itf->rhport, p_itf->ep_in, _vendord_epbuf[idx].epin, (uint16_t)xact_len, false), 0); return xact_len; @@ -184,7 +184,7 @@ uint32_t tud_vendor_n_write_available(uint8_t idx) { #else // Non-FIFO mode TU_VERIFY(p_itf->ep_in > 0, 0); // must be opened - return usbd_edpt_busy(p_itf->rhport, p_itf->ep_in) ? 0 : CFG_TUD_VENDOR_EPSIZE; + return usbd_edpt_busy(p_itf->rhport, p_itf->ep_in) ? 0 : CFG_TUD_VENDOR_TX_EPSIZE; #endif } @@ -307,13 +307,13 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc; TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); - uint16_t rx_xfer_len = _vendord_cfg.rx_need_zlp ? CFG_TUD_VENDOR_EPSIZE : tu_edpt_packet_size(desc_ep); + uint16_t rx_xfer_len = _vendord_cfg.rx_need_zlp ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep); #if CFG_TUD_VENDOR_TXRX_BUFFERED // open endpoint stream if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *tx_stream = &p_vendor->tx_stream; - tu_edpt_stream_open(tx_stream, rhport, desc_ep, CFG_TUD_VENDOR_EPSIZE); + tu_edpt_stream_open(tx_stream, rhport, desc_ep, CFG_TUD_VENDOR_TX_EPSIZE); tu_edpt_stream_write_xfer(tx_stream); // flush pending data } else { tu_edpt_stream_t *rx_stream = &p_vendor->rx_stream; diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 54f3548c7..491a7d7fb 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -36,8 +36,20 @@ extern "C" { //--------------------------------------------------------------------+ // Configuration //--------------------------------------------------------------------+ -#ifndef CFG_TUD_VENDOR_EPSIZE - #define CFG_TUD_VENDOR_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#ifndef CFG_TUD_VENDOR_RX_EPSIZE + #ifdef CFG_TUD_VENDOR_EPSIZE + #define CFG_TUD_VENDOR_RX_EPSIZE CFG_TUD_VENDOR_EPSIZE + #else + #define CFG_TUD_VENDOR_RX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif +#endif + +#ifndef CFG_TUD_VENDOR_TX_EPSIZE + #ifdef CFG_TUD_VENDOR_EPSIZE + #define CFG_TUD_VENDOR_TX_EPSIZE CFG_TUD_VENDOR_EPSIZE + #else + #define CFG_TUD_VENDOR_TX_EPSIZE TUD_EPSIZE_BULK_MAX + #endif #endif // RX FIFO can be disabled by setting this value to 0 diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 8a48a0f04..a18f9feb7 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -115,6 +115,10 @@ enum { TUSB_EPSIZE_ISO_HS_MAX = 1024, }; +// Endpoint Bulk size depending on host/device max speed +#define TUD_EPSIZE_BULK_MAX (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define TUH_EPSIZE_BULK_MAX (TUH_OPT_HIGH_SPEED ? 512 : 64) + /// Isochronous Endpoint Attributes typedef enum { TUSB_ISO_EP_ATT_NO_SYNC = 0x00, diff --git a/src/host/usbh.h b/src/host/usbh.h index 143d36f8c..7ddec35b7 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -41,8 +41,7 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -// Endpoint Bulk size depending on host mx speed -#define TUH_EPSIZE_BULK_MPS (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS) + // forward declaration struct tuh_xfer_s; diff --git a/test/fuzz/device/cdc/src/tusb_config.h b/test/fuzz/device/cdc/src/tusb_config.h index 76f44619e..b4b45d798 100644 --- a/test/fuzz/device/cdc/src/tusb_config.h +++ b/test/fuzz/device/cdc/src/tusb_config.h @@ -102,7 +102,8 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 diff --git a/test/fuzz/device/msc/src/tusb_config.h b/test/fuzz/device/msc/src/tusb_config.h index abd8cd4ce..3400c141d 100644 --- a/test/fuzz/device/msc/src/tusb_config.h +++ b/test/fuzz/device/msc/src/tusb_config.h @@ -102,7 +102,8 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 diff --git a/test/fuzz/device/net/src/tusb_config.h b/test/fuzz/device/net/src/tusb_config.h index 4fe98e043..46fc10cdb 100644 --- a/test/fuzz/device/net/src/tusb_config.h +++ b/test/fuzz/device/net/src/tusb_config.h @@ -107,7 +107,8 @@ #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 -- cgit v1.3.1 From aea4f6046e9a07d5f169de9fcc31bca2b667ea45 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 12:16:57 +0700 Subject: refactor(vendor/cdc): add CFG_TUD_CDC_RX_NEED_ZLP and CFG_TUD_VENDOR_RX_NEED_ZLP --- examples/device/cdc_dual_ports/src/tusb_config.h | 4 ++-- examples/device/cdc_msc/src/tusb_config.h | 4 ++-- examples/device/cdc_msc_freertos/src/tusb_config.h | 4 ++-- examples/device/cdc_uac2/src/tusb_config.h | 4 ++-- examples/device/printer_to_cdc/src/tusb_config.h | 3 ++- examples/dual/dynamic_switch/src/tusb_config.h | 3 ++- .../dual/host_hid_to_device_cdc/src/tusb_config.h | 3 ++- .../dual/host_info_to_device_cdc/src/tusb_config.h | 3 ++- src/class/cdc/cdc_device.c | 2 +- src/class/cdc/cdc_device.h | 7 +++++-- src/class/vendor/vendor_device.c | 13 +------------ src/class/vendor/vendor_device.h | 19 +++---------------- test/fuzz/device/cdc/src/tusb_config.h | 3 ++- test/fuzz/device/msc/src/tusb_config.h | 3 ++- test/fuzz/device/net/src/tusb_config.h | 3 ++- 15 files changed, 32 insertions(+), 46 deletions(-) (limited to 'src/class') diff --git a/examples/device/cdc_dual_ports/src/tusb_config.h b/examples/device/cdc_dual_ports/src/tusb_config.h index f8c36a90d..633a3faea 100644 --- a/examples/device/cdc_dual_ports/src/tusb_config.h +++ b/examples/device/cdc_dual_ports/src/tusb_config.h @@ -103,8 +103,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster -// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index c4f4374a2..f0709d8fb 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -103,8 +103,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster -// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index 33342819e..a78f2ea05 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -110,8 +110,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster -// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h index 6724b83b3..358ff6747 100644 --- a/examples/device/cdc_uac2/src/tusb_config.h +++ b/examples/device/cdc_uac2/src/tusb_config.h @@ -159,8 +159,8 @@ extern "C" { #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster -// Only increase RX_EPSIZE if your host driver/application support zero-length packet (ZLP) +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/printer_to_cdc/src/tusb_config.h b/examples/device/printer_to_cdc/src/tusb_config.h index d12313ce5..74b96c8f6 100644 --- a/examples/device/printer_to_cdc/src/tusb_config.h +++ b/examples/device/printer_to_cdc/src/tusb_config.h @@ -102,7 +102,8 @@ extern "C" { #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/dual/dynamic_switch/src/tusb_config.h b/examples/dual/dynamic_switch/src/tusb_config.h index f9500f923..f3e016305 100644 --- a/examples/dual/dynamic_switch/src/tusb_config.h +++ b/examples/dual/dynamic_switch/src/tusb_config.h @@ -129,7 +129,8 @@ extern "C" { #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h index 0a7137d29..fb2a401b5 100644 --- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h @@ -114,7 +114,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/dual/host_info_to_device_cdc/src/tusb_config.h b/examples/dual/host_info_to_device_cdc/src/tusb_config.h index 8f3ed6357..99e9315a4 100644 --- a/examples/dual/host_info_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_info_to_device_cdc/src/tusb_config.h @@ -114,7 +114,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 256) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 3f207462e..c7547c92b 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -356,7 +356,7 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; tu_edpt_stream_open(stream_rx, rhport, desc_ep, - _cdcd_cfg.rx_need_zlp ? CFG_TUD_CDC_RX_EPSIZE : tu_edpt_packet_size(desc_ep)); + CFG_TUD_CDC_RX_NEED_ZLP ? CFG_TUD_CDC_RX_EPSIZE : tu_edpt_packet_size(desc_ep)); if (!_cdcd_cfg.rx_persistent) { tu_edpt_stream_clear(stream_rx); } diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 3baf84d00..0348bd2ec 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -65,13 +65,17 @@ #endif #endif +// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP. +#ifndef CFG_TUD_CDC_RX_NEED_ZLP + #define CFG_TUD_CDC_RX_NEED_ZLP 0 +#endif + #ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \ { \ .rx_persistent = false, \ .tx_persistent = false, \ .tx_overwritabe_if_not_connected = true, \ - .rx_need_zlp = false \ } #endif @@ -82,7 +86,6 @@ typedef struct { bool rx_persistent; // keep rx fifo data even with bus reset or disconnect bool tx_persistent; // keep tx fifo data even with reset or disconnect bool tx_overwritabe_if_not_connected; // if not connected, tx fifo can be overwritten - bool rx_need_zlp; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. } tud_cdc_configure_t; // Configure CDC driver behavior diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index c55cad627..e1017ba48 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -71,8 +71,6 @@ typedef struct { CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; #endif -static tud_vendor_configure_t _vendord_cfg = CFG_TUD_VENDOR_CONFIGURE_DEFAULT(); - //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ @@ -87,15 +85,6 @@ TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) { (void) sent_bytes; } -//-------------------------------------------------------------------- -// Application API -//-------------------------------------------------------------------- -bool tud_vendor_configure(const tud_vendor_configure_t* driver_cfg) { - TU_VERIFY(driver_cfg != NULL); - _vendord_cfg = *driver_cfg; - return true; -} - bool tud_vendor_n_mounted(uint8_t idx) { TU_VERIFY(idx < CFG_TUD_VENDOR); vendord_interface_t *p_itf = &_vendord_itf[idx]; @@ -307,7 +296,7 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc; TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); - uint16_t rx_xfer_len = _vendord_cfg.rx_need_zlp ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep); + uint16_t rx_xfer_len = CFG_TUD_VENDOR_RX_NEED_ZLP ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep); #if CFG_TUD_VENDOR_TXRX_BUFFERED // open endpoint stream diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 491a7d7fb..28accc698 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -74,24 +74,11 @@ extern "C" { #define CFG_TUD_VENDOR_RX_MANUAL_XFER 0 #endif -//--------------------------------------------------------------------+ -// Driver Configuration -//--------------------------------------------------------------------+ -typedef struct TU_ATTR_PACKED { - bool rx_need_zlp : 1; // requires host support ZLP, allow transfer more than one packet in a single transfer, better throughput. -} tud_vendor_configure_t; -TU_VERIFY_STATIC(sizeof(tud_vendor_configure_t) == 1, "size is not correct"); - -#ifndef CFG_TUD_VENDOR_CONFIGURE_DEFAULT - #define CFG_TUD_VENDOR_CONFIGURE_DEFAULT() \ - { \ - .rx_need_zlp = false, \ - } +// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP. +#ifndef CFG_TUD_VENDOR_RX_NEED_ZLP + #define CFG_TUD_VENDOR_RX_NEED_ZLP 0 #endif -// Configure CDC driver behavior -bool tud_vendor_configure(const tud_vendor_configure_t* driver_cfg); - //--------------------------------------------------------------------+ // Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1 //--------------------------------------------------------------------+ diff --git a/test/fuzz/device/cdc/src/tusb_config.h b/test/fuzz/device/cdc/src/tusb_config.h index b4b45d798..14b7b627d 100644 --- a/test/fuzz/device/cdc/src/tusb_config.h +++ b/test/fuzz/device/cdc/src/tusb_config.h @@ -101,7 +101,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/test/fuzz/device/msc/src/tusb_config.h b/test/fuzz/device/msc/src/tusb_config.h index 3400c141d..7a4a24fb8 100644 --- a/test/fuzz/device/msc/src/tusb_config.h +++ b/test/fuzz/device/msc/src/tusb_config.h @@ -101,7 +101,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/test/fuzz/device/net/src/tusb_config.h b/test/fuzz/device/net/src/tusb_config.h index 46fc10cdb..de45e9ead 100644 --- a/test/fuzz/device/net/src/tusb_config.h +++ b/test/fuzz/device/net/src/tusb_config.h @@ -106,7 +106,8 @@ #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -// CDC Endpoint transfer buffer size, more is faster +// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster. +// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support #define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) -- cgit v1.3.1 From 9e5345e70228153ab4efff6f6666c4e54471a4aa Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 14:32:37 +0700 Subject: correct default value for CFG_TUD_VENDOR_TX/RX_BUFSIZE --- src/class/vendor/vendor_device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/class') diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 28accc698..cdca9fd15 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -54,12 +54,12 @@ extern "C" { // 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 TUD_EPSIZE_BULK_MAX #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 TUD_EPSIZE_BULK_MAX #endif // Vendor is buffered (FIFO mode) if both TX and RX buffers are configured -- cgit v1.3.1 From 222af862aa2b3898980e3ed8f28e70fe6c9a7ee9 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 15:22:52 +0700 Subject: refactor(cdc): remove runtime CDC driver configuration in favor of compile-time macros for simplicity and reduced complexity --- examples/dual/host_info_to_device_cdc/src/main.c | 4 --- src/class/cdc/cdc_device.c | 44 +++++++++++------------- src/class/cdc/cdc_device.h | 34 +++++++----------- 3 files changed, 32 insertions(+), 50 deletions(-) (limited to 'src/class') diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c index fe2199a69..cf3430464 100644 --- a/examples/dual/host_info_to_device_cdc/src/main.c +++ b/examples/dual/host_info_to_device_cdc/src/main.c @@ -106,10 +106,6 @@ static void usb_device_init(void) { .speed = TUSB_SPEED_AUTO }; tusb_init(BOARD_TUD_RHPORT, &dev_init); - tud_cdc_configure_t cdc_cfg = CFG_TUD_CDC_CONFIGURE_DEFAULT(); - cdc_cfg.tx_persistent = true; - cdc_cfg.tx_overwritabe_if_not_connected = false; - tud_cdc_configure(&cdc_cfg); board_init_after_tusb(); } diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index c7547c92b..60fc38cab 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -116,7 +116,6 @@ TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; -static tud_cdc_configure_t _cdcd_cfg = CFG_TUD_CDC_CONFIGURE_DEFAULT(); TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) { for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) { @@ -132,12 +131,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) { //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ -bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) { - TU_VERIFY(driver_cfg != NULL); - _cdcd_cfg = *driver_cfg; - return true; -} - bool tud_cdc_n_ready(uint8_t itf) { TU_VERIFY(itf < CFG_TUD_CDC); TU_VERIFY(tud_ready()); @@ -272,7 +265,7 @@ void cdcd_init(void) { // TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not // know if data is actually polled by terminal. This way the most current data is prioritized. // Default: is overwritable - tu_edpt_stream_init(&p_cdc->tx_stream, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected, p_cdc->tx_ff_buf, + tu_edpt_stream_init(&p_cdc->tx_stream, false, true, CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, epin_buf); } } @@ -293,7 +286,7 @@ void cdcd_reset(uint8_t rhport) { cdcd_interface_t* p_cdc = &_cdcd_itf[i]; tu_memclr(p_cdc, ITF_MEM_RESET_SIZE); - tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, _cdcd_cfg.tx_overwritabe_if_not_connected); // back to default + tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED); // back to default tu_edpt_stream_close(&p_cdc->rx_stream); tu_edpt_stream_close(&p_cdc->tx_stream); } @@ -348,18 +341,21 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream; tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_TX_EPSIZE); - if (_cdcd_cfg.tx_persistent) { - tu_edpt_stream_write_xfer(stream_tx); // flush pending data - } else { - tu_edpt_stream_clear(stream_tx); - } + #if CFG_TUD_CDC_TX_PERSISTENT + tu_edpt_stream_write_xfer(stream_tx); // flush pending data + #else + tu_edpt_stream_clear(stream_tx); + #endif } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; - tu_edpt_stream_open(stream_rx, rhport, desc_ep, - CFG_TUD_CDC_RX_NEED_ZLP ? CFG_TUD_CDC_RX_EPSIZE : tu_edpt_packet_size(desc_ep)); - if (!_cdcd_cfg.rx_persistent) { - tu_edpt_stream_clear(stream_rx); - } + #if CFG_TUD_CDC_RX_NEED_ZLP + tu_edpt_stream_open(stream_rx, rhport, desc_ep, CFG_TUD_CDC_RX_EPSIZE); + #else + tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep)); + #endif + #if !CFG_TUD_CDC_RX_PERSISTENT + tu_edpt_stream_clear(stream_rx); + #endif TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare for incoming data } } @@ -424,11 +420,11 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ p_cdc->line_state = (uint8_t) request->wValue; // If enabled: fifo overwriting is disabled if DTR bit is set and vice versa - if (_cdcd_cfg.tx_overwritabe_if_not_connected) { - tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, !dtr); - } else { - tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, false); - } + #if CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED + tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, !dtr); + #else + tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, false); + #endif TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); tud_cdc_line_state_cb(itf, dtr, rts); // invoke callback diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 0348bd2ec..e44d425b9 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -70,30 +70,20 @@ #define CFG_TUD_CDC_RX_NEED_ZLP 0 #endif -#ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT - #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \ - { \ - .rx_persistent = false, \ - .tx_persistent = false, \ - .tx_overwritabe_if_not_connected = true, \ - } +// Keep rx fifo data even with bus reset or disconnect +#ifndef CFG_TUD_CDC_RX_PERSISTENT + #define CFG_TUD_CDC_RX_PERSISTENT 0 #endif -//--------------------------------------------------------------------+ -// Driver Configuration -//--------------------------------------------------------------------+ -typedef struct { - bool rx_persistent; // keep rx fifo data even with bus reset or disconnect - bool tx_persistent; // keep tx fifo data even with reset or disconnect - bool tx_overwritabe_if_not_connected; // if not connected, tx fifo can be overwritten -} tud_cdc_configure_t; - -// Configure CDC driver behavior -bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg); - -// Backward compatible -#define tud_cdc_configure_fifo_t tud_cdc_configure_t -#define tud_cdc_configure_fifo tud_cdc_configure +// Keep tx fifo data even with bus reset or disconnect +#ifndef CFG_TUD_CDC_TX_PERSISTENT + #define CFG_TUD_CDC_TX_PERSISTENT 0 +#endif + +// If not connected, tx fifo can be overwritten +#ifndef CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED + #define CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED 1 +#endif //--------------------------------------------------------------------+ // Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1 -- cgit v1.3.1 From cd60008e8921a9c1d1d98c44e67c4fd1ceca075c Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 15:56:48 +0700 Subject: clean up --- src/class/cdc/cdc_device.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'src/class') diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 60fc38cab..c499756b9 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -341,21 +341,26 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16 if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream; tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_TX_EPSIZE); - #if CFG_TUD_CDC_TX_PERSISTENT + + #if CFG_TUD_CDC_TX_PERSISTENT tu_edpt_stream_write_xfer(stream_tx); // flush pending data - #else + #else tu_edpt_stream_clear(stream_tx); - #endif + #endif } else { tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream; - #if CFG_TUD_CDC_RX_NEED_ZLP - tu_edpt_stream_open(stream_rx, rhport, desc_ep, CFG_TUD_CDC_RX_EPSIZE); - #else - tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep)); - #endif - #if !CFG_TUD_CDC_RX_PERSISTENT + #if CFG_TUD_CDC_RX_NEED_ZLP + const uint16_t xfer_len = CFG_TUD_CDC_RX_EPSIZE; + #else + const uint16_t xfer_len = tu_edpt_packet_size(desc_ep); + #endif + + tu_edpt_stream_open(stream_rx, rhport, desc_ep, xfer_len); + + #if !CFG_TUD_CDC_RX_PERSISTENT tu_edpt_stream_clear(stream_rx); - #endif + #endif + TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare for incoming data } } @@ -420,12 +425,13 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ p_cdc->line_state = (uint8_t) request->wValue; // If enabled: fifo overwriting is disabled if DTR bit is set and vice versa - #if CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED - tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, !dtr); - #else - tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, false); - #endif + #if CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED + const bool is_overwritable = !dtr; + #else + const bool is_overwritable = false; + #endif + tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, is_overwritable); TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); tud_cdc_line_state_cb(itf, dtr, rts); // invoke callback } else { -- cgit v1.3.1 From 0e23ccd7df3e8e4119f4dd29236314fe1aed0ae5 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Mar 2026 16:52:27 +0700 Subject: refactor(cdc): remove deprecated runtime CDC configuration, add backward compatibility with no-op macros --- src/class/cdc/cdc_device.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/class') diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index e44d425b9..9ac6bc58a 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -85,6 +85,18 @@ #define CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED 1 #endif +// Backward compatible: tud_cdc_configure_t and tud_cdc_configure() are no longer used. +// Configuration is now done via compile-time macros above. +typedef struct { + bool rx_persistent; + bool tx_persistent; + bool tx_overwritabe_if_not_connected; +} tud_cdc_configure_t; + +#define tud_cdc_configure(_cfg) ((void)(_cfg)) +#define tud_cdc_configure_fifo_t tud_cdc_configure_t +#define tud_cdc_configure_fifo(_cfg) ((void)(_cfg)) + //--------------------------------------------------------------------+ // Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1 //--------------------------------------------------------------------+ -- cgit v1.3.1 From c06dc871d651ddaf756afdc59ed148c4b1af314d Mon Sep 17 00:00:00 2001 From: Zixun LI Date: Thu, 12 Mar 2026 10:52:51 +0100 Subject: reset notification state to speed change Signed-off-by: Zixun LI --- src/class/net/ncm_device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/class') diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 3e6891ed7..405e4467b 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -808,8 +808,8 @@ void tud_network_link_state(uint8_t rhport, bool is_up) { return; } - // Reset notification state to send link state update - ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED; + // Reset notification state to send speed change notification first, then link state notification + ncm_interface.notification_xmit_state = NOTIFICATION_SPEED; // Trigger notification transmission notification_xmit(rhport, false); @@ -978,7 +978,7 @@ bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t notification_xmit(rhport, false); } else { // Reset notification state to send link state update when interface is re-activated - ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED; + ncm_interface.notification_xmit_state = NOTIFICATION_SPEED; } tud_control_status(rhport, request); } break; -- cgit v1.3.1 From cca3e7e346d67554597ca5706fc9266c4767266e Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 17 Mar 2026 11:28:46 +0700 Subject: fix midi host issue 3544 --- src/class/midi/midi_host.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/class') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 0feb5106d..8d59dfc66 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -57,7 +57,7 @@ typedef struct { uint8_t daddr; uint8_t bInterfaceNumber; // interface number of MIDI streaming uint8_t iInterface; - uint8_t itf_count; // number of interface including Audio Control + MIDI streaming + uint8_t itf_count; // number of interfaces including Audio Control + MIDI streaming uint8_t rx_cable_count; // IN endpoint CS descriptor bNumEmbMIDIJack value uint8_t tx_cable_count; // OUT endpoint CS descriptor bNumEmbMIDIJack value @@ -249,10 +249,12 @@ uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_ p_midi->itf_count++; desc_cb.desc_midi = desc_itf; - p_desc = tu_desc_next(p_desc); // next to CS Header - bool found_new_interface = false; - while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) { + do { + p_desc = tu_desc_next(p_desc); + if (!tu_desc_in_bounds(p_desc, desc_end)) { + break; + } switch (tu_desc_type(p_desc)) { case TUSB_DESC_INTERFACE: found_new_interface = true; @@ -314,8 +316,8 @@ uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_ default: break; // skip unknown descriptor } - p_desc = tu_desc_next(p_desc); - } + } while (!found_new_interface); + desc_cb.desc_midi_total_len = (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_start); p_midi->daddr = dev_addr; @@ -332,7 +334,7 @@ bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) { const tuh_midi_mount_cb_t mount_cb_data = { .daddr = dev_addr, - .bInterfaceNumber = itf_num, + .bInterfaceNumber = p_midi->bInterfaceNumber, .rx_cable_count = p_midi->rx_cable_count, .tx_cable_count = p_midi->tx_cable_count, }; -- cgit v1.3.1 From 28e14abdc7b160ff5cb57d08f42493d5a30002c8 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 17 Mar 2026 15:50:12 +0700 Subject: CFG_TUH_HID_SET_PROTOCOL_ON_ENUM option to skip set_protocol on enum, default is 1 --- src/class/hid/hid_host.c | 24 +++++++++++++----------- src/class/hid/hid_host.h | 11 ++++++----- 2 files changed, 19 insertions(+), 16 deletions(-) (limited to 'src/class') diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 98f9bf80b..b1d487c75 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -240,7 +240,7 @@ void tuh_hid_set_default_protocol(uint8_t protocol) { _hidh_default_protocol = protocol; } -static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, +static bool hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol); @@ -272,7 +272,7 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) { hidh_interface_t* p_hid = get_hid_itf(daddr, idx); TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE); - return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0); + return hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0); } static void get_report_complete(tuh_xfer_t* xfer) { @@ -359,7 +359,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r return tuh_control_xfer(&xfer); } -static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, +static bool hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { // SET IDLE request, device can stall if not support this request TU_LOG_DRV("HID Set Idle \r\n"); @@ -623,25 +623,27 @@ static void process_set_config(tuh_xfer_t* xfer) { switch (state) { case CONFG_SET_IDLE: { - // Idle rate = 0 mean only report when there is changes + // Idle rate = 0 mean only report when there are changes const uint16_t idle_rate = 0; const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; - _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); + hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); break; } case CONFIG_SET_PROTOCOL: - _hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC); + #if CFG_TUH_HID_SET_PROTOCOL_ON_ENUM + hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC); break; + #else + TU_ATTR_FALLTHROUGH; + #endif case CONFIG_GET_REPORT_DESC: // Get Report Descriptor if possible - // using usbh enumeration buffer since report descriptor can be very long + // using usbh enumeration buffer since the report descriptor can be very long if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) { TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len); - - // Driver is mounted without report descriptor config_driver_mount_complete(daddr, idx, NULL, 0); } else { tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, @@ -651,8 +653,8 @@ static void process_set_config(tuh_xfer_t* xfer) { break; case CONFIG_COMPLETE: { - uint8_t const* desc_report = usbh_get_enum_buf(); - uint16_t const desc_len = tu_le16toh(xfer->setup->wLength); + const uint8_t *desc_report = usbh_get_enum_buf(); + const uint16_t desc_len = tu_le16toh(xfer->setup->wLength); config_driver_mount_complete(daddr, idx, desc_report, desc_len); break; diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index d7a415485..922848fc2 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -36,7 +36,6 @@ extern "C" { //--------------------------------------------------------------------+ // Class Driver Configuration //--------------------------------------------------------------------+ - // TODO Highspeed interrupt can be up to 512 bytes #ifndef CFG_TUH_HID_EPIN_BUFSIZE #define CFG_TUH_HID_EPIN_BUFSIZE 64 @@ -46,7 +45,13 @@ extern "C" { #define CFG_TUH_HID_EPOUT_BUFSIZE 64 #endif +#ifndef CFG_TUH_HID_SET_PROTOCOL_ON_ENUM + #define CFG_TUH_HID_SET_PROTOCOL_ON_ENUM 1 +#endif +//--------------------------------------------------------------------+ +// Interface API +//--------------------------------------------------------------------+ typedef struct { uint8_t report_id; uint8_t usage; @@ -57,10 +62,6 @@ typedef struct { // uint8_t out_len; // length of OUT report } tuh_hid_report_info_t; -//--------------------------------------------------------------------+ -// Interface API -//--------------------------------------------------------------------+ - // Get the total number of mounted HID interfaces of a device uint8_t tuh_hid_itf_get_count(uint8_t dev_addr); -- cgit v1.3.1 From a46c863cc0adae61b28f00ad82a4ca6f53052a81 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 18 Mar 2026 12:57:56 +0700 Subject: Update HID host to correctly handle protocol mode initialization and CONFIG_GET_REPORT_DESC response --- src/class/hid/hid_host.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/class') diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index b1d487c75..7935b84d3 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -566,8 +566,8 @@ uint16_t hidh_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *d // Use offsetof to avoid pointer to the odd/misaligned address p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*)desc_hid + offsetof(tusb_hid_descriptor_hid_t, wReportLength)); - // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config - p_hid->protocol_mode = _hidh_default_protocol; + // Per HID Specs: default is Report protocol + p_hid->protocol_mode = HID_PROTOCOL_REPORT; if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) { p_hid->itf_protocol = desc_itf->bInterfaceProtocol; } @@ -632,14 +632,17 @@ static void process_set_config(tuh_xfer_t* xfer) { } case CONFIG_SET_PROTOCOL: - #if CFG_TUH_HID_SET_PROTOCOL_ON_ENUM + #if CFG_TUH_HID_SET_PROTOCOL_ON_ENUM hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC); break; - #else + #else TU_ATTR_FALLTHROUGH; - #endif + #endif case CONFIG_GET_REPORT_DESC: + if (xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL && xfer->result == XFER_RESULT_SUCCESS) { + p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); + } // Get Report Descriptor if possible // using usbh enumeration buffer since the report descriptor can be very long if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) { -- cgit v1.3.1 From 88196051e8bd89b282fda8d395f4c333425a91b1 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 18 Mar 2026 19:04:13 +0700 Subject: fix bugs in fsdev hcd, dcd and midi host stream write - hcd_edpt_clear_stall: use ep_addr instead of hardcoded 0 (control endpoint) - hcd: add TUP_USBIP_FSDEV_DRD define for MCUs with host support (C0, G0, H5, U3, U5) and use it in hcd compile guard instead of enumerating MCUs - dcd_edpt_close_all: use FSDEV_EP_COUNT instead of CFG_TUD_ENDPPOINT_MAX for PMA btable offset to match handle_bus_reset - midi host tuh_midi_stream_write: add missing cable_num to system messages, SysEx, and real-time MIDI packets. Add 0xF mask for SysEx CIN checks. Aligns with midi_device.c tud_midi_n_stream_write implementation. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/class/midi/midi_host.c | 11 ++++++----- src/common/tusb_mcu.h | 5 +++++ src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 2 +- src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 7 ++----- 4 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/class') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 8d59dfc66..a53546404 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -461,7 +461,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) { // real-time messages need to be sent right away midi_driver_stream_t streamrt; - streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; + streamrt.buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE); streamrt.buffer[1] = data; streamrt.index = 2; streamrt.total = 2; @@ -476,9 +476,9 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu stream->buffer[1] = data; // Check to see if we're still in a SysEx transmit. - if (stream->buffer[0] == MIDI_CIN_SYSEX_START) { + if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START) { if (data == MIDI_STATUS_SYSEX_END) { - stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; + stream->buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE); stream->total = 2; } else { stream->total = 4; @@ -506,6 +506,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; stream->total = 2; } + stream->buffer[0] |= (uint8_t)(cable_num << 4); } else { // Pack individual bytes if we don't support packing them into words. stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf); @@ -520,8 +521,8 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu stream->buffer[stream->index] = data; stream->index++; // See if this byte ends a SysEx. - if (stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) { - stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1); + if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) { + stream->buffer[0] = (uint8_t)((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1))); stream->total = stream->index; } } diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 34dd6af8d..b12a3177e 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -202,6 +202,7 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32C0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 + #define TUP_USBIP_FSDEV_DRD #define CFG_TUSB_FSDEV_PMA_SIZE 2048u #elif TU_CHECK_MCU(OPT_MCU_STM32F0) @@ -278,6 +279,7 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32G0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 + #define TUP_USBIP_FSDEV_DRD #define CFG_TUSB_FSDEV_PMA_SIZE 2048u #elif TU_CHECK_MCU(OPT_MCU_STM32G4) @@ -293,6 +295,7 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32H5) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 + #define TUP_USBIP_FSDEV_DRD #define CFG_TUSB_FSDEV_PMA_SIZE 2048u #elif TU_CHECK_MCU(OPT_MCU_STM32H7) @@ -366,6 +369,7 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32U3) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 + #define TUP_USBIP_FSDEV_DRD #define CFG_TUSB_FSDEV_PMA_SIZE 2048u #elif TU_CHECK_MCU(OPT_MCU_STM32U5) @@ -373,6 +377,7 @@ #if defined(STM32U535xx) || defined(STM32U545xx) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 + #define TUP_USBIP_FSDEV_DRD #define CFG_TUSB_FSDEV_PMA_SIZE 2048u #else #define TUP_USBIP_DWC2 diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 13d1a6cb4..8b4719b21 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -612,7 +612,7 @@ void dcd_edpt_close_all(uint8_t rhport) { dcd_int_enable(rhport); // Reset PMA allocation - ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT + 2 * CFG_TUD_ENDPOINT0_SIZE; } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c index dd86b052a..18685dbdc 100644 --- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -38,8 +38,7 @@ #include "tusb_option.h" -#if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV) && \ - TU_CHECK_MCU(OPT_MCU_STM32C0, OPT_MCU_STM32G0, OPT_MCU_STM32H5, OPT_MCU_STM32U5) +#if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV) && defined(TUP_USBIP_FSDEV_DRD) #include "host/hcd.h" #include "host/usbh.h" @@ -672,10 +671,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet // Clear stall, data toggle is also reset to DATA0 bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { (void) rhport; - (void) dev_addr; - (void) ep_addr; - uint8_t const ep_id = endpoint_find(dev_addr, 0); + uint8_t const ep_id = endpoint_find(dev_addr, ep_addr); TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id]; -- cgit v1.3.1