diff options
| author | hathach <[email protected]> | 2026-04-29 17:27:59 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-04-29 17:27:59 +0700 |
| commit | 9d3ad336bfad062fa1e6f6d63feb97a9851cc9e1 (patch) | |
| tree | b72ef398d47529eb25810333b319c4ca644cc397 | |
| parent | 939c2f91c200db2bf9874c2e6c50b3530f8a6725 (diff) | |
deprecated `usbd_control.c` and merge its functionality into `usbd.c`
| -rw-r--r-- | hw/bsp/rp2040/family.cmake | 1 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/device/usbd.c | 173 | ||||
| -rw-r--r-- | src/device/usbd_control.c | 199 | ||||
| -rw-r--r-- | src/tinyusb.mk | 1 | ||||
| -rw-r--r-- | test/fuzz/rules.mk | 1 | ||||
| -rw-r--r-- | test/unit-test/CMakeLists.txt | 4 |
7 files changed, 172 insertions, 208 deletions
diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 6e88b9fa1..075582554 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -93,7 +93,6 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/portable/raspberrypi/rp2040/dcd_rp2040.c ${TOP}/src/portable/raspberrypi/rp2040/rp2040_usb.c ${TOP}/src/device/usbd.c - ${TOP}/src/device/usbd_control.c ${TOP}/src/class/audio/audio_device.c ${TOP}/src/class/cdc/cdc_device.c ${TOP}/src/class/dfu/dfu_device.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00f466007..c7a5184c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,6 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c # device ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_device.c diff --git a/src/device/usbd.c b/src/device/usbd.c index da0ffb4c6..acf808bf6 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -419,11 +419,10 @@ static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_req } #endif -// from usbd_control.c -void usbd_control_reset(void); -void usbd_control_set_request(tusb_control_request_t const *request); -void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); -bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +// Control Endpoint +static void usbd_control_reset(void); +static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); +static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available @@ -809,6 +808,157 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { } //--------------------------------------------------------------------+ +// Control Endpoint +//--------------------------------------------------------------------+ + +// Weak hook: invoked when the control transfer's status stage completes +TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) { + (void) rhport; + (void) request; +} + +typedef struct { + tusb_control_request_t request; + uint8_t* buffer; + uint16_t data_len; + uint16_t total_xferred; + usbd_control_xfer_cb_t complete_cb; +} usbd_control_xfer_t; + +static usbd_control_xfer_t _ctrl_xfer; + +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; +} + +// Endpoint used for the Status stage of a control transfer. +// Per USB 2.0 §9.3.1, when wLength == 0 the Direction bit is ignored and the Status +// stage is always IN. Otherwise the Status stage is opposite of the Data stage direction. +TU_ATTR_ALWAYS_INLINE static inline uint8_t status_stage_ep(const tusb_control_request_t* request) { + return (request->wLength != 0 && request->bmRequestType_bit.direction) ? TU_EP0_OUT : TU_EP0_IN; +} + +// Queue ZLP status transaction +TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8_t ep_status) { + return usbd_edpt_xfer(rhport, ep_status, NULL, 0, false); +} + +// Queue a transaction in Data Stage. Each transaction has up to Endpoint0's max +// packet size. This function can also transfer a zero-length packet. +static bool data_stage_xact(uint8_t rhport) { + const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); + uint8_t ep_addr = TU_EP0_OUT; + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { + ep_addr = TU_EP0_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)); + } + } + + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); +} + +// Status phase +bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { + // _ctrl_xfer fields are pre-initialized at process_control_request entry + (void) request; + return status_stage_xact(rhport, status_stage_ep(&_ctrl_xfer.request)); +} + +// Transmit data to/from the control endpoint. If wLength is zero, a status packet is sent instead. +bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { + // _ctrl_xfer.request and reset fields are pre-initialized at process_control_request entry + (void) request; + _ctrl_xfer.buffer = (uint8_t*) buffer; + _ctrl_xfer.data_len = tu_min16(len, _ctrl_xfer.request.wLength); + + if (_ctrl_xfer.request.wLength > 0U) { + if (_ctrl_xfer.data_len > 0U) { + TU_ASSERT(buffer); + } + TU_ASSERT(data_stage_xact(rhport)); + } else { + // wLength == 0: Status stage is always IN per USB 2.0 §9.3.1 + TU_ASSERT(status_stage_xact(rhport, TU_EP0_IN)); + } + + return true; +} + +static void usbd_control_reset(void) { + tu_varclr(&_ctrl_xfer); +} + +static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { + _ctrl_xfer.complete_cb = fp; +} + +// Callback when a transaction completes on the DATA stage or Status stage of EP0 +static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void) result; + + // Status Stage complete: ep_addr matches the resolved Status stage endpoint + uint8_t const ep_status = status_stage_ep(&_ctrl_xfer.request); + if (ep_addr == ep_status) { + TU_ASSERT(0 == xferred_bytes); + + // invoke optional dcd hook if available + dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); + + if (NULL != _ctrl_xfer.complete_cb) { + // TODO refactor with usbd_driver_print_control_complete_name + _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); + } + + return true; + } + + // Data stage progress + 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); + } + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); + } + + _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; + _ctrl_xfer.buffer += xferred_bytes; + + // Data Stage complete when wLength reached or short packet (incl. ZLP) seen + if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || + (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { + bool is_ok = true; + + if (NULL != _ctrl_xfer.complete_cb) { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); + #endif + // Callback can still stall control in status phase, e.g. OUT data doesn't make sense + is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); + } + + if (is_ok) { + TU_ASSERT(status_stage_xact(rhport, ep_status)); + } else { + // Stall both IN and OUT control endpoint + dcd_edpt_stall(rhport, TU_EP0_OUT); + dcd_edpt_stall(rhport, TU_EP0_IN); + } + } else { + // More data to transfer + TU_ASSERT(data_stage_xact(rhport)); + } + + return true; +} + +//--------------------------------------------------------------------+ // Control Request Parser & Handling //--------------------------------------------------------------------+ @@ -822,7 +972,14 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri // This handles the actual request and its response. // Returns false if unable to complete the request, causing caller to stall control endpoints. static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { - usbd_control_set_complete_callback(NULL); + // Initialize control transfer state for this request. The request copy must be + // visible to usbd_control_xfer_cb when the (asynchronous) status ZLP completes, + // since the SETUP packet event has already gone out of scope by then. + _ctrl_xfer.request = *p_request; + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; + _ctrl_xfer.complete_cb = NULL; TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); // Vendor request @@ -865,9 +1022,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // Depending on mcu, status phase could be sent either before or after changing device address, // or even require stack to not response with status at all // Therefore DCD must take full responsibility to response and include zlp status packet if needed. - usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + // _ctrl_xfer.request was already populated at process_control_request() entry, so the + // status ZLP that the DCD queues will be recognized by usbd_control_xfer_cb(). dcd_set_address(rhport, (uint8_t) p_request->wValue); - // skip tud_control_status() _usbd_dev.addressed = 1; break; diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index b14d08a9c..38dcc6a82 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -24,197 +24,8 @@ * This file is part of the TinyUSB stack. */ -#include "tusb_option.h" - -#if CFG_TUD_ENABLED - -#include "dcd.h" -#include "tusb.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// Callback weak stubs (called if application does not provide) -//--------------------------------------------------------------------+ -TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) { - (void) rhport; - (void) request; -} - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - - -typedef struct { - tusb_control_request_t request; - uint8_t* buffer; - uint16_t data_len; - uint16_t total_xferred; - usbd_control_xfer_cb_t complete_cb; -} usbd_control_xfer_t; - -static usbd_control_xfer_t _ctrl_xfer; - -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 -//--------------------------------------------------------------------+ - -// Endpoint used for the Status stage of a control transfer. -// Per USB 2.0 §9.3.1, when wLength == 0 the Direction bit is ignored and the Status stage -// is always IN. Otherwise the Status stage is opposite to the Data stage direction. -TU_ATTR_ALWAYS_INLINE static inline uint8_t status_stage_ep(const tusb_control_request_t* request) { - return (request->wLength != 0 && request->bmRequestType_bit.direction) ? TU_EP0_OUT : TU_EP0_IN; -} - -// Queue ZLP status transaction -TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8_t ep_status) { - return usbd_edpt_xfer(rhport, ep_status, NULL, 0, false); -} - -// Status phase -bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = NULL; - _ctrl_xfer.total_xferred = 0; - _ctrl_xfer.data_len = 0; - - return status_stage_xact(rhport, status_stage_ep(request)); -} - -// Queue a transaction in Data Stage -// Each transaction has up to Endpoint0's max packet size. -// This function can also transfer an zero-length packet -static bool data_stage_xact(uint8_t rhport) { - const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); - uint8_t ep_addr = TU_EP0_OUT; - - if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { - ep_addr = TU_EP0_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)); - } - } - - return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); -} - -// Transmit data to/from the control endpoint. -// If the request's wLength is zero, a status packet is sent instead. -bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = (uint8_t*) buffer; - _ctrl_xfer.total_xferred = 0U; - _ctrl_xfer.data_len = tu_min16(len, request->wLength); - - if (request->wLength > 0U) { - if (_ctrl_xfer.data_len > 0U) { - TU_ASSERT(buffer); - } - TU_ASSERT(data_stage_xact(rhport)); - } else { - TU_ASSERT(status_stage_xact(rhport, TU_EP0_IN)); - } - - return true; -} - -//--------------------------------------------------------------------+ -// USBD API -//--------------------------------------------------------------------+ -void usbd_control_reset(void); -void usbd_control_set_request(const tusb_control_request_t* request); -void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); -bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - -void usbd_control_reset(void) { - tu_varclr(&_ctrl_xfer); -} - -// Set complete callback -void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { - _ctrl_xfer.complete_cb = fp; -} - -// for dcd_set_address where DCD is responsible for status response -void usbd_control_set_request(const tusb_control_request_t* request) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = NULL; - _ctrl_xfer.total_xferred = 0; - _ctrl_xfer.data_len = 0; -} - -// callback when a transaction complete on -// - DATA stage of control endpoint or -// - Status stage -bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - (void) result; - - // Status Stage complete: endpoint matches the Status stage endpoint - uint8_t const ep_status = status_stage_ep(&_ctrl_xfer.request); - if (ep_addr == ep_status) { - TU_ASSERT(0 == xferred_bytes); - - // invoke optional dcd hook if available - dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); - - if (NULL != _ctrl_xfer.complete_cb) { - // TODO refactor with usbd_driver_print_control_complete_name - _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); - } - - return true; - } - - // Data stage complete - 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); - } - TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); - } - - _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; - _ctrl_xfer.buffer += xferred_bytes; - - // Data Stage is complete when all request's length are transferred or - // a short packet is sent including zero-length packet. - if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || - (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { - // DATA stage is complete - bool is_ok = true; - - // invoke complete callback if set - // callback can still stall control in status phase e.g out data does not make sense - if (NULL != _ctrl_xfer.complete_cb) { - #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL - usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); - #endif - - is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); - } - - if (is_ok) { - TU_ASSERT(status_stage_xact(rhport, ep_status)); - } else { - // Stall both IN and OUT control endpoint - dcd_edpt_stall(rhport, TU_EP0_OUT); - dcd_edpt_stall(rhport, TU_EP0_IN); - } - } else { - // More data to transfer - TU_ASSERT(data_stage_xact(rhport)); - } - - return true; -} - -#endif +// The usbd control function that used to live in this file has been merged +// into src/device/usbd.c. This translation unit is intentionally empty and is +// kept only so external/vendor build systems that still reference the path +// keep resolving. Drop usbd_control.c from your build to silence the warning. +#warning "src/device/usbd_control.c is deprecated and now empty; remove it from your build (its content lives in src/device/usbd.c)." diff --git a/src/tinyusb.mk b/src/tinyusb.mk index 169098016..e3ef35dcf 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -3,7 +3,6 @@ TINYUSB_SRC_C += \ src/tusb.c \ src/common/tusb_fifo.c \ src/device/usbd.c \ - src/device/usbd_control.c \ src/typec/usbc.c \ src/class/audio/audio_device.c \ src/class/cdc/cdc_device.c \ diff --git a/test/fuzz/rules.mk b/test/fuzz/rules.mk index 329dcce11..c14330312 100644 --- a/test/fuzz/rules.mk +++ b/test/fuzz/rules.mk @@ -23,7 +23,6 @@ SRC_C += \ src/tusb.c \ src/common/tusb_fifo.c \ src/device/usbd.c \ - src/device/usbd_control.c \ src/class/audio/audio_device.c \ src/class/cdc/cdc_device.c \ src/class/dfu/dfu_device.c \ diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index b44a91d57..a33af4563 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -117,14 +117,14 @@ add_ceedling_test( add_ceedling_test( test_usbd ${CEEDLING_WORKDIR}/test/device/usbd/test_usbd.c - "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/device/usbd_control.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" "${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_dcd.c;${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_msc_device.c" ) add_ceedling_test( test_msc_device ${CEEDLING_WORKDIR}/test/device/msc/test_msc_device.c - "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/device/usbd_control.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" "${CEEDLING_BUILD_DIR}/test/mocks/test_msc_device/mock_dcd.c" ) |
