diff options
| author | Ha Thach <[email protected]> | 2022-03-11 10:33:50 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-03-11 10:33:50 +0700 |
| commit | 69ef918021b1c4c97db3bfa41f73b0348ec617c8 (patch) | |
| tree | ba5d86fd18e1e674c0012306c3e9e0bafc03ba20 /src/host | |
| parent | ec6a8f00ad98f1f66b13a24329c33c009191cc8e (diff) | |
| parent | a715077b1079993f4cd01bbf54b8166a6612b29b (diff) | |
Merge pull request #1393 from hathach/more-host-bare-api
More host enhancement
Diffstat (limited to 'src/host')
| -rw-r--r-- | src/host/hcd.h | 3 | ||||
| -rw-r--r-- | src/host/hub.c | 4 | ||||
| -rw-r--r-- | src/host/hub.h | 2 | ||||
| -rw-r--r-- | src/host/usbh.c | 704 | ||||
| -rw-r--r-- | src/host/usbh.h | 18 | ||||
| -rw-r--r-- | src/host/usbh_classdriver.h | 1 | ||||
| -rw-r--r-- | src/host/usbh_control.c | 138 |
7 files changed, 450 insertions, 420 deletions
diff --git a/src/host/hcd.h b/src/host/hcd.h index 9819f5f2a..c40bea64c 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -94,7 +94,8 @@ typedef struct } hcd_event_t; -typedef struct { +typedef struct +{ uint8_t rhport; uint8_t hub_addr; uint8_t hub_port; diff --git a/src/host/hub.c b/src/host/hub.c index 1fec8b892..4c375c290 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -189,7 +189,7 @@ void hub_close(uint8_t dev_addr) if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t)); } -bool hub_status_pipe_queue(uint8_t dev_addr) +bool hub_edpt_status_xfer(uint8_t dev_addr) { hub_interface_t* hub_itf = get_itf(dev_addr); return usbh_edpt_xfer(dev_addr, hub_itf->ep_in, &hub_itf->status_change, 1); @@ -324,7 +324,7 @@ static bool connection_get_status_complete (uint8_t dev_addr, tusb_control_reque // prepare for next hub status // TODO continue with status_change, or maybe we can do it again with status - hub_status_pipe_queue(dev_addr); + hub_edpt_status_xfer(dev_addr); } return true; diff --git a/src/host/hub.h b/src/host/hub.h index c4d544193..6d81f6773 100644 --- a/src/host/hub.h +++ b/src/host/hub.h @@ -176,7 +176,7 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, t bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_control_complete_cb_t complete_cb); bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_control_complete_cb_t complete_cb); -bool hub_status_pipe_queue(uint8_t dev_addr); +bool hub_edpt_status_xfer(uint8_t dev_addr); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/host/usbh.c b/src/host/usbh.c index 192979ce6..11bf2f997 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -29,6 +29,8 @@ #if CFG_TUH_ENABLED #include "tusb.h" +#include "common/tusb_private.h" + #include "host/usbh.h" #include "host/usbh_classdriver.h" #include "hub.h" @@ -53,6 +55,7 @@ //--------------------------------------------------------------------+ // device0 struct must be strictly a subset of normal device struct +// TODO refactor later typedef struct { // port @@ -63,11 +66,12 @@ typedef struct struct TU_ATTR_PACKED { - volatile uint8_t connected : 1; - volatile uint8_t addressed : 1; - volatile uint8_t configured : 1; - volatile uint8_t suspended : 1; + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t configured : 1; + volatile uint8_t suspended : 1; }; + } usbh_dev0_t; typedef struct { @@ -77,15 +81,16 @@ typedef struct { uint8_t hub_port; uint8_t speed; + // Device State struct TU_ATTR_PACKED { - volatile uint8_t connected : 1; - volatile uint8_t addressed : 1; - volatile uint8_t configured : 1; - volatile uint8_t suspended : 1; + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t configured : 1; + volatile uint8_t suspended : 1; }; - //------------- device descriptor -------------// + // Device Descriptor uint8_t ep0_size; uint16_t vid; @@ -95,32 +100,25 @@ typedef struct { uint8_t i_product; uint8_t i_serial; - //------------- configuration descriptor -------------// + // Configuration Descriptor // uint8_t interface_count; // bNumInterfaces alias - //------------- device -------------// - volatile uint8_t state; // device state, value from enum tusbh_device_state_t - + // Endpoint & Interface uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) - uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ) + uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each - struct TU_ATTR_PACKED - { - volatile bool busy : 1; - volatile bool stalled : 1; - volatile bool claimed : 1; - - // TODO merge ep2drv here, 4-bit should be sufficient - }ep_status[CFG_TUH_ENDPOINT_MAX][2]; - - // Mutex for claiming endpoint, only needed when using with preempted RTOS -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_def_t mutexdef; - osal_mutex_t mutex; -#endif + tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2]; } 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 @@ -204,6 +202,9 @@ enum { CONFIG_NUM = 1 }; // default to use configuration 1 // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ +// sum of end device + hub +#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB) + static bool _usbh_initialized = false; // Device with address = 0 for enumeration @@ -211,14 +212,47 @@ static usbh_dev0_t _dev0; // all devices excluding zero-address // hub address start from CFG_TUH_DEVICE_MAX+1 -CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB]; +// TODO: hub can has its own simpler struct to save memory +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; +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 -// role device/host is used by OS NONE for mutex (disable usb isr) +// usbh_int_set is used as mutex in OS NONE config OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t); static osal_queue_t _usbh_q; -CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; +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 -------------// @@ -232,9 +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_cb (uint8_t dev_addr, uint8_t ep_addr, 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) @@ -297,17 +329,18 @@ bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer return true; } -bool tuh_descriptor_device_get(uint8_t daddr, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { + len = tu_min16(len, sizeof(tusb_desc_device_t)); return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb); } -bool tuh_descriptor_configuration_get(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb); } -bool tuh_descriptor_string_get(uint8_t daddr, uint16_t language_id, uint8_t index, +bool tuh_descriptor_get_string(uint8_t daddr, uint16_t language_id, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { tusb_control_request_t const request = @@ -329,36 +362,57 @@ bool tuh_descriptor_string_get(uint8_t daddr, uint16_t language_id, uint8_t inde } // Get manufacturer string descriptor -bool tuh_descriptor_string_manufacturer_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { TU_VERIFY(tuh_mounted(daddr)); usbh_device_t const* dev = get_device(daddr); if (dev->i_manufacturer == 0) { return false; } - return tuh_descriptor_string_get(daddr, language_id, dev->i_manufacturer, buffer, len, complete_cb); + return tuh_descriptor_get_string(daddr, language_id, dev->i_manufacturer, buffer, len, complete_cb); } // Get product string descriptor -bool tuh_descriptor_string_product_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { TU_VERIFY(tuh_mounted(daddr)); usbh_device_t const* dev = get_device(daddr); if (dev->i_product == 0) { return false; } - return tuh_descriptor_string_get(daddr, language_id, dev->i_product, buffer, len, complete_cb); + return tuh_descriptor_get_string(daddr, language_id, dev->i_product, buffer, len, complete_cb); } // Get serial string descriptor -bool tuh_descriptor_string_serial_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) { TU_VERIFY(tuh_mounted(daddr)); usbh_device_t const* dev = get_device(daddr); if (dev->i_serial == 0) { return false; } - return tuh_descriptor_string_get(daddr, language_id, dev->i_serial, buffer, len, complete_cb); + return tuh_descriptor_get_string(daddr, language_id, dev->i_serial, buffer, len, complete_cb); +} + +// Get HID report descriptor +bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb) +{ + TU_LOG2("HID Get Report Descriptor\r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_htole16(TU_U16(desc_type, 0)), + .wIndex = itf_num, + .wLength = len + }; + + return tuh_control_xfer(daddr, &request, buffer, complete_cb); } bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, tuh_control_complete_cb_t complete_cb) @@ -386,6 +440,13 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, tuh_control_comple // CLASS-USBD API (don't require to verify parameters) //--------------------------------------------------------------------+ +static void clear_device(usbh_device_t* dev) +{ + tu_memclr(dev, sizeof(usbh_device_t)); + memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping +} + bool tuh_inited(void) { return _usbh_initialized; @@ -398,29 +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)); - tu_memclr(_usbh_devices, sizeof(_usbh_devices)); - tu_memclr(&_dev0, sizeof(_dev0)); - - //------------- Enumeration & Reporter Task init -------------// + // Event queue _usbh_q = osal_queue_create( &_usbh_qdef ); TU_ASSERT(_usbh_q != NULL); - //------------- Semaphore, Mutex for Control Pipe -------------// - for(uint8_t i=0; i<TU_ARRAY_SIZE(_usbh_devices); i++) - { - usbh_device_t * dev = &_usbh_devices[i]; - -#if CFG_TUSB_OS != OPT_OS_NONE - dev->mutex = osal_mutex_create(&dev->mutexdef); - TU_ASSERT(dev->mutex); +#if TUSB_OPT_MUTEX + // Mutex + _usbh_mutex = osal_mutex_create(&_usbh_mutexdef); + TU_ASSERT(_usbh_mutex); #endif - memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping + // 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; i<TOTAL_DEVICES; i++) + { + clear_device(&_usbh_devices[i]); } - // Class drivers init + // Class drivers for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { TU_LOG2("%s init\r\n", usbh_class_drivers[drv_id].name); @@ -481,7 +543,7 @@ void tuh_task(void) if ( event.connection.hub_addr != 0) { // done with hub, waiting for next data on status pipe - (void) hub_status_pipe_queue( event.connection.hub_addr ); + (void) hub_edpt_status_xfer( event.connection.hub_addr ); } #endif break; @@ -503,7 +565,7 @@ void tuh_task(void) else { usbh_device_t* dev = get_device(event.dev_addr); - dev->ep_status[epnum][ep_dir].busy = false; + dev->ep_status[epnum][ep_dir].busy = 0; dev->ep_status[epnum][ep_dir].claimed = 0; if ( 0 == epnum ) @@ -635,10 +697,219 @@ void hcd_event_device_remove(uint8_t hostid, bool in_isr) hcd_event_handler(&event, in_isr); } +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +{ + // addr0 is always available + if (dev_addr == 0) return true; + + usbh_device_t* dev = get_device(dev_addr); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(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); +#else + return tu_edpt_claim(ep_state, NULL); +#endif +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) +{ + // addr0 is always available + if (dev_addr == 0) return true; + + usbh_device_t* dev = get_device(dev_addr); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; -// a device unplugged on hostid, hub_addr, hub_port -// return true if found and unmounted device, false if cannot find -void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) +#if TUSB_OPT_MUTEX + return tu_edpt_release(ep_state, _usbh_mutex); +#else + return tu_edpt_release(ep_state, NULL); +#endif +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + + TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(dev->ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() + // could return and USBH task can preempt and clear the busy + dev->ep_status[epnum][dir].busy = true; + + if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) + { + TU_LOG2("OK\r\n"); + return true; + }else + { + // HCD error, mark endpoint as ready to allow next transfer + dev->ep_status[epnum][dir].busy = false; + dev->ep_status[epnum][dir].claimed = 0; + TU_LOG2("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) +{ + TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr); + + tusb_desc_endpoint_t ep0_desc = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = max_packet_size, + .bInterval = 0 + }; + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); +} + +bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +{ + usbh_device_t* dev = get_device(dev_addr); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) dev->speed)); + + return hcd_edpt_open(rhport, dev_addr, desc_ep); +} + +bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_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 @@ -651,7 +922,7 @@ void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port 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->state != TUSB_DEVICE_STATE_UNPLUG) + dev->connected) { // Invoke callback before close driver if (tuh_umount_cb) tuh_umount_cb(dev_addr); @@ -664,58 +935,11 @@ void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port } hcd_device_close(rhport, dev_addr); - - // release all endpoints associated with the device - memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping - tu_memclr(dev->ep_status, sizeof(dev->ep_status)); - - dev->state = TUSB_DEVICE_STATE_UNPLUG; - dev->configured = false; - } - } -} - -//--------------------------------------------------------------------+ -// 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)->state == TUSB_DEVICE_STATE_UNPLUG) 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; + clear_device(dev); + // abort on-going control xfer if any + if (_ctrl_xfer.xfer.daddr == dev_addr) set_control_xfer_stage(CONTROL_STAGE_IDLE); } } - - // all interface are configured - if (itf_num == CFG_TUH_INTERFACE_MAX) - { - // Invoke callback if available - if (tuh_mount_cb) tuh_mount_cb(dev_addr); - } } //--------------------------------------------------------------------+ @@ -728,9 +952,9 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) //--------------------------------------------------------------------+ static bool enum_request_addr0_device_desc(void); -static bool enum_request_set_addr(void); - static bool enum_get_addr0_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); + +static bool enum_request_set_addr(void); static bool enum_set_address_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool enum_get_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool enum_get_9byte_config_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); @@ -738,7 +962,48 @@ static bool enum_get_config_desc_complete (uint8_t dev_addr, tusb_control_ static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); +static uint8_t get_new_address(bool is_hub); +static void enum_full_complete(void); + #if CFG_TUH_HUB + +// 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 + +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) +{ + (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)); + + if ( !port_status.status.connection ) + { + // device unplugged while delaying, nothing else to do, queue hub status + enum_full_complete(); + return false; + } + + _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : + (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; + + // Acknowledge Port Reset Change + if (port_status.change.reset) + { + hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset0_complete); + } + + 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; @@ -747,15 +1012,14 @@ static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_reques return true; } -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_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); - enum_request_set_addr(); + osal_task_delay(RESET_DELAY); - // done with hub, waiting for next data on status pipe - (void) hub_status_pipe_queue( _dev0.hub_addr ); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) ); return true; } @@ -777,42 +1041,27 @@ static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request 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_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); - hub_port_status_response_t port_status; - memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); - - if ( !port_status.status.connection ) - { - // device unplugged while delaying, nothing else to do, queue hub status - return hub_status_pipe_queue(dev_addr); - } - - _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : - (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; - - // Acknowledge Port Reset Change - if (port_status.change.reset) - { - hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset0_complete); - } + enum_request_set_addr(); return true; } -#endif + +#endif // hub static bool enum_new_device(hcd_event_t* event) { - _dev0.rhport = event->rhport; // TODO refractor integrate to device_pool + _dev0.rhport = event->rhport; _dev0.hub_addr = event->connection.hub_addr; _dev0.hub_port = event->connection.hub_port; - //------------- connected/disconnected directly with roothub -------------// if (_dev0.hub_addr == 0) { + // connected/disconnected directly with roothub // wait until device is stable TODO non blocking osal_task_delay(RESET_DELAY); @@ -820,19 +1069,19 @@ static bool enum_new_device(hcd_event_t* event) if ( !hcd_port_connect_status(_dev0.rhport) ) return true; _dev0.speed = hcd_port_speed_get(_dev0.rhport ); - TU_LOG2("%s Speed\r\n", tusb_speed_str[_dev0.speed]); + TU_LOG2("%s Speed\r\n", tu_str_speed[_dev0.speed]); enum_request_addr0_device_desc(); } #if CFG_TUH_HUB - //------------- connected/disconnected via hub -------------// else { + // connected/disconnected via external hub // wait until device is stable osal_task_delay(RESET_DELAY); TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete) ); } -#endif // CFG_TUH_HUB +#endif // hub return true; } @@ -845,8 +1094,7 @@ static bool enum_request_addr0_device_desc(void) // Get first 8 bytes of device descriptor for Control Endpoint size TU_LOG2("Get 8 byte of Device Descriptor\r\n"); - - TU_ASSERT(tuh_descriptor_device_get(addr0, _usbh_ctrl_buf, 8, enum_get_addr0_device_desc_complete)); + TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, enum_get_addr0_device_desc_complete)); return true; } @@ -858,11 +1106,8 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r if (XFER_RESULT_SUCCESS != result) { -#if CFG_TUH_HUB - // TODO remove, waiting for next data on status pipe - if (_dev0.hub_addr != 0) hub_status_pipe_queue(_dev0.hub_addr); -#endif - + // stop enumeration, maybe we could retry this + enum_full_complete(); return false; } @@ -875,7 +1120,7 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r if (_dev0.hub_addr == 0) { // connected directly to roothub - hcd_port_reset( _dev0.rhport ); // reset port after 8 byte descriptor + hcd_port_reset( _dev0.rhport ); osal_task_delay(RESET_DELAY); enum_request_set_addr(); @@ -884,21 +1129,15 @@ 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 +#endif // hub return true; } static bool enum_request_set_addr(void) { - uint8_t const addr0 = 0; tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; // Get new address @@ -930,6 +1169,7 @@ static bool enum_request_set_addr(void) .wLength = 0 }; + uint8_t const addr0 = 0; TU_ASSERT( tuh_control_xfer(addr0, &new_request, NULL, enum_set_address_complete) ); return true; @@ -955,7 +1195,7 @@ static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t c // Get full device descriptor TU_LOG2("Get Device Descriptor\r\n"); - TU_ASSERT(tuh_descriptor_device_get(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), enum_get_device_desc_complete)); + TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), enum_get_device_desc_complete)); return true; } @@ -978,7 +1218,7 @@ static bool enum_get_device_desc_complete(uint8_t dev_addr, tusb_control_request // Get 9-byte for total length uint8_t const config_idx = CONFIG_NUM - 1; TU_LOG2("Get Configuration[0] Descriptor (9 bytes)\r\n"); - TU_ASSERT( tuh_descriptor_configuration_get(dev_addr, config_idx, _usbh_ctrl_buf, 9, enum_get_9byte_config_desc_complete) ); + TU_ASSERT( tuh_descriptor_get_configuration(dev_addr, config_idx, _usbh_ctrl_buf, 9, enum_get_9byte_config_desc_complete) ); return true; } @@ -998,7 +1238,7 @@ static bool enum_get_9byte_config_desc_complete(uint8_t dev_addr, tusb_control_r // Get full configuration descriptor uint8_t const config_idx = CONFIG_NUM - 1; TU_LOG2("Get Configuration[0] Descriptor\r\n"); - TU_ASSERT( tuh_descriptor_configuration_get(dev_addr, config_idx, _usbh_ctrl_buf, total_len, enum_get_config_desc_complete) ); + TU_ASSERT( tuh_descriptor_get_configuration(dev_addr, config_idx, _usbh_ctrl_buf, total_len, enum_get_config_desc_complete) ); return true; } @@ -1023,7 +1263,6 @@ static bool enum_set_config_complete(uint8_t dev_addr, tusb_control_request_t co TU_LOG2("Device configured\r\n"); usbh_device_t* dev = get_device(dev_addr); dev->configured = 1; - dev->state = TUSB_DEVICE_STATE_CONFIGURED; // Start the Set Configuration process for interfaces (itf = DRVID_INVALID) // Since driver can perform control transfer within its set_config, this is done asynchronously. @@ -1127,131 +1366,54 @@ static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configura return true; } -//--------------------------------------------------------------------+ -// Endpoint API -//--------------------------------------------------------------------+ - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - usbh_device_t* dev = get_device(dev_addr); -#if CFG_TUSB_OS != OPT_OS_NONE - // pre-check to help reducing mutex lock - TU_VERIFY((dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0)); - osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif - - // can only claim the endpoint if it is not busy and not claimed yet. - bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0); - if (ret) + for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) { - dev->ep_status[epnum][dir].claimed = 1; + // 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; + } } -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_unlock(dev->mutex); -#endif + // all interface are configured + if (itf_num == CFG_TUH_INTERFACE_MAX) + { + enum_full_complete(); - return ret; + // Invoke callback if available + if (tuh_mount_cb) tuh_mount_cb(dev_addr); + } } -// TODO has some duplication code with device, refactor later -bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) +static void enum_full_complete(void) { - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = get_device(dev_addr); - -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif - - // can only release the endpoint if it is claimed and not busy - bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 1); - if (ret) - { - dev->ep_status[epnum][dir].claimed = 0; - } - -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_unlock(dev->mutex); +#if CFG_TUH_HUB + // get next hub status + if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr); #endif - return ret; } -// TODO has some duplication code with device, refactor later -bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +static uint8_t get_new_address(bool is_hub) { - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = get_device(dev_addr); - - TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); - - // Attempt to transfer on a busy endpoint, sound like an race condition ! - TU_ASSERT(dev->ep_status[epnum][dir].busy == 0); - - // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() - // could return and USBH task can preempt and clear the busy - dev->ep_status[epnum][dir].busy = true; + 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); - if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) - { - TU_LOG2("OK\r\n"); - return true; - }else + for (uint8_t i=0; i < count; i++) { - // HCD error, mark endpoint as ready to allow next transfer - dev->ep_status[epnum][dir].busy = false; - dev->ep_status[epnum][dir].claimed = 0; - TU_LOG2("failed\r\n"); - TU_BREAKPOINT(); - return false; + uint8_t const addr = start + i; + if (!get_device(addr)->connected) return addr; } + return ADDR_INVALID; } -static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) -{ - TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr); - - tusb_desc_endpoint_t ep0_desc = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = max_packet_size, - .bInterval = 0 - }; - - return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); -} - -bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) -{ - usbh_device_t* dev = get_device(dev_addr); - TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) dev->speed)); - - return hcd_edpt_open(rhport, dev_addr, desc_ep); -} - -bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = get_device(dev_addr); - - return dev->ep_status[epnum][dir].busy; -} - - - #endif diff --git a/src/host/usbh.h b/src/host/usbh.h index 9465d1d1b..eec2ed702 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -80,7 +80,8 @@ static inline bool tuh_ready(uint8_t daddr) return tuh_mounted(daddr) && !tuh_suspended(daddr); } -// Carry out control transfer +// Carry out a control transfer +// true on success, false if there is on-going control trasnfer bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb); // Set Configuration @@ -94,23 +95,26 @@ bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get device descriptor -bool tuh_descriptor_device_get(uint8_t daddr, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); +bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get configuration descriptor -bool tuh_descriptor_configuration_get(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); +bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get string descriptor -bool tuh_descriptor_string_get(uint8_t daddr, uint16_t language_id, uint8_t index, +bool tuh_descriptor_get_string(uint8_t daddr, uint16_t language_id, uint8_t index, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get manufacturer string descriptor -bool tuh_descriptor_string_manufacturer_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); +bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get product string descriptor -bool tuh_descriptor_string_product_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); +bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); // Get serial string descriptor -bool tuh_descriptor_string_serial_get(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); +bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); + +// Get HID report descriptor +bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, void* buffer, uint16_t len, tuh_control_complete_cb_t complete_cb); //--------------------------------------------------------------------+ // APPLICATION CALLBACK 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 deleted file mode 100644 index d034eec7f..000000000 --- a/src/host/usbh_control.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020, 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_TUH_ENABLED - -#include "tusb.h" -#include "usbh_classdriver.h" - -enum -{ - STAGE_SETUP, - STAGE_DATA, - STAGE_ACK -}; - -typedef struct -{ - tusb_control_request_t request TU_ATTR_ALIGNED(4); - - uint8_t stage; - uint8_t* buffer; - tuh_control_complete_cb_t complete_cb; -} usbh_control_xfer_t; - -static usbh_control_xfer_t _ctrl_xfer; - -//CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN -//static uint8_t _tuh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM DECLARATION -//--------------------------------------------------------------------+ - -bool tuh_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.stage = STAGE_SETUP; - _ctrl_xfer.complete_cb = complete_cb; - - TU_LOG2("Control Setup (addr = %u): ", dev_addr); - TU_LOG2_VAR(request); - TU_LOG2("\r\n"); - - // Send setup packet - TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.request) ); - - return true; -} - -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); -} - -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.request; - - if (XFER_RESULT_SUCCESS != result) - { - TU_LOG2("Control failed: result = %d\r\n", result); - - // terminate transfer if any stage failed - _xfer_complete(dev_addr, result); - }else - { - switch(_ctrl_xfer.stage) - { - case STAGE_SETUP: - _ctrl_xfer.stage = 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 STAGE_DATA: - _ctrl_xfer.stage = STAGE_ACK; - - if (request->wLength) - { - TU_LOG2("Control data (addr = %u):\r\n", 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 STAGE_ACK: - _xfer_complete(dev_addr, result); - break; - - default: return false; - } - } - - return true; -} - -#endif |
