usbh always retry control transfer (max 3) in case of XFER_RESULT_FAILED.

added tuh_connected()
This commit is contained in:
hathach
2025-02-17 22:40:19 +07:00
parent 18d566b94c
commit 792a446405
6 changed files with 125 additions and 109 deletions

View File

@@ -44,6 +44,10 @@
#define CFG_TUH_INTERFACE_MAX 8
#endif
enum {
USBH_CONTROL_RETRY_MAX = 3,
};
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
@@ -272,9 +276,10 @@ static struct {
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
uint8_t daddr;
volatile uint8_t stage;
uint8_t daddr;
volatile uint16_t actual_len;
uint8_t failed_count;
} _ctrl_xfer;
typedef struct {
@@ -285,7 +290,6 @@ typedef struct {
CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;
//------------- Helper Function -------------//
TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
return &_usbh_devices[dev_addr-1];
@@ -312,6 +316,15 @@ bool tuh_mounted(uint8_t dev_addr) {
return dev->configured;
}
bool tuh_connected(uint8_t daddr) {
if (daddr == 0) {
return _dev0.enumerating; // dev0 is connected if still enumerating
} else {
const usbh_device_t* dev = get_device(daddr);
return dev && dev->connected;
}
}
bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
*vid = *pid = 0;
@@ -421,7 +434,9 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
}
bool tuh_deinit(uint8_t rhport) {
if (!tuh_rhport_is_active(rhport)) return true;
if (!tuh_rhport_is_active(rhport)) {
return true;
}
// deinit host controller
hcd_int_disable(rhport);
@@ -482,7 +497,9 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
if (!tuh_inited()) return;
if (!tuh_inited()) {
return;
}
// Loop until there is no more events in the queue
while (1) {
@@ -616,17 +633,9 @@ static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
// TODO timeout_ms is not supported yet
bool tuh_control_xfer (tuh_xfer_t* xfer) {
// EP0 with setup packet
TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);
// Check if device is still connected (enumerating for dev0)
TU_VERIFY(xfer->ep_addr == 0 && xfer->setup); // EP0 with setup packet
const uint8_t daddr = xfer->daddr;
if (daddr == 0) {
TU_VERIFY(_dev0.enumerating);
} else {
const usbh_device_t* dev = get_device(daddr);
TU_VERIFY(dev && dev->connected);
}
TU_VERIFY(tuh_connected(daddr)); // Check if device is still connected (enumerating for dev0)
// pre-check to help reducing mutex lock
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
@@ -634,14 +643,15 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
if (is_idle) {
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.daddr = daddr;
_ctrl_xfer.actual_len = 0;
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.daddr = daddr;
_ctrl_xfer.actual_len = 0;
_ctrl_xfer.failed_count = 0;
_ctrl_xfer.buffer = xfer->buffer;
_ctrl_xfer.complete_cb = xfer->complete_cb;
_ctrl_xfer.user_data = xfer->user_data;
_usbh_epbuf.request = (*xfer->setup);
_ctrl_xfer.buffer = xfer->buffer;
_ctrl_xfer.complete_cb = xfer->complete_cb;
_ctrl_xfer.user_data = xfer->user_data;
_usbh_epbuf.request = (*xfer->setup);
}
(void) osal_mutex_unlock(_usbh_mutex);
@@ -655,7 +665,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
TU_LOG_BUF_USBH(xfer->setup, 8);
if (xfer->complete_cb) {
TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_usbh_epbuf.request) );
TU_ASSERT(hcd_setup_send(rhport, daddr, (uint8_t const *) &_usbh_epbuf.request));
}else {
// blocking if complete callback is not provided
// change callback to internal blocking, and result as user argument
@@ -665,7 +675,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
_ctrl_xfer.user_data = (uintptr_t) &result;
_ctrl_xfer.complete_cb = _control_blocking_complete_cb;
TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_usbh_epbuf.request) );
TU_ASSERT(hcd_setup_send(rhport, daddr, (uint8_t *) &_usbh_epbuf.request));
while (result == XFER_RESULT_INVALID) {
// Note: this can be called within an callback ie. part of tuh_task()
@@ -722,28 +732,46 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
const uint8_t rhport = usbh_get_rhport(daddr);
tusb_control_request_t const * request = &_usbh_epbuf.request;
if (XFER_RESULT_SUCCESS != result) {
TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
switch (result) {
case XFER_RESULT_STALLED:
TU_LOG_USBH("[%u:%u] Control STALLED, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
_control_xfer_complete(daddr, result);
break;
// terminate transfer if any stage failed
_control_xfer_complete(daddr, result);
}else {
switch(_ctrl_xfer.stage) {
case CONTROL_STAGE_SETUP:
if (request->wLength) {
// DATA stage: initial data toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_DATA);
TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
return true;
}
case XFER_RESULT_FAILED:
if (tuh_connected(daddr) && _ctrl_xfer.failed_count < USBH_CONTROL_RETRY_MAX) {
TU_LOG_USBH("[%u:%u] Control FAILED %u/%u, retrying\r\n", rhport, daddr, _ctrl_xfer.failed_count+1, USBH_CONTROL_RETRY_MAX);
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.failed_count++;
_ctrl_xfer.actual_len = 0; // reset actual_len
(void) osal_mutex_unlock(_usbh_mutex);
TU_ASSERT(hcd_setup_send(rhport, daddr, (uint8_t const *) request));
} else {
TU_LOG_USBH("[%u:%u] Control FAILED, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
_control_xfer_complete(daddr, result);
}
break;
case XFER_RESULT_SUCCESS:
switch(_ctrl_xfer.stage) {
case CONTROL_STAGE_SETUP:
if (request->wLength) {
// DATA stage: initial data toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_DATA);
TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
return true;
}
TU_ATTR_FALLTHROUGH;
case CONTROL_STAGE_DATA:
if (request->wLength) {
TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
TU_LOG_MEM_USBH(_ctrl_xfer.buffer, xferred_bytes, 2);
}
case CONTROL_STAGE_DATA:
if (request->wLength) {
TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
TU_LOG_MEM_USBH(_ctrl_xfer.buffer, xferred_bytes, 2);
}
_ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
@@ -752,23 +780,26 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction), NULL, 0) );
break;
case CONTROL_STAGE_ACK: {
// Abort all pending transfers if SET_CONFIGURATION request
// NOTE: should we force closing all non-control endpoints in the future?
if (request->bRequest == TUSB_REQ_SET_CONFIGURATION && request->bmRequestType == 0x00) {
for(uint8_t epnum=1; epnum<CFG_TUH_ENDPOINT_MAX; epnum++) {
for(uint8_t dir=0; dir<2; dir++) {
tuh_edpt_abort_xfer(daddr, tu_edpt_addr(epnum, dir));
case CONTROL_STAGE_ACK: {
// Abort all pending transfers if SET_CONFIGURATION request
// NOTE: should we force closing all non-control endpoints in the future?
if (request->bRequest == TUSB_REQ_SET_CONFIGURATION && request->bmRequestType == 0x00) {
for(uint8_t epnum=1; epnum<CFG_TUH_ENDPOINT_MAX; epnum++) {
for(uint8_t dir=0; dir<2; dir++) {
tuh_edpt_abort_xfer(daddr, tu_edpt_addr(epnum, dir));
}
}
}
_control_xfer_complete(daddr, result);
break;
}
_control_xfer_complete(daddr, result);
break;
default: return false; // unsupported stage
}
break;
default: return false;
}
default: return false; // unsupported result
}
return true;
@@ -1769,8 +1800,9 @@ static void enum_full_complete(void) {
_dev0.enumerating = 0;
#if CFG_TUH_HUB
// get next hub status
if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr);
if (_dev0.hub_addr) {
hub_edpt_status_xfer(_dev0.hub_addr); // get next hub status
}
#endif
}