From 171d021ab56960f8d7e222faa263168ead0f9d6a Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 10 Mar 2022 22:22:05 +0700 Subject: [PATCH] refactor and integrate usbh control xfer back to usbh.c fix enumeration with hub when reset port before set address --- src/device/usbd_pvt.h | 2 +- src/host/usbh.c | 447 +++++++++++++++++++++++------------- src/host/usbh_classdriver.h | 1 + src/host/usbh_control.c | 82 ++----- src/osal/osal_none.h | 2 +- 5 files changed, 317 insertions(+), 217 deletions(-) diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index dae95cebb..29753451e 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -80,7 +80,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 // If caller does not make any transfer, it must release endpoint for others. bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr); -// Release an endpoint without submitting a transfer +// Release claimed endpoint without submitting a transfer bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr); // Check if endpoint is busy transferring diff --git a/src/host/usbh.c b/src/host/usbh.c index 07baeee96..3d9db70fa 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -72,7 +72,6 @@ typedef struct volatile uint8_t suspended : 1; }; - uint8_t control_stage; // state of control transfer } usbh_dev0_t; typedef struct { @@ -91,13 +90,12 @@ typedef struct { volatile uint8_t suspended : 1; }; - uint8_t control_stage; // state of control transfer - // Device Descriptor + uint8_t ep0_size; + uint16_t vid; uint16_t pid; - uint8_t ep0_size; uint8_t i_manufacturer; uint8_t i_product; uint8_t i_serial; @@ -113,6 +111,14 @@ typedef struct { } usbh_device_t; +typedef struct +{ + tusb_control_request_t request TU_ATTR_ALIGNED(4); + uint8_t* buffer; + tuh_control_complete_cb_t complete_cb; + + uint8_t daddr; +} usbh_control_xfer_t; //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -211,8 +217,24 @@ CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[TOTAL_DEVICES]; // Mutex for claiming endpoint, only needed when using with preempted RTOS #if TUSB_OPT_MUTEX -static osal_mutex_def_t _usbh_mutexdef[TOTAL_DEVICES]; -static osal_mutex_t _usbh_mutex[TOTAL_DEVICES]; +static osal_mutex_def_t _usbh_mutexdef; +static osal_mutex_t _usbh_mutex; + +TU_ATTR_ALWAYS_INLINE static inline void usbh_lock(void) +{ + osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); +} + +TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void) +{ + osal_mutex_unlock(_usbh_mutex); +} + +#else + +#define usbh_lock() +#define usbh_unlock() + #endif // Event queue @@ -223,6 +245,15 @@ static osal_queue_t _usbh_q; CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; +// Control transfer: since most controller does not support multiple control transfer +// on multiple devices concurrently. And control transfer is not used much except enumeration +// We will only execute control transfer one at a time. +struct +{ + usbh_control_xfer_t xfer; + uint8_t stage; +}_ctrl_xfer; + //------------- Helper Function -------------// TU_ATTR_ALWAYS_INLINE @@ -235,10 +266,7 @@ static inline usbh_device_t* get_device(uint8_t dev_addr) static bool enum_new_device(hcd_event_t* event); static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); - -// from usbh_control.c -extern bool usbh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb); -extern uint8_t usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, uint8_t* stage, xfer_result_t result, uint32_t xferred_bytes); +static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); //--------------------------------------------------------------------+ // PUBLIC API (Parameter Verification is required) @@ -276,24 +304,6 @@ void osal_task_delay(uint32_t msec) } #endif - -bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb) -{ - TU_LOG2("[%u:%u] %s: ", usbh_get_rhport(daddr), daddr, request->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[request->bRequest] : "Unknown Request"); - TU_LOG2_VAR(request); - TU_LOG2("\r\n"); - - if (daddr) - { - get_device(daddr)->control_stage = CONTROL_STAGE_SETUP; - }else - { - _dev0.control_stage = CONTROL_STAGE_SETUP; - } - - return usbh_control_xfer(daddr, request, buffer, complete_cb); -} - //--------------------------------------------------------------------+ // Descriptors //--------------------------------------------------------------------+ @@ -449,26 +459,30 @@ bool tuh_init(uint8_t rhport) TU_LOG2("USBH init\r\n"); TU_LOG2_INT(sizeof(usbh_device_t)); + TU_LOG2_INT(sizeof(hcd_event_t)); + TU_LOG2_INT(sizeof(usbh_control_xfer_t)); - //------------- Enumeration & Reporter Task init -------------// + // Event queue _usbh_q = osal_queue_create( &_usbh_qdef ); TU_ASSERT(_usbh_q != NULL); - //------------- Semaphore, Mutex for Control Pipe -------------// +#if TUSB_OPT_MUTEX + // Mutex + _usbh_mutex = osal_mutex_create(&_usbh_mutexdef); + TU_ASSERT(_usbh_mutex); +#endif + + // Device tu_memclr(&_dev0, sizeof(_dev0)); tu_memclr(_usbh_devices, sizeof(_usbh_devices)); + tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer)); for(uint8_t i=0; icontrol_stage, event.xfer_complete.result, event.xfer_complete.len); + usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); }else { uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; @@ -683,81 +697,6 @@ void hcd_event_device_remove(uint8_t hostid, bool in_isr) hcd_event_handler(&event, in_isr); } - -// a device unplugged from rhport:hub_addr:hub_port -static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) -{ - //------------- find the all devices (star-network) under port that is unplugged -------------// - // TODO mark as disconnected in ISR, also handle dev0 - for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ ) - { - usbh_device_t* dev = &_usbh_devices[dev_id]; - uint8_t const dev_addr = dev_id+1; - - // TODO Hub multiple level - if (dev->rhport == rhport && - (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub - (hub_port == 0 || dev->hub_port == hub_port) && - dev->connected) - { - // Invoke callback before close driver - if (tuh_umount_cb) tuh_umount_cb(dev_addr); - - // Close class driver - for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) - { - TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name); - usbh_class_drivers[drv_id].close(dev_addr); - } - - hcd_device_close(rhport, dev_addr); - clear_device(dev); - } - } -} - -//--------------------------------------------------------------------+ -// INTERNAL HELPER -//--------------------------------------------------------------------+ -static uint8_t get_new_address(bool is_hub) -{ - uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1; - uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX); - - for (uint8_t i=0; i < count; i++) - { - uint8_t const addr = start + i; - if (!get_device(addr)->connected) return addr; - } - return ADDR_INVALID; -} - -void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) -{ - usbh_device_t* dev = get_device(dev_addr); - - for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) - { - // continue with next valid interface - // TODO skip IAD binding interface such as CDCs - uint8_t const drv_id = dev->itf2drv[itf_num]; - if (drv_id != DRVID_INVALID) - { - usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; - TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num); - driver->set_config(dev_addr, itf_num); - break; - } - } - - // all interface are configured - if (itf_num == CFG_TUH_INTERFACE_MAX) - { - // Invoke callback if available - if (tuh_mount_cb) tuh_mount_cb(dev_addr); - } -} - //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ @@ -774,7 +713,7 @@ bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; #if TUSB_OPT_MUTEX - return tu_edpt_claim(ep_state, _usbh_mutex[dev_addr-1]); + return tu_edpt_claim(ep_state, _usbh_mutex); #else return tu_edpt_claim(ep_state, NULL); #endif @@ -792,7 +731,7 @@ bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; #if TUSB_OPT_MUTEX - return tu_edpt_release(ep_state, _usbh_mutex[dev_addr-1]); + return tu_edpt_release(ep_state, _usbh_mutex); #else return tu_edpt_release(ep_state, NULL); #endif @@ -865,6 +804,183 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) return dev->ep_status[epnum][dir].busy; } +//--------------------------------------------------------------------+ +// Control transfer +//--------------------------------------------------------------------+ + +bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb) +{ + // pre-check to help reducing mutex lock + TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + + usbh_lock(); + + bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + if (is_idle) _ctrl_xfer.stage = CONTROL_STAGE_SETUP; + + usbh_unlock(); + + TU_VERIFY(is_idle); + + const uint8_t rhport = usbh_get_rhport(daddr); + + TU_LOG2("[%u:%u] %s: ", rhport, daddr, request->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[request->bRequest] : "Unknown Request"); + TU_LOG2_VAR(request); + TU_LOG2("\r\n"); + + _ctrl_xfer.xfer.request = (*request); + _ctrl_xfer.xfer.buffer = buffer; + _ctrl_xfer.xfer.complete_cb = complete_cb; + _ctrl_xfer.xfer.daddr = daddr; + + return hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.xfer.request); +} + +TU_ATTR_ALWAYS_INLINE static inline void set_control_xfer_stage(uint8_t stage) +{ + usbh_lock(); + _ctrl_xfer.stage = stage; + usbh_unlock(); +} + +static void _xfer_complete(uint8_t dev_addr, xfer_result_t result) +{ + TU_LOG2("\r\n"); + + usbh_lock(); + _ctrl_xfer.stage = CONTROL_STAGE_IDLE; + usbh_unlock(); + + if (_ctrl_xfer.xfer.complete_cb) _ctrl_xfer.xfer.complete_cb(dev_addr, &_ctrl_xfer.xfer.request, result); +} + +static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) ep_addr; + (void) xferred_bytes; + + const uint8_t rhport = usbh_get_rhport(dev_addr); + tusb_control_request_t const * request = &_ctrl_xfer.xfer.request; + + if (XFER_RESULT_SUCCESS != result) + { + TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED"); + + // terminate transfer if any stage failed + _xfer_complete(dev_addr, 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); + return hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.xfer.buffer, request->wLength); + } + __attribute__((fallthrough)); + + case CONTROL_STAGE_DATA: + if (request->wLength) + { + TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr); + TU_LOG2_MEM(_ctrl_xfer.xfer.buffer, request->wLength, 2); + } + + // ACK stage: toggle is always 1 + set_control_xfer_stage(CONTROL_STAGE_ACK); + hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); + break; + + case CONTROL_STAGE_ACK: + _xfer_complete(dev_addr, result); + break; + + default: return false; + } + } + + return true; +} + +// a device unplugged from rhport:hub_addr:hub_port +static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) +{ + //------------- find the all devices (star-network) under port that is unplugged -------------// + // TODO mark as disconnected in ISR, also handle dev0 + for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ ) + { + usbh_device_t* dev = &_usbh_devices[dev_id]; + uint8_t const dev_addr = dev_id+1; + + // TODO Hub multiple level + if (dev->rhport == rhport && + (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub + (hub_port == 0 || dev->hub_port == hub_port) && + dev->connected) + { + // Invoke callback before close driver + if (tuh_umount_cb) tuh_umount_cb(dev_addr); + + // Close class driver + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) + { + TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name); + usbh_class_drivers[drv_id].close(dev_addr); + } + + hcd_device_close(rhport, dev_addr); + clear_device(dev); + // abort on-going control xfer if any + if (_ctrl_xfer.xfer.daddr == dev_addr) set_control_xfer_stage(CONTROL_STAGE_IDLE); + } + } +} + +//--------------------------------------------------------------------+ +// INTERNAL HELPER +//--------------------------------------------------------------------+ + +static uint8_t get_new_address(bool is_hub) +{ + uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1; + uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX); + + for (uint8_t i=0; i < count; i++) + { + uint8_t const addr = start + i; + if (!get_device(addr)->connected) return addr; + } + return ADDR_INVALID; +} + +void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) +{ + usbh_device_t* dev = get_device(dev_addr); + + for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) + { + // continue with next valid interface + // TODO skip IAD binding interface such as CDCs + uint8_t const drv_id = dev->itf2drv[itf_num]; + if (drv_id != DRVID_INVALID) + { + usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num); + driver->set_config(dev_addr, itf_num); + break; + } + } + + // all interface are configured + if (itf_num == CFG_TUH_INTERFACE_MAX) + { + // Invoke callback if available + if (tuh_mount_cb) tuh_mount_cb(dev_addr); + } +} + //--------------------------------------------------------------------+ // Enumeration Process // is a lengthy process with a series of control transfer to configure @@ -886,43 +1002,16 @@ static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_ static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); #if CFG_TUH_HUB -static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) -{ - (void) dev_addr; (void) request; - TU_ASSERT(XFER_RESULT_SUCCESS == result); - enum_request_addr0_device_desc(); - return true; -} -static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) -{ - (void) dev_addr; (void) request; - TU_ASSERT(XFER_RESULT_SUCCESS == result); +// Enum sequence: +// New device (reset on the way) -> Get Status 0 -> Clear Reset 0 -> Get 8byte Device Descriptor +// -> Port Reset 1 -> reset delay -> Get Status 1 -> Clear Reset 1 -> queue hub interrupt endpoint - enum_request_set_addr(); - - // done with hub, waiting for next data on status pipe - (void) hub_status_pipe_queue( _dev0.hub_addr ); - - return true; -} - -static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) -{ - (void) dev_addr; (void) request; - TU_ASSERT(XFER_RESULT_SUCCESS == result); - - hub_port_status_response_t port_status; - memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); - - // Acknowledge Port Reset Change if Reset Successful - if (port_status.change.reset) - { - TU_ASSERT( hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) ); - } - - return true; -} +static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static bool enum_hub_set_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); +static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { @@ -949,6 +1038,57 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request return true; } + +static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +{ + (void) dev_addr; (void) request; + TU_ASSERT(XFER_RESULT_SUCCESS == result); + enum_request_addr0_device_desc(); + return true; +} + +static bool enum_hub_set_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +{ + (void) dev_addr; (void) request; + TU_ASSERT(XFER_RESULT_SUCCESS == result); + + osal_task_delay(RESET_DELAY); + + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) ); + + return true; +} + +static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +{ + (void) dev_addr; (void) request; + TU_ASSERT(XFER_RESULT_SUCCESS == result); + + hub_port_status_response_t port_status; + memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); + + // Acknowledge Port Reset Change if Reset Successful + if (port_status.change.reset) + { + TU_ASSERT( hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) ); + } + + return true; +} + +static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) +{ + (void) dev_addr; (void) request; + TU_ASSERT(XFER_RESULT_SUCCESS == result); + + enum_request_set_addr(); + + // done with hub, waiting for next data on status pipe + (void) hub_status_pipe_queue( _dev0.hub_addr ); + + return true; +} + #endif // hub static bool enum_new_device(hcd_event_t* event) @@ -1030,12 +1170,7 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r else { // after RESET_DELAY the hub_port_reset() already complete - TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, NULL) ); - osal_task_delay(RESET_DELAY); - - tuh_task(); // FIXME temporarily to clean up port_reset control transfer - - TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) ); + TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, enum_hub_set_reset1_complete) ); } #endif // hub diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h index 2f9957cc6..0435eae70 100644 --- a/src/host/usbh_classdriver.h +++ b/src/host/usbh_classdriver.h @@ -73,6 +73,7 @@ bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_ // If caller does not make any transfer, it must release endpoint for others. bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr); +// Release claimed endpoint without submitting a transfer bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr); // Check if endpoint transferring is complete diff --git a/src/host/usbh_control.c b/src/host/usbh_control.c index 88287a47b..38f03a8d2 100644 --- a/src/host/usbh_control.c +++ b/src/host/usbh_control.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if CFG_TUH_ENABLED +#if CFG_TUH_ENABLED && 0 #include "tusb.h" #include "usbh_classdriver.h" @@ -36,86 +36,50 @@ typedef struct tusb_control_request_t request TU_ATTR_ALIGNED(4); uint8_t* buffer; tuh_control_complete_cb_t complete_cb; + + uint8_t daddr; } usbh_control_xfer_t; -static usbh_control_xfer_t _ctrl_xfer; +static usbh_control_xfer_t _xfer; +static uint8_t _stage = CONTROL_STAGE_IDLE; //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +uint8_t usbh_control_xfer_stage(void) +{ + return _stage; +} + bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb) { // TODO need to claim the endpoint first const uint8_t rhport = usbh_get_rhport(dev_addr); - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = buffer; - _ctrl_xfer.complete_cb = complete_cb; + _ctrl_xfer.xfer.daddr = dev_addr; + _ctrl_xfer.xfer.request = (*request); + _ctrl_xfer.xfer.buffer = buffer; + _ctrl_xfer.xfer.complete_cb = complete_cb; + + _stage = CONTROL_STAGE_SETUP; // Send setup packet - TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.request) ); + TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.xfer.request) ); return true; } +void usbh_control_xfer_abort(uint8_t dev_addr) +{ + if (_ctrl_xfer.xfer.daddr == dev_addr) _stage = CONTROL_STAGE_IDLE; +} + static void _xfer_complete(uint8_t dev_addr, xfer_result_t result) { TU_LOG2("\r\n"); - if (_ctrl_xfer.complete_cb) _ctrl_xfer.complete_cb(dev_addr, &_ctrl_xfer.request, result); + if (_ctrl_xfer.xfer.complete_cb) _ctrl_xfer.xfer.complete_cb(dev_addr, &_ctrl_xfer.xfer.request, result); } -bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, uint8_t* stage, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) ep_addr; - (void) xferred_bytes; - - const uint8_t rhport = usbh_get_rhport(dev_addr); - tusb_control_request_t const * request = &_ctrl_xfer.request; - - if (XFER_RESULT_SUCCESS != result) - { - TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED"); - - // terminate transfer if any stage failed - *stage = CONTROL_STAGE_IDLE; - _xfer_complete(dev_addr, result); - }else - { - switch(*stage) - { - case CONTROL_STAGE_SETUP: - *stage = CONTROL_STAGE_DATA; - if (request->wLength) - { - // DATA stage: initial data toggle is always 1 - hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength); - return true; - } - __attribute__((fallthrough)); - - case CONTROL_STAGE_DATA: - *stage = CONTROL_STAGE_ACK; - if (request->wLength) - { - TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr); - TU_LOG2_MEM(_ctrl_xfer.buffer, request->wLength, 2); - } - - // ACK stage: toggle is always 1 - hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); - break; - - case CONTROL_STAGE_ACK: - *stage = CONTROL_STAGE_IDLE; - _xfer_complete(dev_addr, result); - break; - - default: return false; - } - } - - return true; -} #endif diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index e1dd95c34..4217f0422 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -111,7 +111,7 @@ typedef struct typedef osal_queue_def_t* osal_queue_t; -// role device/host is used by OS NONE for mutex (disable usb isr) only +// _int_set is used as mutex in OS NONE (disable/enable USB ISR) #define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ uint8_t _name##_buf[_depth*sizeof(_type)]; \ osal_queue_def_t _name = { \