mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
usbd: forward vendor EP0 requests; clear ep state on iso activate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
This commit is contained in:
@ -256,7 +256,7 @@ static const usbd_class_driver_t _usbd_driver[] = {
|
||||
.deinit = vendord_deinit,
|
||||
.reset = vendord_reset,
|
||||
.open = vendord_open,
|
||||
.control_xfer_cb = tud_vendor_control_xfer_cb,
|
||||
.control_xfer_cb = vendord_control_xfer_cb,
|
||||
.xfer_cb = vendord_xfer_cb,
|
||||
.xfer_isr = NULL,
|
||||
.sof = NULL
|
||||
@ -418,6 +418,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event,
|
||||
//--------------------------------------------------------------------+
|
||||
static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request);
|
||||
static bool process_get_status(uint8_t rhport, tusb_control_request_t const * request, uint16_t status);
|
||||
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
|
||||
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
|
||||
|
||||
@ -1041,9 +1042,7 @@ static bool process_std_device_request(uint8_t rhport, tusb_control_request_t co
|
||||
// Device status bit mask
|
||||
// - Bit 0: Self Powered TODO must invoke callback to get actual status
|
||||
// - Bit 1: Remote Wakeup enabled
|
||||
uint16_t status = (uint16_t) _usbd_dev.dev_state_bm;
|
||||
tud_control_xfer(rhport, p_request, &status, 2);
|
||||
return true;
|
||||
return process_get_status(rhport, p_request, (uint16_t) _usbd_dev.dev_state_bm);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -1053,6 +1052,14 @@ static bool process_std_device_request(uint8_t rhport, tusb_control_request_t co
|
||||
}
|
||||
|
||||
|
||||
// Reply to a standard GET_STATUS (device/interface/endpoint) with its 2-byte status word.
|
||||
// GET_STATUS is Device-to-host only; reject a mis-directed (OUT) request rather than handing
|
||||
// usbd the address of a stack local to write host data into after this frame has returned.
|
||||
static bool process_get_status(uint8_t rhport, tusb_control_request_t const * request, uint16_t status) {
|
||||
TU_VERIFY(request->bmRequestType_bit.direction == TUSB_DIR_IN);
|
||||
return tud_control_xfer(rhport, request, &status, 2);
|
||||
}
|
||||
|
||||
// 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_setup_received(uint8_t rhport, tusb_control_request_t const * p_request) {
|
||||
@ -1150,9 +1157,18 @@ static bool process_setup_received(uint8_t rhport, tusb_control_request_t const
|
||||
}
|
||||
|
||||
case TUSB_REQ_SET_INTERFACE:
|
||||
// A class that implements altsettings handles SET_INTERFACE itself and returns true,
|
||||
// so reaching here means the class does not — where only alt 0 is valid. Any non-zero
|
||||
// alt (unimplemented, or rejected as invalid by the class) is a Request Error (stall).
|
||||
TU_VERIFY(tu_u16_low(p_request->wValue) == 0);
|
||||
tud_control_status(rhport, p_request);
|
||||
break;
|
||||
|
||||
case TUSB_REQ_GET_STATUS:
|
||||
// USB 2.0 9.4.5: interface GET_STATUS returns 2 reserved (zero) bytes
|
||||
TU_VERIFY(process_get_status(rhport, p_request, 0x0000));
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@ -1175,35 +1191,34 @@ static bool process_setup_received(uint8_t rhport, tusb_control_request_t const
|
||||
} else {
|
||||
// Handle STD request to endpoint
|
||||
switch (p_request->bRequest) { //-V2520
|
||||
case TUSB_REQ_GET_STATUS: {
|
||||
uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001u : 0x0000u;
|
||||
tud_control_xfer(rhport, p_request, &status, 2);
|
||||
}
|
||||
break;
|
||||
case TUSB_REQ_GET_STATUS:
|
||||
// USB 2.0 9.4.5: endpoint GET_STATUS bit 0 = Halt
|
||||
TU_VERIFY(process_get_status(rhport, p_request, usbd_edpt_stalled(rhport, ep_addr) ? 0x0001u : 0x0000u));
|
||||
break;
|
||||
|
||||
case TUSB_REQ_CLEAR_FEATURE:
|
||||
case TUSB_REQ_SET_FEATURE: {
|
||||
if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) {
|
||||
if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) {
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
}else {
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
}
|
||||
// ENDPOINT_HALT is the only endpoint feature; it exists only on a non-control endpoint
|
||||
// that an interface actually owns. Any other selector, the control endpoint (EP0 has no
|
||||
// Halt feature, USB 2.0 9.4.9), or an endpoint no driver owns is a Request Error (stall).
|
||||
TU_VERIFY(TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue);
|
||||
TU_VERIFY(ep_num != 0);
|
||||
TU_VERIFY(driver != NULL);
|
||||
|
||||
if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) {
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
} else {
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
}
|
||||
|
||||
if (driver != NULL) {
|
||||
// Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request
|
||||
// We will also forward std request targeted endpoint to class drivers as well
|
||||
// Some classes such as USBTMC need to clear/re-init their buffer on CLEAR_FEATURE.
|
||||
// Clear complete callback if driver set since it can also stall the request.
|
||||
(void) invoke_class_control(rhport, driver, p_request);
|
||||
ctrl_xfer->complete_cb = NULL;
|
||||
|
||||
// STD request must always be ACKed regardless of driver returned value
|
||||
// Also clear complete callback if driver set since it can also stall the request.
|
||||
(void) invoke_class_control(rhport, driver, p_request);
|
||||
ctrl_xfer->complete_cb = NULL;
|
||||
|
||||
// skip ZLP status if driver already did that
|
||||
if (!(_usbd_dev.ep_status[0][TUSB_DIR_IN] & TU_EDPT_STATE_BUSY)) {
|
||||
tud_control_status(rhport, p_request);
|
||||
}
|
||||
// STD request must always be ACKed; skip ZLP status if driver already did that.
|
||||
if (!(_usbd_dev.ep_status[0][TUSB_DIR_IN] & TU_EDPT_STATE_BUSY)) {
|
||||
tud_control_status(rhport, p_request);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1648,10 +1663,21 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// only clear if currently stalled
|
||||
TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr);
|
||||
const bool was_stalled = (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_STALLED) != 0;
|
||||
dcd_edpt_clear_stall(rhport, ep_addr);
|
||||
_usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY);
|
||||
// Clear STALLED|BUSY unconditionally (long-standing behavior; some classes, e.g. audio's
|
||||
// set-interface, call this on a non-stalled endpoint solely to drop a leftover BUSY bit).
|
||||
// Only release the CLAIMED ownership bit when the endpoint was actually stalled: the stall
|
||||
// aborts the in-flight transfer in the dcd with no completion event to release the claim, so
|
||||
// clearing it here prevents starvation. On a non-stalled clear (e.g. a data-toggle reset) a
|
||||
// transfer may still be legitimately claimed by another task, so keep CLAIMED to preserve the
|
||||
// claim->xfer mutual exclusion.
|
||||
uint8_t clear_mask = TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY;
|
||||
if (was_stalled) {
|
||||
clear_mask |= TU_EDPT_STATE_CLAIMED;
|
||||
}
|
||||
_usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~clear_mask;
|
||||
}
|
||||
|
||||
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) {
|
||||
|
||||
Reference in New Issue
Block a user