Merge remote-tracking branch 'remotes/hathach/master' into work
This commit is contained in:
		
							
								
								
									
										2
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,3 +6,5 @@ | ||||
| /dataSources.local.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
| # GitHub Copilot persisted chat sessions | ||||
| /copilot/chatSessions | ||||
|   | ||||
| @@ -1,8 +1,14 @@ | ||||
| set(CMAKE_SYSTEM_NAME Generic) | ||||
|  | ||||
| set(CMAKE_C_COMPILER "arm-none-eabi-gcc") | ||||
| set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") | ||||
| set(CMAKE_ASM_COMPILER "arm-none-eabi-gcc") | ||||
| if (NOT DEFINED CMAKE_C_COMPILER) | ||||
|   set(CMAKE_C_COMPILER "arm-none-eabi-gcc") | ||||
| endif () | ||||
|  | ||||
| if (NOT DEFINED CMAKE_CXX_COMPILER) | ||||
|   set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") | ||||
| endif () | ||||
|  | ||||
| set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) | ||||
|  | ||||
| set(CMAKE_SIZE "arm-none-eabi-size" CACHE FILEPATH "") | ||||
| set(CMAKE_OBJCOPY "arm-none-eabi-objcopy" CACHE FILEPATH "") | ||||
|   | ||||
| @@ -31,12 +31,24 @@ | ||||
| #include "bsp/board_api.h" | ||||
| #include "tusb.h" | ||||
|  | ||||
| //------------- prototypes -------------// | ||||
| /* Blink pattern | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum { | ||||
|   BLINK_NOT_MOUNTED = 250, | ||||
|   BLINK_MOUNTED = 1000, | ||||
|   BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
|  | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
|  | ||||
| static void led_blinking_task(void); | ||||
| static void cdc_task(void); | ||||
|  | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) | ||||
| { | ||||
| int main(void) { | ||||
|   board_init(); | ||||
|  | ||||
|   // init device stack on configured roothub port | ||||
| @@ -46,28 +58,23 @@ int main(void) | ||||
|     board_init_after_tusb(); | ||||
|   } | ||||
|  | ||||
|   while (1) | ||||
|   { | ||||
|   while (1) { | ||||
|     tud_task(); // tinyusb device task | ||||
|     cdc_task(); | ||||
|     led_blinking_task(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // echo to either Serial0 or Serial1 | ||||
| // with Serial0 as all lower case, Serial1 as all upper case | ||||
| static void echo_serial_port(uint8_t itf, uint8_t buf[], uint32_t count) | ||||
| { | ||||
| static void echo_serial_port(uint8_t itf, uint8_t buf[], uint32_t count) { | ||||
|   uint8_t const case_diff = 'a' - 'A'; | ||||
|  | ||||
|   for(uint32_t i=0; i<count; i++) | ||||
|   { | ||||
|     if (itf == 0) | ||||
|     { | ||||
|   for (uint32_t i = 0; i < count; i++) { | ||||
|     if (itf == 0) { | ||||
|       // echo back 1st port as lower case | ||||
|       if (isupper(buf[i])) buf[i] += case_diff; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     } else { | ||||
|       // echo back 2nd port as upper case | ||||
|       if (islower(buf[i])) buf[i] -= case_diff; | ||||
|     } | ||||
| @@ -77,21 +84,29 @@ static void echo_serial_port(uint8_t itf, uint8_t buf[], uint32_t count) | ||||
|   tud_cdc_n_write_flush(itf); | ||||
| } | ||||
|  | ||||
| // Invoked when device is mounted | ||||
| void tud_mount_cb(void) { | ||||
|   blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
|  | ||||
| // Invoked when device is unmounted | ||||
| void tud_umount_cb(void) { | ||||
|   blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
|  | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // USB CDC | ||||
| //--------------------------------------------------------------------+ | ||||
| static void cdc_task(void) | ||||
| { | ||||
| static void cdc_task(void) { | ||||
|   uint8_t itf; | ||||
|  | ||||
|   for (itf = 0; itf < CFG_TUD_CDC; itf++) | ||||
|   { | ||||
|   for (itf = 0; itf < CFG_TUD_CDC; itf++) { | ||||
|     // connected() check for DTR bit | ||||
|     // Most but not all terminal client set this when making connection | ||||
|     // if ( tud_cdc_n_connected(itf) ) | ||||
|     { | ||||
|       if ( tud_cdc_n_available(itf) ) | ||||
|       { | ||||
|       if (tud_cdc_n_available(itf)) { | ||||
|         uint8_t buf[64]; | ||||
|  | ||||
|         uint32_t count = tud_cdc_n_read(itf, buf, sizeof(buf)); | ||||
| @@ -103,3 +118,18 @@ static void cdc_task(void) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // BLINKING TASK | ||||
| //--------------------------------------------------------------------+ | ||||
| void led_blinking_task(void) { | ||||
|   static uint32_t start_ms = 0; | ||||
|   static bool led_state = false; | ||||
|  | ||||
|   // Blink every interval ms | ||||
|   if (board_millis() - start_ms < blink_interval_ms) return; // not enough time | ||||
|   start_ms += blink_interval_ms; | ||||
|  | ||||
|   board_led_write(led_state); | ||||
|   led_state = 1 - led_state; // toggle | ||||
| } | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_RP2040 | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use max3421 as host controller | ||||
|   // #define CFG_TUH_MAX3421       1 // use max3421 as host controller | ||||
|  | ||||
|   // host roothub port is 1 if using either pio-usb or max3421 | ||||
|   #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_RP2040 | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use max3421 as host controller | ||||
|   // #define CFG_TUH_MAX3421       1 // use max3421 as host controller | ||||
|  | ||||
|   // host roothub port is 1 if using either pio-usb or max3421 | ||||
|   #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) | ||||
|   | ||||
| @@ -76,7 +76,7 @@ | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_RP2040 | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use max3421 as host controller | ||||
|   // #define CFG_TUH_MAX3421       1 // use max3421 as host controller | ||||
|  | ||||
|   // host roothub port is 1 if using either pio-usb or max3421 | ||||
|   #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_RP2040 | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use max3421 as host controller | ||||
|   // #define CFG_TUH_MAX3421       1 // use max3421 as host controller | ||||
|  | ||||
|   // host roothub port is 1 if using either pio-usb or max3421 | ||||
|   #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_RP2040 | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller | ||||
|   // #define CFG_TUH_RPI_PIO_USB   1 // use max3421 as host controller | ||||
|   // #define CFG_TUH_MAX3421       1 // use max3421 as host controller | ||||
|  | ||||
|   // host roothub port is 1 if using either pio-usb or max3421 | ||||
|   #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) | ||||
|   | ||||
| @@ -39,12 +39,11 @@ | ||||
| #endif | ||||
|  | ||||
| #define TU_LOG_DRV(...)   TU_LOG(CFG_TUH_HID_LOG_LEVEL, __VA_ARGS__) | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
| typedef struct { | ||||
|   uint8_t daddr; | ||||
|  | ||||
|   uint8_t itf_num; | ||||
| @@ -55,7 +54,7 @@ typedef struct | ||||
|   uint8_t itf_protocol;   // None, Keyboard, Mouse | ||||
|   uint8_t protocol_mode;  // Boot (0) or Report protocol (1) | ||||
|  | ||||
|   uint8_t  report_desc_type; | ||||
|   uint8_t report_desc_type; | ||||
|   uint16_t report_desc_len; | ||||
|  | ||||
|   uint16_t epin_size; | ||||
| @@ -73,79 +72,57 @@ tu_static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT; | ||||
| //--------------------------------------------------------------------+ | ||||
| // Helper | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| TU_ATTR_ALWAYS_INLINE static inline | ||||
| hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) | ||||
| { | ||||
| TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) { | ||||
|   TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL); | ||||
|   hidh_interface_t* p_hid = &_hidh_itf[idx]; | ||||
|   return (p_hid->daddr == daddr) ? p_hid : NULL; | ||||
| } | ||||
|  | ||||
| // Get instance ID by endpoint address | ||||
| static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) | ||||
| { | ||||
|   for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ ) | ||||
|   { | ||||
|     hidh_interface_t const * p_hid = &_hidh_itf[idx]; | ||||
|  | ||||
|     if ( p_hid->daddr == daddr && | ||||
|          (p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr) ) | ||||
|     { | ||||
| static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) { | ||||
|   for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) { | ||||
|     hidh_interface_t const* p_hid = &_hidh_itf[idx]; | ||||
|     if (p_hid->daddr == daddr && | ||||
|         (p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr)) { | ||||
|       return idx; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return TUSB_INDEX_INVALID_8; | ||||
| } | ||||
|  | ||||
| static hidh_interface_t* find_new_itf(void) | ||||
| { | ||||
|   for(uint8_t i=0; i<CFG_TUH_HID; i++) | ||||
|   { | ||||
| static hidh_interface_t* find_new_itf(void) { | ||||
|   for (uint8_t i = 0; i < CFG_TUH_HID; i++) { | ||||
|     if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i]; | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Interface API | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| uint8_t tuh_hid_itf_get_count(uint8_t daddr) | ||||
| { | ||||
| uint8_t tuh_hid_itf_get_count(uint8_t daddr) { | ||||
|   uint8_t count = 0; | ||||
|  | ||||
|   for(uint8_t i=0; i<CFG_TUH_HID; i++) | ||||
|   { | ||||
|   for (uint8_t i = 0; i < CFG_TUH_HID; i++) { | ||||
|     if (_hidh_itf[i].daddr == daddr) count++; | ||||
|   } | ||||
|  | ||||
|   return count; | ||||
| } | ||||
|  | ||||
| uint8_t tuh_hid_itf_get_total_count(void) | ||||
| { | ||||
| uint8_t tuh_hid_itf_get_total_count(void) { | ||||
|   uint8_t count = 0; | ||||
|  | ||||
|   for(uint8_t i=0; i<CFG_TUH_HID; i++) | ||||
|   { | ||||
|   for (uint8_t i = 0; i < CFG_TUH_HID; i++) { | ||||
|     if (_hidh_itf[i].daddr != 0) count++; | ||||
|   } | ||||
|  | ||||
|   return count; | ||||
| } | ||||
|  | ||||
| bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) | ||||
| { | ||||
| bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|   return p_hid->mounted; | ||||
| } | ||||
|  | ||||
| bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) | ||||
| { | ||||
| bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid && info); | ||||
|  | ||||
| @@ -153,34 +130,30 @@ bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) | ||||
|  | ||||
|   // re-construct descriptor | ||||
|   tusb_desc_interface_t* desc = &info->desc; | ||||
|   desc->bLength            = sizeof(tusb_desc_interface_t); | ||||
|   desc->bDescriptorType    = TUSB_DESC_INTERFACE; | ||||
|   desc->bLength = sizeof(tusb_desc_interface_t); | ||||
|   desc->bDescriptorType = TUSB_DESC_INTERFACE; | ||||
|  | ||||
|   desc->bInterfaceNumber   = p_hid->itf_num; | ||||
|   desc->bAlternateSetting  = 0; | ||||
|   desc->bNumEndpoints      = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u)); | ||||
|   desc->bInterfaceClass    = TUSB_CLASS_HID; | ||||
|   desc->bInterfaceNumber = p_hid->itf_num; | ||||
|   desc->bAlternateSetting = 0; | ||||
|   desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u)); | ||||
|   desc->bInterfaceClass = TUSB_CLASS_HID; | ||||
|   desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE); | ||||
|   desc->bInterfaceProtocol = p_hid->itf_protocol; | ||||
|   desc->iInterface         = 0; // not used yet | ||||
|   desc->iInterface = 0; // not used yet | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) | ||||
| { | ||||
|   for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ ) | ||||
|   { | ||||
|     hidh_interface_t const * p_hid = &_hidh_itf[idx]; | ||||
|  | ||||
|     if ( p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx; | ||||
| uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) { | ||||
|   for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) { | ||||
|     hidh_interface_t const* p_hid = &_hidh_itf[idx]; | ||||
|     if (p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx; | ||||
|   } | ||||
|  | ||||
|   return TUSB_INDEX_INVALID_8; | ||||
| } | ||||
|  | ||||
| uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) | ||||
| { | ||||
| uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   return p_hid ? p_hid->itf_protocol : 0; | ||||
| } | ||||
| @@ -188,29 +161,24 @@ uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) | ||||
| //--------------------------------------------------------------------+ | ||||
| // Control Endpoint API | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) | ||||
| { | ||||
| uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   return p_hid ? p_hid->protocol_mode : 0; | ||||
| } | ||||
|  | ||||
| static void set_protocol_complete(tuh_xfer_t* xfer) | ||||
| { | ||||
| static void set_protocol_complete(tuh_xfer_t* xfer) { | ||||
|   uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); | ||||
|   uint8_t const daddr   = xfer->daddr; | ||||
|   uint8_t const idx     = tuh_hid_itf_get_index(daddr, itf_num); | ||||
|   uint8_t const daddr = xfer->daddr; | ||||
|   uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num); | ||||
|  | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid, ); | ||||
|   TU_VERIFY(p_hid,); | ||||
|  | ||||
|   if (XFER_RESULT_SUCCESS == xfer->result) | ||||
|   { | ||||
|   if (XFER_RESULT_SUCCESS == xfer->result) { | ||||
|     p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); | ||||
|   } | ||||
|  | ||||
|   if (tuh_hid_set_protocol_complete_cb) | ||||
|   { | ||||
|   if (tuh_hid_set_protocol_complete_cb) { | ||||
|     tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode); | ||||
|   } | ||||
| } | ||||
| @@ -219,123 +187,109 @@ void tuh_hid_set_default_protocol(uint8_t protocol) { | ||||
|   _hidh_default_protocol = protocol; | ||||
| } | ||||
|  | ||||
| static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data) | ||||
| { | ||||
| static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, | ||||
|                                tuh_xfer_cb_t complete_cb, uintptr_t user_data) { | ||||
|   TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol); | ||||
|  | ||||
|   tusb_control_request_t const request = | ||||
|   { | ||||
|     .bmRequestType_bit = | ||||
|     { | ||||
|       .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|       .type      = TUSB_REQ_TYPE_CLASS, | ||||
|       .direction = TUSB_DIR_OUT | ||||
|     }, | ||||
|     .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, | ||||
|     .wValue   = protocol, | ||||
|     .wIndex   = itf_num, | ||||
|     .wLength  = 0 | ||||
|   tusb_control_request_t const request = { | ||||
|       .bmRequestType_bit = { | ||||
|           .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|           .type      = TUSB_REQ_TYPE_CLASS, | ||||
|           .direction = TUSB_DIR_OUT | ||||
|       }, | ||||
|       .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, | ||||
|       .wValue   = protocol, | ||||
|       .wIndex   = itf_num, | ||||
|       .wLength  = 0 | ||||
|   }; | ||||
|  | ||||
|   tuh_xfer_t xfer = | ||||
|   { | ||||
|     .daddr       = daddr, | ||||
|     .ep_addr     = 0, | ||||
|     .setup       = &request, | ||||
|     .buffer      = NULL, | ||||
|     .complete_cb = complete_cb, | ||||
|     .user_data   = user_data | ||||
|   tuh_xfer_t xfer = { | ||||
|       .daddr       = daddr, | ||||
|       .ep_addr     = 0, | ||||
|       .setup       = &request, | ||||
|       .buffer      = NULL, | ||||
|       .complete_cb = complete_cb, | ||||
|       .user_data   = user_data | ||||
|   }; | ||||
|  | ||||
|   return tuh_control_xfer(&xfer); | ||||
| } | ||||
|  | ||||
| bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) | ||||
| { | ||||
| bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE); | ||||
|  | ||||
|   return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0); | ||||
| } | ||||
|  | ||||
| static void set_report_complete(tuh_xfer_t* xfer) | ||||
| { | ||||
| static void set_report_complete(tuh_xfer_t* xfer) { | ||||
|   TU_LOG_DRV("HID Set Report complete\r\n"); | ||||
|  | ||||
|   if (tuh_hid_set_report_complete_cb) | ||||
|   { | ||||
|   if (tuh_hid_set_report_complete_cb) { | ||||
|     uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); | ||||
|     uint8_t const idx     = tuh_hid_itf_get_index(xfer->daddr, itf_num); | ||||
|     uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num); | ||||
|  | ||||
|     uint8_t const report_type = tu_u16_high(xfer->setup->wValue); | ||||
|     uint8_t const report_id   = tu_u16_low(xfer->setup->wValue); | ||||
|     uint8_t const report_id = tu_u16_low(xfer->setup->wValue); | ||||
|  | ||||
|     tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type, | ||||
|                                    (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) | ||||
| { | ||||
| bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|  | ||||
|   TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len); | ||||
|  | ||||
|   tusb_control_request_t const request = | ||||
|   { | ||||
|     .bmRequestType_bit = | ||||
|     { | ||||
|       .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|       .type      = TUSB_REQ_TYPE_CLASS, | ||||
|       .direction = TUSB_DIR_OUT | ||||
|     }, | ||||
|     .bRequest = HID_REQ_CONTROL_SET_REPORT, | ||||
|     .wValue   = tu_htole16(tu_u16(report_type, report_id)), | ||||
|     .wIndex   = tu_htole16((uint16_t)p_hid->itf_num), | ||||
|     .wLength  = len | ||||
|   tusb_control_request_t const request = { | ||||
|       .bmRequestType_bit = { | ||||
|           .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|           .type      = TUSB_REQ_TYPE_CLASS, | ||||
|           .direction = TUSB_DIR_OUT | ||||
|       }, | ||||
|       .bRequest = HID_REQ_CONTROL_SET_REPORT, | ||||
|       .wValue   = tu_htole16(tu_u16(report_type, report_id)), | ||||
|       .wIndex   = tu_htole16((uint16_t) p_hid->itf_num), | ||||
|       .wLength  = len | ||||
|   }; | ||||
|  | ||||
|   tuh_xfer_t xfer = | ||||
|   { | ||||
|     .daddr       = daddr, | ||||
|     .ep_addr     = 0, | ||||
|     .setup       = &request, | ||||
|     .buffer      = report, | ||||
|     .complete_cb = set_report_complete, | ||||
|     .user_data   = 0 | ||||
|   tuh_xfer_t xfer = { | ||||
|       .daddr       = daddr, | ||||
|       .ep_addr     = 0, | ||||
|       .setup       = &request, | ||||
|       .buffer      = report, | ||||
|       .complete_cb = set_report_complete, | ||||
|       .user_data   = 0 | ||||
|   }; | ||||
|  | ||||
|   return tuh_control_xfer(&xfer); | ||||
| } | ||||
|  | ||||
| static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) | ||||
| { | ||||
| static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, | ||||
|                            tuh_xfer_cb_t complete_cb, uintptr_t user_data) { | ||||
|   // SET IDLE request, device can stall if not support this request | ||||
|   TU_LOG_DRV("HID Set Idle \r\n"); | ||||
|  | ||||
|   tusb_control_request_t const request = | ||||
|   { | ||||
|     .bmRequestType_bit = | ||||
|     { | ||||
|       .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|       .type      = TUSB_REQ_TYPE_CLASS, | ||||
|       .direction = TUSB_DIR_OUT | ||||
|     }, | ||||
|     .bRequest = HID_REQ_CONTROL_SET_IDLE, | ||||
|     .wValue   = tu_htole16(idle_rate), | ||||
|     .wIndex   = tu_htole16((uint16_t)itf_num), | ||||
|     .wLength  = 0 | ||||
|   tusb_control_request_t const request = { | ||||
|       .bmRequestType_bit = { | ||||
|           .recipient = TUSB_REQ_RCPT_INTERFACE, | ||||
|           .type      = TUSB_REQ_TYPE_CLASS, | ||||
|           .direction = TUSB_DIR_OUT | ||||
|       }, | ||||
|       .bRequest = HID_REQ_CONTROL_SET_IDLE, | ||||
|       .wValue   = tu_htole16(idle_rate), | ||||
|       .wIndex   = tu_htole16((uint16_t) itf_num), | ||||
|       .wLength  = 0 | ||||
|   }; | ||||
|  | ||||
|   tuh_xfer_t xfer = | ||||
|   { | ||||
|     .daddr       = daddr, | ||||
|     .ep_addr     = 0, | ||||
|     .setup       = &request, | ||||
|     .buffer      = NULL, | ||||
|     .complete_cb = complete_cb, | ||||
|     .user_data   = user_data | ||||
|   tuh_xfer_t xfer = { | ||||
|       .daddr       = daddr, | ||||
|       .ep_addr     = 0, | ||||
|       .setup       = &request, | ||||
|       .buffer      = NULL, | ||||
|       .complete_cb = complete_cb, | ||||
|       .user_data   = user_data | ||||
|   }; | ||||
|  | ||||
|   return tuh_control_xfer(&xfer); | ||||
| @@ -346,68 +300,60 @@ static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, t | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Check if HID interface is ready to receive report | ||||
| bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) | ||||
| { | ||||
| bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|  | ||||
|   return !usbh_edpt_busy(dev_addr, p_hid->ep_in); | ||||
| } | ||||
|  | ||||
| bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) | ||||
| { | ||||
| bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|  | ||||
|   // claim endpoint | ||||
|   TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_in) ); | ||||
|   TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in)); | ||||
|  | ||||
|   if ( !usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size) ) | ||||
|   { | ||||
|   if (!usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size)) { | ||||
|     usbh_edpt_release(daddr, p_hid->ep_in); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) | ||||
| { | ||||
| bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|   return tuh_edpt_abort_xfer(dev_addr, p_hid->ep_in); | ||||
| } | ||||
|  | ||||
| bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|   return !usbh_edpt_busy(dev_addr, p_hid->ep_out); | ||||
| } | ||||
|  | ||||
| bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) | ||||
| { | ||||
| bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) { | ||||
|   TU_LOG_DRV("HID Send Report %d\r\n", report_id); | ||||
|  | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|  | ||||
|   if (p_hid->ep_out == 0) | ||||
|   { | ||||
|   if (p_hid->ep_out == 0) { | ||||
|     // This HID does not have an out endpoint (other than control) | ||||
|     return false; | ||||
|   } | ||||
|   else if (len > CFG_TUH_HID_EPOUT_BUFSIZE || | ||||
|            (report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) | ||||
|   { | ||||
|   } else if (len > CFG_TUH_HID_EPOUT_BUFSIZE || | ||||
|              (report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) { | ||||
|     // ep_out buffer is not large enough to hold contents | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   // claim endpoint | ||||
|   TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_out) ); | ||||
|   TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_out)); | ||||
|  | ||||
|   if (report_id == 0) | ||||
|   { | ||||
|   if (report_id == 0) { | ||||
|     // No report ID in transmission | ||||
|     memcpy(&p_hid->epout_buf[0], report, len); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|   } else { | ||||
|     p_hid->epout_buf[0] = report_id; | ||||
|     memcpy(&p_hid->epout_buf[1], report, len); | ||||
|     ++len; // 1 more byte for report_id | ||||
| @@ -415,8 +361,7 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo | ||||
|  | ||||
|   TU_LOG3_MEM(p_hid->epout_buf, len, 2); | ||||
|  | ||||
|   if ( !usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len) ) | ||||
|   { | ||||
|   if (!usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len)) { | ||||
|     usbh_edpt_release(daddr, p_hid->ep_out); | ||||
|     return false; | ||||
|   } | ||||
| @@ -427,13 +372,11 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo | ||||
| //--------------------------------------------------------------------+ | ||||
| // USBH API | ||||
| //--------------------------------------------------------------------+ | ||||
| void hidh_init(void) | ||||
| { | ||||
| void hidh_init(void) { | ||||
|   tu_memclr(_hidh_itf, sizeof(_hidh_itf)); | ||||
| } | ||||
|  | ||||
| bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) | ||||
| { | ||||
| bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { | ||||
|   (void) result; | ||||
|  | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
| @@ -442,28 +385,25 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid); | ||||
|  | ||||
|   if ( dir == TUSB_DIR_IN ) | ||||
|   { | ||||
|   if (dir == TUSB_DIR_IN) { | ||||
|     TU_LOG_DRV("  Get Report callback (%u, %u)\r\n", daddr, idx); | ||||
|     TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2); | ||||
|     tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes); | ||||
|   }else | ||||
|   { | ||||
|     if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes); | ||||
|   } else { | ||||
|     if (tuh_hid_report_sent_cb) { | ||||
|       tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void hidh_close(uint8_t daddr) | ||||
| { | ||||
|   for(uint8_t i=0; i<CFG_TUH_HID; i++) | ||||
|   { | ||||
| void hidh_close(uint8_t daddr) { | ||||
|   for (uint8_t i = 0; i < CFG_TUH_HID; i++) { | ||||
|     hidh_interface_t* p_hid = &_hidh_itf[i]; | ||||
|     if (p_hid->daddr == daddr) | ||||
|     { | ||||
|     if (p_hid->daddr == daddr) { | ||||
|       TU_LOG_DRV("  HIDh close addr = %u index = %u\r\n", daddr, i); | ||||
|       if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i); | ||||
|       if (tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i); | ||||
|       p_hid->daddr = 0; | ||||
|       p_hid->mounted = false; | ||||
|     } | ||||
| @@ -474,25 +414,22 @@ void hidh_close(uint8_t daddr) | ||||
| // Enumeration | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) | ||||
| { | ||||
| bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) { | ||||
|   (void) rhport; | ||||
|   (void) max_len; | ||||
|  | ||||
|   TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); | ||||
|  | ||||
|   TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber); | ||||
|  | ||||
|   // len = interface + hid + n*endpoints | ||||
|   uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + | ||||
|                                        desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); | ||||
|   TU_ASSERT(max_len >= drv_len); | ||||
|  | ||||
|   uint8_t const *p_desc = (uint8_t const *) desc_itf; | ||||
|   uint8_t const* p_desc = (uint8_t const*) desc_itf; | ||||
|  | ||||
|   //------------- HID descriptor -------------// | ||||
|   p_desc = tu_desc_next(p_desc); | ||||
|   tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; | ||||
|   tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc; | ||||
|   TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType); | ||||
|  | ||||
|   hidh_interface_t* p_hid = find_new_itf(); | ||||
| @@ -501,38 +438,33 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_ | ||||
|  | ||||
|   //------------- Endpoint Descriptors -------------// | ||||
|   p_desc = tu_desc_next(p_desc); | ||||
|   tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; | ||||
|   tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; | ||||
|  | ||||
|   for(int i = 0; i < desc_itf->bNumEndpoints; i++) | ||||
|   { | ||||
|   for (int i = 0; i < desc_itf->bNumEndpoints; i++) { | ||||
|     TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); | ||||
|     TU_ASSERT( tuh_edpt_open(daddr, desc_ep) ); | ||||
|     TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); | ||||
|  | ||||
|     if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) | ||||
|     { | ||||
|       p_hid->ep_in     = desc_ep->bEndpointAddress; | ||||
|     if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { | ||||
|       p_hid->ep_in = desc_ep->bEndpointAddress; | ||||
|       p_hid->epin_size = tu_edpt_packet_size(desc_ep); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       p_hid->ep_out     = desc_ep->bEndpointAddress; | ||||
|     } else { | ||||
|       p_hid->ep_out = desc_ep->bEndpointAddress; | ||||
|       p_hid->epout_size = tu_edpt_packet_size(desc_ep); | ||||
|     } | ||||
|  | ||||
|     p_desc = tu_desc_next(p_desc); | ||||
|     desc_ep = (tusb_desc_endpoint_t const *) p_desc; | ||||
|     desc_ep = (tusb_desc_endpoint_t const*) p_desc; | ||||
|   } | ||||
|  | ||||
|   p_hid->itf_num   = desc_itf->bInterfaceNumber; | ||||
|   p_hid->itf_num = desc_itf->bInterfaceNumber; | ||||
|  | ||||
|   // Assume bNumDescriptors = 1 | ||||
|   p_hid->report_desc_type = desc_hid->bReportType; | ||||
|   p_hid->report_desc_len  = tu_unaligned_read16(&desc_hid->wReportLength); | ||||
|   p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength); | ||||
|  | ||||
|   // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config | ||||
|   p_hid->protocol_mode = _hidh_default_protocol; | ||||
|   if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) | ||||
|   { | ||||
|   if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) { | ||||
|     p_hid->itf_protocol = desc_itf->bInterfaceProtocol; | ||||
|   } | ||||
|  | ||||
| @@ -553,15 +485,14 @@ enum { | ||||
| static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len); | ||||
| static void process_set_config(tuh_xfer_t* xfer); | ||||
|  | ||||
| bool hidh_set_config(uint8_t daddr, uint8_t itf_num) | ||||
| { | ||||
| bool hidh_set_config(uint8_t daddr, uint8_t itf_num) { | ||||
|   tusb_control_request_t request; | ||||
|   request.wIndex = tu_htole16((uint16_t) itf_num); | ||||
|  | ||||
|   tuh_xfer_t xfer; | ||||
|   xfer.daddr     = daddr; | ||||
|   xfer.result    = XFER_RESULT_SUCCESS; | ||||
|   xfer.setup     = &request; | ||||
|   xfer.daddr = daddr; | ||||
|   xfer.result = XFER_RESULT_SUCCESS; | ||||
|   xfer.setup = &request; | ||||
|   xfer.user_data = CONFG_SET_IDLE; | ||||
|  | ||||
|   // fake request to kick-off the set config process | ||||
| @@ -570,71 +501,67 @@ bool hidh_set_config(uint8_t daddr, uint8_t itf_num) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static void process_set_config(tuh_xfer_t* xfer) | ||||
| { | ||||
| static void process_set_config(tuh_xfer_t* xfer) { | ||||
|   // Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well | ||||
|   // therefore we could ignore its result | ||||
|   if ( !(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE || | ||||
|          xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL) ) | ||||
|   { | ||||
|     TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); | ||||
|   if (!(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE || | ||||
|         xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL)) { | ||||
|     TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS,); | ||||
|   } | ||||
|  | ||||
|   uintptr_t const state = xfer->user_data; | ||||
|   uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); | ||||
|   uint8_t const daddr   = xfer->daddr; | ||||
|   uint8_t const daddr = xfer->daddr; | ||||
|  | ||||
|   uint8_t const idx       = tuh_hid_itf_get_index(daddr, itf_num); | ||||
|   uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num); | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid, ); | ||||
|   TU_VERIFY(p_hid,); | ||||
|  | ||||
|   switch(state) | ||||
|   { | ||||
|     case CONFG_SET_IDLE: | ||||
|     { | ||||
|   switch (state) { | ||||
|     case CONFG_SET_IDLE: { | ||||
|       // Idle rate = 0 mean only report when there is changes | ||||
|       const uint16_t idle_rate = 0; | ||||
|       const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; | ||||
|       const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) | ||||
|                                    ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; | ||||
|       _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); | ||||
|       break; | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     case CONFIG_SET_PROTOCOL: | ||||
|       _hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC); | ||||
|     break; | ||||
|       break; | ||||
|  | ||||
|     case CONFIG_GET_REPORT_DESC: | ||||
|       // Get Report Descriptor if possible | ||||
|       // using usbh enumeration buffer since report descriptor can be very long | ||||
|       if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) | ||||
|       { | ||||
|       if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) { | ||||
|         TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len); | ||||
|  | ||||
|         // Driver is mounted without report descriptor | ||||
|         config_driver_mount_complete(daddr, idx, NULL, 0); | ||||
|       }else | ||||
|       { | ||||
|         tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, usbh_get_enum_buf(), p_hid->report_desc_len, process_set_config, CONFIG_COMPLETE); | ||||
|       } else { | ||||
|         tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, | ||||
|                                       usbh_get_enum_buf(), p_hid->report_desc_len, | ||||
|                                       process_set_config, CONFIG_COMPLETE); | ||||
|       } | ||||
|       break; | ||||
|  | ||||
|     case CONFIG_COMPLETE: | ||||
|     { | ||||
|     case CONFIG_COMPLETE: { | ||||
|       uint8_t const* desc_report = usbh_get_enum_buf(); | ||||
|       uint16_t const desc_len    = tu_le16toh(xfer->setup->wLength); | ||||
|       uint16_t const desc_len = tu_le16toh(xfer->setup->wLength); | ||||
|  | ||||
|       config_driver_mount_complete(daddr, idx, desc_report, desc_len); | ||||
|       break; | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     default: break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) | ||||
| { | ||||
| static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) { | ||||
|   hidh_interface_t* p_hid = get_hid_itf(daddr, idx); | ||||
|   TU_VERIFY(p_hid, ); | ||||
|   TU_VERIFY(p_hid,); | ||||
|   p_hid->mounted = true; | ||||
|  | ||||
|   // enumeration is complete | ||||
| @@ -648,21 +575,19 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con | ||||
| // Report Descriptor Parser | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) | ||||
| { | ||||
| uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, | ||||
|                                         uint8_t const* desc_report, uint16_t desc_len) { | ||||
|   // Report Item 6.2.2.2 USB HID 1.11 | ||||
|   union TU_ATTR_PACKED | ||||
|   { | ||||
|   union TU_ATTR_PACKED { | ||||
|     uint8_t byte; | ||||
|     struct TU_ATTR_PACKED | ||||
|     { | ||||
|         uint8_t size : 2; | ||||
|         uint8_t type : 2; | ||||
|         uint8_t tag  : 4; | ||||
|     struct TU_ATTR_PACKED { | ||||
|       uint8_t size : 2; | ||||
|       uint8_t type : 2; | ||||
|       uint8_t tag : 4; | ||||
|     }; | ||||
|   } header; | ||||
|  | ||||
|   tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t)); | ||||
|   tu_memclr(report_info_arr, arr_count * sizeof(tuh_hid_report_info_t)); | ||||
|  | ||||
|   uint8_t report_num = 0; | ||||
|   tuh_hid_report_info_t* info = report_info_arr; | ||||
| @@ -672,114 +597,105 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, | ||||
| //  uint8_t ri_report_size = 0; | ||||
|  | ||||
|   uint8_t ri_collection_depth = 0; | ||||
|  | ||||
|   while(desc_len && report_num < arr_count) | ||||
|   { | ||||
|   while (desc_len && report_num < arr_count) { | ||||
|     header.byte = *desc_report++; | ||||
|     desc_len--; | ||||
|  | ||||
|     uint8_t const tag  = header.tag; | ||||
|     uint8_t const tag = header.tag; | ||||
|     uint8_t const type = header.type; | ||||
|     uint8_t const size = header.size; | ||||
|  | ||||
|     uint8_t const data8 = desc_report[0]; | ||||
|  | ||||
|     TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size); | ||||
|     for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]); | ||||
|     for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]); | ||||
|     TU_LOG(3, "\r\n"); | ||||
|  | ||||
|     switch(type) | ||||
|     { | ||||
|     switch (type) { | ||||
|       case RI_TYPE_MAIN: | ||||
|         switch (tag) | ||||
|         { | ||||
|         switch (tag) { | ||||
|           case RI_MAIN_INPUT: break; | ||||
|           case RI_MAIN_OUTPUT: break; | ||||
|           case RI_MAIN_FEATURE: break; | ||||
|  | ||||
|           case RI_MAIN_COLLECTION: | ||||
|             ri_collection_depth++; | ||||
|           break; | ||||
|             break; | ||||
|  | ||||
|           case RI_MAIN_COLLECTION_END: | ||||
|             ri_collection_depth--; | ||||
|             if (ri_collection_depth == 0) | ||||
|             { | ||||
|             if (ri_collection_depth == 0) { | ||||
|               info++; | ||||
|               report_num++; | ||||
|             } | ||||
|           break; | ||||
|             break; | ||||
|  | ||||
|           default: break; | ||||
|           default:break; | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case RI_TYPE_GLOBAL: | ||||
|         switch(tag) | ||||
|         { | ||||
|         switch (tag) { | ||||
|           case RI_GLOBAL_USAGE_PAGE: | ||||
|             // only take in account the "usage page" before REPORT ID | ||||
|             if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size); | ||||
|           break; | ||||
|             if (ri_collection_depth == 0) memcpy(&info->usage_page, desc_report, size); | ||||
|             break; | ||||
|  | ||||
|           case RI_GLOBAL_LOGICAL_MIN   : break; | ||||
|           case RI_GLOBAL_LOGICAL_MAX   : break; | ||||
|           case RI_GLOBAL_PHYSICAL_MIN  : break; | ||||
|           case RI_GLOBAL_PHYSICAL_MAX  : break; | ||||
|           case RI_GLOBAL_LOGICAL_MIN: break; | ||||
|           case RI_GLOBAL_LOGICAL_MAX: break; | ||||
|           case RI_GLOBAL_PHYSICAL_MIN: break; | ||||
|           case RI_GLOBAL_PHYSICAL_MAX: break; | ||||
|  | ||||
|           case RI_GLOBAL_REPORT_ID: | ||||
|             info->report_id = data8; | ||||
|           break; | ||||
|             break; | ||||
|  | ||||
|           case RI_GLOBAL_REPORT_SIZE: | ||||
| //            ri_report_size = data8; | ||||
|           break; | ||||
|             break; | ||||
|  | ||||
|           case RI_GLOBAL_REPORT_COUNT: | ||||
| //            ri_report_count = data8; | ||||
|           break; | ||||
|             break; | ||||
|  | ||||
|           case RI_GLOBAL_UNIT_EXPONENT : break; | ||||
|           case RI_GLOBAL_UNIT          : break; | ||||
|           case RI_GLOBAL_PUSH          : break; | ||||
|           case RI_GLOBAL_POP           : break; | ||||
|           case RI_GLOBAL_UNIT_EXPONENT: break; | ||||
|           case RI_GLOBAL_UNIT: break; | ||||
|           case RI_GLOBAL_PUSH: break; | ||||
|           case RI_GLOBAL_POP: break; | ||||
|  | ||||
|           default: break; | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case RI_TYPE_LOCAL: | ||||
|         switch(tag) | ||||
|         { | ||||
|         switch (tag) { | ||||
|           case RI_LOCAL_USAGE: | ||||
|             // only take in account the "usage" before starting REPORT ID | ||||
|             if ( ri_collection_depth == 0 ) info->usage = data8; | ||||
|           break; | ||||
|             if (ri_collection_depth == 0) info->usage = data8; | ||||
|             break; | ||||
|  | ||||
|           case RI_LOCAL_USAGE_MIN        : break; | ||||
|           case RI_LOCAL_USAGE_MAX        : break; | ||||
|           case RI_LOCAL_DESIGNATOR_INDEX : break; | ||||
|           case RI_LOCAL_DESIGNATOR_MIN   : break; | ||||
|           case RI_LOCAL_DESIGNATOR_MAX   : break; | ||||
|           case RI_LOCAL_STRING_INDEX     : break; | ||||
|           case RI_LOCAL_STRING_MIN       : break; | ||||
|           case RI_LOCAL_STRING_MAX       : break; | ||||
|           case RI_LOCAL_DELIMITER        : break; | ||||
|           case RI_LOCAL_USAGE_MIN: break; | ||||
|           case RI_LOCAL_USAGE_MAX: break; | ||||
|           case RI_LOCAL_DESIGNATOR_INDEX: break; | ||||
|           case RI_LOCAL_DESIGNATOR_MIN: break; | ||||
|           case RI_LOCAL_DESIGNATOR_MAX: break; | ||||
|           case RI_LOCAL_STRING_INDEX: break; | ||||
|           case RI_LOCAL_STRING_MIN: break; | ||||
|           case RI_LOCAL_STRING_MAX: break; | ||||
|           case RI_LOCAL_DELIMITER: break; | ||||
|           default: break; | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       // error | ||||
|         // error | ||||
|       default: break; | ||||
|     } | ||||
|  | ||||
|     desc_report += size; | ||||
|     desc_len    -= size; | ||||
|     desc_len -= size; | ||||
|   } | ||||
|  | ||||
|   for ( uint8_t i = 0; i < report_num; i++ ) | ||||
|   { | ||||
|     info = report_info_arr+i; | ||||
|   for (uint8_t i = 0; i < report_num; i++) { | ||||
|     info = report_info_arr + i; | ||||
|     TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
| #include "hid.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  extern "C" { | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| @@ -47,10 +47,9 @@ | ||||
| #endif | ||||
|  | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   uint8_t  report_id; | ||||
|   uint8_t  usage; | ||||
| typedef struct { | ||||
|   uint8_t report_id; | ||||
|   uint8_t usage; | ||||
|   uint16_t usage_page; | ||||
|  | ||||
|   // TODO still use the endpoint size for now | ||||
| @@ -86,7 +85,8 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx); | ||||
|  | ||||
| // Parse report descriptor into array of report_info struct and return number of reports. | ||||
| // For complicated report, application should write its own parser. | ||||
| uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED; | ||||
| TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, | ||||
|                                                        uint8_t const* desc_report, uint16_t desc_len); | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Control Endpoint API | ||||
| @@ -107,7 +107,8 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol); | ||||
|  | ||||
| // Set Report using control endpoint | ||||
| // report_type is either Input, Output or Feature, (value from hid_report_type_t) | ||||
| bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len); | ||||
| bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, | ||||
|                         void* report, uint16_t len); | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Interrupt Endpoint API | ||||
| @@ -121,6 +122,9 @@ bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx); | ||||
| // - false if failed to queue the transfer e.g endpoint is busy | ||||
| bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx); | ||||
|  | ||||
| // Abort receiving report on Interrupt Endpoint | ||||
| bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx); | ||||
|  | ||||
| // Check if HID interface is ready to send report | ||||
| bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx); | ||||
|  | ||||
| @@ -159,11 +163,11 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx | ||||
| //--------------------------------------------------------------------+ | ||||
| // Internal Class Driver API | ||||
| //--------------------------------------------------------------------+ | ||||
| void hidh_init       (void); | ||||
| bool hidh_open       (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); | ||||
| bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num); | ||||
| bool hidh_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); | ||||
| void hidh_close      (uint8_t dev_addr); | ||||
| void hidh_init(void); | ||||
| bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len); | ||||
| bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num); | ||||
| bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); | ||||
| void hidh_close(uint8_t dev_addr); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|   | ||||
| @@ -207,6 +207,24 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des | ||||
|   return cur; | ||||
| } | ||||
|  | ||||
| /** Find the first descriptor of two given types | ||||
|  * | ||||
|  * @param[in] beg        The head of descriptor byte array. | ||||
|  * @param[in] end        The tail of descriptor byte array. | ||||
|  * @param[in] desc_type_0 The first target descriptor type. | ||||
|  * @param[in] desc_type_1 The second target descriptor type. | ||||
|  * | ||||
|  * @return The pointer for interface descriptor. | ||||
|  * @retval end   did not found interface descriptor */ | ||||
| static void const* _find_desc_2_type(void const *beg, void const *end, uint_fast8_t desc_type_0, uint_fast8_t desc_type_1) | ||||
| { | ||||
|   void const *cur = beg; | ||||
|   while ((cur < end) && (desc_type_0 != tu_desc_type(cur)) && (desc_type_1 != tu_desc_type(cur))) { | ||||
|     cur = tu_desc_next(cur); | ||||
|   } | ||||
|   return cur; | ||||
| } | ||||
|  | ||||
| /** Find the first descriptor specified by the arguments | ||||
|  * | ||||
|  * @param[in] beg        The head of descriptor byte array. | ||||
| @@ -233,6 +251,10 @@ static void const* _find_desc_3(void const *beg, void const *end, | ||||
| } | ||||
|  | ||||
| /** Return the next interface descriptor which has another interface number. | ||||
|  *  If there are multiple VC interfaces, there will be an IAD descriptor before | ||||
|  *  the next interface descriptor. Check both the IAD descriptor and the interface | ||||
|  *  descriptor. | ||||
|  *  3.1 Descriptor Layout Overview | ||||
|  * | ||||
|  * @param[in] beg     The head of descriptor byte array. | ||||
|  * @param[in] end     The tail of descriptor byte array. | ||||
| @@ -245,7 +267,7 @@ static void const* _next_desc_itf(void const *beg, void const *end) | ||||
|   uint_fast8_t itfnum = ((tusb_desc_interface_t const*)cur)->bInterfaceNumber; | ||||
|   while ((cur < end) && | ||||
|          (itfnum == ((tusb_desc_interface_t const*)cur)->bInterfaceNumber)) { | ||||
|     cur = _find_desc(tu_desc_next(cur), end, TUSB_DESC_INTERFACE); | ||||
|     cur = _find_desc_2_type(tu_desc_next(cur), end, TUSB_DESC_INTERFACE, TUSB_DESC_INTERFACE_ASSOCIATION); | ||||
|   } | ||||
|   return cur; | ||||
| } | ||||
| @@ -1216,7 +1238,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin | ||||
|        * host may not issue set_interface so open the streaming interface here. */ | ||||
|       uint8_t const *sbeg = (uint8_t const*)itf_desc + stm->desc.beg; | ||||
|       uint8_t const *send = (uint8_t const*)itf_desc + stm->desc.end; | ||||
|       if (end == _find_desc_itf(sbeg, send, _desc_itfnum(sbeg), 1)) { | ||||
|       if (send == _find_desc_itf(sbeg, send, _desc_itfnum(sbeg), 1)) { | ||||
|         TU_VERIFY(_open_vs_itf(rhport, stm, 0), 0); | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -68,9 +68,9 @@ typedef struct { | ||||
|     uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute | ||||
|     uint8_t self_powered          : 1; // configuration descriptor's attribute | ||||
|   }; | ||||
|  | ||||
|   volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) | ||||
|   uint8_t speed; | ||||
|   volatile uint8_t setup_count; | ||||
|  | ||||
|   uint8_t itf2drv[CFG_TUD_INTERFACE_MAX];   // map interface number to driver (0xff is invalid) | ||||
|   uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each | ||||
| @@ -372,13 +372,13 @@ bool tud_inited(void) { | ||||
|   return _usbd_rhport != RHPORT_INVALID; | ||||
| } | ||||
|  | ||||
| bool tud_init (uint8_t rhport) | ||||
| { | ||||
| bool tud_init(uint8_t rhport) { | ||||
|   // skip if already initialized | ||||
|   if ( tud_inited() ) return true; | ||||
|   if (tud_inited()) return true; | ||||
|  | ||||
|   TU_LOG_USBD("USBD init on controller %u\r\n", rhport); | ||||
|   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t)); | ||||
|   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t)); | ||||
|   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t)); | ||||
|   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); | ||||
|  | ||||
| @@ -395,15 +395,13 @@ bool tud_init (uint8_t rhport) | ||||
|   TU_ASSERT(_usbd_q); | ||||
|  | ||||
|   // Get application driver if available | ||||
|   if ( usbd_app_driver_get_cb ) | ||||
|   { | ||||
|   if (usbd_app_driver_get_cb) { | ||||
|     _app_driver = usbd_app_driver_get_cb(&_app_driver_count); | ||||
|   } | ||||
|  | ||||
|   // Init class drivers | ||||
|   for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) | ||||
|   { | ||||
|     usbd_class_driver_t const * driver = get_driver(i); | ||||
|   for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { | ||||
|     usbd_class_driver_t const* driver = get_driver(i); | ||||
|     TU_ASSERT(driver); | ||||
|     TU_LOG_USBD("%s init\r\n", driver->name); | ||||
|     driver->init(); | ||||
| @@ -418,31 +416,26 @@ bool tud_init (uint8_t rhport) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static void configuration_reset(uint8_t rhport) | ||||
| { | ||||
|   for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ ) | ||||
|   { | ||||
|     usbd_class_driver_t const * driver = get_driver(i); | ||||
|     TU_ASSERT(driver, ); | ||||
| static void configuration_reset(uint8_t rhport) { | ||||
|   for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { | ||||
|     usbd_class_driver_t const* driver = get_driver(i); | ||||
|     TU_ASSERT(driver,); | ||||
|     driver->reset(rhport); | ||||
|   } | ||||
|  | ||||
|   tu_varclr(&_usbd_dev); | ||||
|   memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping | ||||
|   memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping | ||||
|   memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping | ||||
| } | ||||
|  | ||||
| static void usbd_reset(uint8_t rhport) | ||||
| { | ||||
| static void usbd_reset(uint8_t rhport) { | ||||
|   configuration_reset(rhport); | ||||
|   usbd_control_reset(); | ||||
| } | ||||
|  | ||||
| bool tud_task_event_ready(void) | ||||
| { | ||||
| bool tud_task_event_ready(void) { | ||||
|   // Skip if stack is not initialized | ||||
|   if ( !tud_inited() ) return false; | ||||
|  | ||||
|   if (!tud_inited()) return false; | ||||
|   return !osal_queue_empty(_usbd_q); | ||||
| } | ||||
|  | ||||
| @@ -450,56 +443,52 @@ bool tud_task_event_ready(void) | ||||
|  * This top level thread manages all device controller event and delegates events to class-specific drivers. | ||||
|  * This should be called periodically within the mainloop or rtos thread. | ||||
|  * | ||||
|    @code | ||||
|     int main(void) | ||||
|     { | ||||
|     int main(void) { | ||||
|       application_init(); | ||||
|       tusb_init(); | ||||
|  | ||||
|       while(1) // the mainloop | ||||
|       { | ||||
|       while(1) { // the mainloop | ||||
|         application_code(); | ||||
|         tud_task(); // tinyusb device task | ||||
|       } | ||||
|     } | ||||
|     @endcode | ||||
|  */ | ||||
| void tud_task_ext(uint32_t timeout_ms, bool in_isr) | ||||
| { | ||||
| void tud_task_ext(uint32_t timeout_ms, bool in_isr) { | ||||
|   (void) in_isr; // not implemented yet | ||||
|  | ||||
|   // Skip if stack is not initialized | ||||
|   if ( !tud_inited() ) return; | ||||
|   if (!tud_inited()) return; | ||||
|  | ||||
|   // Loop until there is no more events in the queue | ||||
|   while (1) | ||||
|   { | ||||
|   while (1) { | ||||
|     dcd_event_t event; | ||||
|     if ( !osal_queue_receive(_usbd_q, &event, timeout_ms) ) return; | ||||
|     if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return; | ||||
|  | ||||
| #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL | ||||
|     if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup | ||||
|     TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); | ||||
| #endif | ||||
|  | ||||
|     switch ( event.event_id ) | ||||
|     { | ||||
|     switch (event.event_id) { | ||||
|       case DCD_EVENT_BUS_RESET: | ||||
|         TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]); | ||||
|         usbd_reset(event.rhport); | ||||
|         _usbd_dev.speed = event.bus_reset.speed; | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case DCD_EVENT_UNPLUGGED: | ||||
|         TU_LOG_USBD("\r\n"); | ||||
|         usbd_reset(event.rhport); | ||||
|  | ||||
|         // invoke callback | ||||
|         if (tud_umount_cb) tud_umount_cb(); | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case DCD_EVENT_SETUP_RECEIVED: | ||||
|         _usbd_dev.setup_count--; | ||||
|         TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); | ||||
|         if (_usbd_dev.setup_count) { | ||||
|           TU_LOG_USBD("  Skipped since there is other SETUP in queue\r\n"); | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|         // Mark as connected after receiving 1st setup packet. | ||||
|         // But it is easier to set it every time instead of wasting time to check then set | ||||
| @@ -508,81 +497,72 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) | ||||
|         // mark both in & out control as free | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0; | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0; | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = 0; | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0; | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0; | ||||
|         _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0; | ||||
|  | ||||
|         // Process control request | ||||
|         if ( !process_control_request(event.rhport, &event.setup_received) ) | ||||
|         { | ||||
|         if (!process_control_request(event.rhport, &event.setup_received)) { | ||||
|           TU_LOG_USBD("  Stall EP0\r\n"); | ||||
|           // Failed -> stall both control endpoint IN and OUT | ||||
|           dcd_edpt_stall(event.rhport, 0); | ||||
|           dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case DCD_EVENT_XFER_COMPLETE: | ||||
|       { | ||||
|       case DCD_EVENT_XFER_COMPLETE: { | ||||
|         // Invoke the class callback associated with the endpoint address | ||||
|         uint8_t const ep_addr = event.xfer_complete.ep_addr; | ||||
|         uint8_t const epnum   = tu_edpt_number(ep_addr); | ||||
|         uint8_t const ep_dir  = tu_edpt_dir(ep_addr); | ||||
|         uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|         uint8_t const ep_dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|         TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); | ||||
|  | ||||
|         _usbd_dev.ep_status[epnum][ep_dir].busy = 0; | ||||
|         _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; | ||||
|  | ||||
|         if ( 0 == epnum ) | ||||
|         { | ||||
|           usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete | ||||
|           .len); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|           usbd_class_driver_t const * driver = get_driver( _usbd_dev.ep2drv[epnum][ep_dir] ); | ||||
|           TU_ASSERT(driver, ); | ||||
|         if (0 == epnum) { | ||||
|           usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, | ||||
|                                event.xfer_complete.len); | ||||
|         } else { | ||||
|           usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); | ||||
|           TU_ASSERT(driver,); | ||||
|  | ||||
|           TU_LOG_USBD("  %s xfer callback\r\n", driver->name); | ||||
|           driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       break; | ||||
|  | ||||
|       case DCD_EVENT_SUSPEND: | ||||
|         // NOTE: When plugging/unplugging device, the D+/D- state are unstable and | ||||
|         // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event | ||||
|         // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected | ||||
|         if ( _usbd_dev.connected ) | ||||
|         { | ||||
|         if (_usbd_dev.connected) { | ||||
|           TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en); | ||||
|           if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); | ||||
|         }else | ||||
|         { | ||||
|         } else { | ||||
|           TU_LOG_USBD(" Skipped\r\n"); | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case DCD_EVENT_RESUME: | ||||
|         if ( _usbd_dev.connected ) | ||||
|         { | ||||
|         if (_usbd_dev.connected) { | ||||
|           TU_LOG_USBD("\r\n"); | ||||
|           if (tud_resume_cb) tud_resume_cb(); | ||||
|         }else | ||||
|         { | ||||
|         } else { | ||||
|           TU_LOG_USBD(" Skipped\r\n"); | ||||
|         } | ||||
|       break; | ||||
|         break; | ||||
|  | ||||
|       case USBD_EVENT_FUNC_CALL: | ||||
|         TU_LOG_USBD("\r\n"); | ||||
|         if ( event.func_call.func ) event.func_call.func(event.func_call.param); | ||||
|       break; | ||||
|         if (event.func_call.func) event.func_call.func(event.func_call.param); | ||||
|         break; | ||||
|  | ||||
|       case DCD_EVENT_SOF: | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|       break; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
| #if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO | ||||
| @@ -597,8 +577,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Helper to invoke class driver control request handler | ||||
| static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) | ||||
| { | ||||
| static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) { | ||||
|   usbd_control_set_complete_callback(driver->control_xfer_cb); | ||||
|   TU_LOG_USBD("  %s control request\r\n", driver->name); | ||||
|   return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); | ||||
| @@ -606,15 +585,12 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri | ||||
|  | ||||
| // This handles the actual request and its response. | ||||
| // return false will cause its caller to stall control endpoint | ||||
| static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
| static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { | ||||
|   usbd_control_set_complete_callback(NULL); | ||||
|  | ||||
|   TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); | ||||
|  | ||||
|   // Vendor request | ||||
|   if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) | ||||
|   { | ||||
|   if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { | ||||
|     TU_VERIFY(tud_vendor_control_xfer_cb); | ||||
|  | ||||
|     usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); | ||||
| @@ -622,19 +598,16 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|   } | ||||
|  | ||||
| #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL | ||||
|   if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) | ||||
|   { | ||||
|   if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) { | ||||
|     TU_LOG_USBD("  %s", tu_str_std_request[p_request->bRequest]); | ||||
|     if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG_USBD("\r\n"); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   switch ( p_request->bmRequestType_bit.recipient ) | ||||
|   { | ||||
|   switch ( p_request->bmRequestType_bit.recipient ) { | ||||
|     //------------- Device Requests e.g in enumeration -------------// | ||||
|     case TUSB_REQ_RCPT_DEVICE: | ||||
|       if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) | ||||
|       { | ||||
|       if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) { | ||||
|         uint8_t const itf = tu_u16_low(p_request->wIndex); | ||||
|         TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); | ||||
|  | ||||
| @@ -645,15 +618,13 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|         return invoke_class_control(rhport, driver, p_request); | ||||
|       } | ||||
|  | ||||
|       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) | ||||
|       { | ||||
|       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { | ||||
|         // Non standard request is not supported | ||||
|         TU_BREAKPOINT(); | ||||
|         return false; | ||||
|       } | ||||
|  | ||||
|       switch ( p_request->bRequest ) | ||||
|       { | ||||
|       switch ( p_request->bRequest ) { | ||||
|         case TUSB_REQ_SET_ADDRESS: | ||||
|           // 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 | ||||
| @@ -664,22 +635,18 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|           _usbd_dev.addressed = 1; | ||||
|         break; | ||||
|  | ||||
|         case TUSB_REQ_GET_CONFIGURATION: | ||||
|         { | ||||
|         case TUSB_REQ_GET_CONFIGURATION: { | ||||
|           uint8_t cfg_num = _usbd_dev.cfg_num; | ||||
|           tud_control_xfer(rhport, p_request, &cfg_num, 1); | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|         case TUSB_REQ_SET_CONFIGURATION: | ||||
|         { | ||||
|         case TUSB_REQ_SET_CONFIGURATION: { | ||||
|           uint8_t const cfg_num = (uint8_t) p_request->wValue; | ||||
|  | ||||
|           // Only process if new configure is different | ||||
|           if (_usbd_dev.cfg_num != cfg_num) | ||||
|           { | ||||
|             if ( _usbd_dev.cfg_num ) | ||||
|             { | ||||
|           if (_usbd_dev.cfg_num != cfg_num) { | ||||
|             if ( _usbd_dev.cfg_num ) { | ||||
|               // already configured: need to clear all endpoints and driver first | ||||
|               TU_LOG_USBD("  Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); | ||||
|  | ||||
| @@ -694,15 +661,11 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|             } | ||||
|  | ||||
|             // Handle the new configuration and execute the corresponding callback | ||||
|             if ( cfg_num ) | ||||
|             { | ||||
|             if ( cfg_num ) { | ||||
|               // switch to new configuration if not zero | ||||
|               TU_ASSERT( process_set_config(rhport, cfg_num) ); | ||||
|  | ||||
|               if ( tud_mount_cb ) tud_mount_cb(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|             } else { | ||||
|               if ( tud_umount_cb ) tud_umount_cb(); | ||||
|             } | ||||
|           } | ||||
| @@ -738,15 +701,14 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|           tud_control_status(rhport, p_request); | ||||
|         break; | ||||
|  | ||||
|         case TUSB_REQ_GET_STATUS: | ||||
|         { | ||||
|         case TUSB_REQ_GET_STATUS: { | ||||
|           // Device status bit mask | ||||
|           // - Bit 0: Self Powered | ||||
|           // - Bit 1: Remote Wakeup enabled | ||||
|           uint16_t status = (uint16_t) ((_usbd_dev.self_powered ? 1u : 0u) | (_usbd_dev.remote_wakeup_en ? 2u : 0u)); | ||||
|           tud_control_xfer(rhport, p_request, &status, 2); | ||||
|           break; | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|         // Unknown/Unsupported request | ||||
|         default: TU_BREAKPOINT(); return false; | ||||
| @@ -754,8 +716,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|     break; | ||||
|  | ||||
|     //------------- Class/Interface Specific Request -------------// | ||||
|     case TUSB_REQ_RCPT_INTERFACE: | ||||
|     { | ||||
|     case TUSB_REQ_RCPT_INTERFACE: { | ||||
|       uint8_t const itf = tu_u16_low(p_request->wIndex); | ||||
|       TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); | ||||
|  | ||||
| @@ -764,25 +725,21 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|  | ||||
|       // all requests to Interface (STD or Class) is forwarded to class driver. | ||||
|       // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE | ||||
|       if ( !invoke_class_control(rhport, driver, p_request) ) | ||||
|       { | ||||
|       if ( !invoke_class_control(rhport, driver, p_request) ) { | ||||
|         // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class | ||||
|         // driver doesn't use alternate settings or implement this | ||||
|         TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); | ||||
|  | ||||
|         switch(p_request->bRequest) | ||||
|         { | ||||
|         switch(p_request->bRequest) { | ||||
|           case TUSB_REQ_GET_INTERFACE: | ||||
|           case TUSB_REQ_SET_INTERFACE: | ||||
|             // Clear complete callback if driver set since it can also stall the request. | ||||
|             usbd_control_set_complete_callback(NULL); | ||||
|  | ||||
|             if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) | ||||
|             { | ||||
|             if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) { | ||||
|               uint8_t alternate = 0; | ||||
|               tud_control_xfer(rhport, p_request, &alternate, 1); | ||||
|             }else | ||||
|             { | ||||
|             }else { | ||||
|               tud_control_status(rhport, p_request); | ||||
|             } | ||||
|           break; | ||||
| @@ -790,54 +747,42 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|           default: return false; | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     //------------- Endpoint Request -------------// | ||||
|     case TUSB_REQ_RCPT_ENDPOINT: | ||||
|     { | ||||
|     case TUSB_REQ_RCPT_ENDPOINT: { | ||||
|       uint8_t const ep_addr = tu_u16_low(p_request->wIndex); | ||||
|       uint8_t const ep_num  = tu_edpt_number(ep_addr); | ||||
|       uint8_t const ep_dir  = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|       TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); | ||||
|  | ||||
|       usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]); | ||||
|  | ||||
|       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) | ||||
|       { | ||||
|       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { | ||||
|         // Forward class request to its driver | ||||
|         TU_VERIFY(driver); | ||||
|         return invoke_class_control(rhport, driver, p_request); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|       } else { | ||||
|         // Handle STD request to endpoint | ||||
|         switch ( p_request->bRequest ) | ||||
|         { | ||||
|           case TUSB_REQ_GET_STATUS: | ||||
|           { | ||||
|         switch ( p_request->bRequest ) { | ||||
|           case TUSB_REQ_GET_STATUS: { | ||||
|             uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; | ||||
|             tud_control_xfer(rhport, p_request, &status, 2); | ||||
|           } | ||||
|           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 ) | ||||
|               { | ||||
|           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 | ||||
|               { | ||||
|               }else { | ||||
|                 usbd_edpt_stall(rhport, ep_addr); | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             if (driver) | ||||
|             { | ||||
|             if (driver) { | ||||
|               // 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 | ||||
|  | ||||
| @@ -853,14 +798,18 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | ||||
|           break; | ||||
|  | ||||
|           // Unknown/Unsupported request | ||||
|           default: TU_BREAKPOINT(); return false; | ||||
|           default: | ||||
|             TU_BREAKPOINT(); | ||||
|             return false; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     // Unknown recipient | ||||
|     default: TU_BREAKPOINT(); return false; | ||||
|     default: | ||||
|       TU_BREAKPOINT(); | ||||
|       return false; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| @@ -1120,6 +1069,11 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) | ||||
|       // skip osal queue for SOF in usbd task | ||||
|       break; | ||||
|  | ||||
|     case DCD_EVENT_SETUP_RECEIVED: | ||||
|       _usbd_dev.setup_count++; | ||||
|       send = true; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       send = true; | ||||
|       break; | ||||
| @@ -1185,8 +1139,7 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) { | ||||
| // USBD Endpoint API | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) | ||||
| { | ||||
| bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); | ||||
| @@ -1195,37 +1148,34 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) | ||||
|   return dcd_edpt_open(rhport, desc_ep); | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // TODO add this check later, also make sure we don't starve an out endpoint while suspending | ||||
|   // TU_VERIFY(tud_ready()); | ||||
|  | ||||
|   uint8_t const epnum       = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir         = tu_edpt_dir(ep_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 = &_usbd_dev.ep_status[epnum][dir]; | ||||
|  | ||||
|   return tu_edpt_claim(ep_state, _usbd_mutex); | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint8_t const epnum       = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir         = tu_edpt_dir(ep_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 = &_usbd_dev.ep_status[epnum][dir]; | ||||
|  | ||||
|   return tu_edpt_release(ep_state, _usbd_mutex); | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) | ||||
| { | ||||
| bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   // TODO skip ready() check for now since enumeration also use this API | ||||
|   // TU_VERIFY(tud_ready()); | ||||
| @@ -1239,11 +1189,9 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t | ||||
|   // could return and USBD task can preempt and clear the busy | ||||
|   _usbd_dev.ep_status[epnum][dir].busy = 1; | ||||
|  | ||||
|   if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ) | ||||
|   { | ||||
|   if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes)) { | ||||
|     return true; | ||||
|   }else | ||||
|   { | ||||
|   } else { | ||||
|     // DCD error, mark endpoint as ready to allow next transfer | ||||
|     _usbd_dev.ep_status[epnum][dir].busy = 0; | ||||
|     _usbd_dev.ep_status[epnum][dir].claimed = 0; | ||||
| @@ -1257,12 +1205,11 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t | ||||
| // bytes should be written and second to keep the return value free to give back a boolean | ||||
| // success message. If total_bytes is too big, the FIFO will copy only what is available | ||||
| // into the USB buffer! | ||||
| bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) | ||||
| { | ||||
| bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   TU_LOG_USBD("  Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes); | ||||
|  | ||||
| @@ -1273,12 +1220,10 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 | ||||
|   // and usbd task can preempt and clear the busy | ||||
|   _usbd_dev.ep_status[epnum][dir].busy = 1; | ||||
|  | ||||
|   if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) | ||||
|   { | ||||
|   if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) { | ||||
|     TU_LOG_USBD("OK\r\n"); | ||||
|     return true; | ||||
|   }else | ||||
|   { | ||||
|   } else { | ||||
|     // DCD error, mark endpoint as ready to allow next transfer | ||||
|     _usbd_dev.ep_status[epnum][dir].busy = 0; | ||||
|     _usbd_dev.ep_status[epnum][dir].claimed = 0; | ||||
| @@ -1288,26 +1233,23 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   return _usbd_dev.ep_status[epnum][dir].busy; | ||||
| } | ||||
|  | ||||
| void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   // only stalled if currently cleared | ||||
|   if ( !_usbd_dev.ep_status[epnum][dir].stalled ) | ||||
|   { | ||||
|   if (!_usbd_dev.ep_status[epnum][dir].stalled) { | ||||
|     TU_LOG_USBD("    Stall EP %02X\r\n", ep_addr); | ||||
|     dcd_edpt_stall(rhport, ep_addr); | ||||
|     _usbd_dev.ep_status[epnum][dir].stalled = 1; | ||||
| @@ -1315,16 +1257,14 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
|   } | ||||
| } | ||||
|  | ||||
| void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   // only clear if currently stalled | ||||
|   if ( _usbd_dev.ep_status[epnum][dir].stalled ) | ||||
|   { | ||||
|   if (_usbd_dev.ep_status[epnum][dir].stalled) { | ||||
|     TU_LOG_USBD("    Clear Stall EP %02X\r\n", ep_addr); | ||||
|     dcd_edpt_clear_stall(rhport, ep_addr); | ||||
|     _usbd_dev.ep_status[epnum][dir].stalled = 0; | ||||
| @@ -1332,31 +1272,27 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   return _usbd_dev.ep_status[epnum][dir].stalled; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * usbd_edpt_close will disable an endpoint. | ||||
|  * | ||||
|  * In progress transfers on this EP may be delivered after this call. | ||||
|  * | ||||
|  */ | ||||
| void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   TU_ASSERT(dcd_edpt_close, /**/); | ||||
|   TU_LOG_USBD("  CLOSING Endpoint: 0x%02X\r\n", ep_addr); | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   dcd_edpt_close(rhport, ep_addr); | ||||
|   _usbd_dev.ep_status[epnum][dir].stalled = 0; | ||||
| @@ -1366,8 +1302,7 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) | ||||
|   return; | ||||
| } | ||||
|  | ||||
| void usbd_sof_enable(uint8_t rhport, bool en) | ||||
| { | ||||
| void usbd_sof_enable(uint8_t rhport, bool en) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more. | ||||
| @@ -1375,8 +1310,7 @@ void usbd_sof_enable(uint8_t rhport, bool en) | ||||
|   dcd_sof_enable(rhport, en); | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) | ||||
| { | ||||
| bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   TU_ASSERT(dcd_edpt_iso_alloc); | ||||
| @@ -1385,12 +1319,11 @@ bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packe | ||||
|   return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size); | ||||
| } | ||||
|  | ||||
| bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) | ||||
| { | ||||
| bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { | ||||
|   rhport = _usbd_rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress); | ||||
|   uint8_t const dir   = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||
|   uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||
|  | ||||
|   TU_ASSERT(dcd_edpt_iso_activate); | ||||
|   TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); | ||||
|   | ||||
							
								
								
									
										605
									
								
								src/host/usbh.c
									
									
									
									
									
								
							
							
						
						
									
										605
									
								
								src/host/usbh.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,6 +2,7 @@ | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 tfx2001 (2479727366@qq.com) | ||||
|  * Copyright (c) 2020 yekai (2857693944@qq.com) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
| @@ -111,9 +112,12 @@ TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_de | ||||
| } | ||||
|  | ||||
| TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec) { | ||||
|  | ||||
|     rt_tick_t tick = rt_tick_from_millisecond((rt_int32_t) msec); | ||||
| #if RT_VERSION_MAJOR >= 5 | ||||
|     return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) > 0; | ||||
| #else | ||||
|     return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) == RT_EOK; | ||||
| #endif  /* RT_VERSION_MAJOR >= 5 */ | ||||
| } | ||||
|  | ||||
| TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) { | ||||
|   | ||||
| @@ -31,16 +31,26 @@ | ||||
| #if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) ||  (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED) | ||||
|  | ||||
| // Espressif | ||||
| #include "freertos/xtensa_api.h" | ||||
| #include "xtensa_api.h" | ||||
| #include "esp_intr_alloc.h" | ||||
| #include "esp_log.h" | ||||
| #include "soc/dport_reg.h" | ||||
| #include "soc/gpio_sig_map.h" | ||||
| #include "soc/usb_periph.h" | ||||
| #include "soc/usb_reg.h" | ||||
| #include "soc/usb_struct.h" | ||||
| #include "soc/periph_defs.h" // for interrupt source | ||||
|  | ||||
| #include "device/dcd.h" | ||||
|  | ||||
| #ifndef USB_OUT_EP_NUM | ||||
| #define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0]))) | ||||
| #endif | ||||
|  | ||||
| #ifndef USB_IN_EP_NUM | ||||
| #define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0]))) | ||||
| #endif | ||||
|  | ||||
| // Max number of bi-directional endpoints including EP0 | ||||
| // Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0 | ||||
| // We should probably prohibit enabling Endpoint IN > 4 (not done yet) | ||||
|   | ||||
| @@ -48,7 +48,7 @@ | ||||
|  *------------------------------------------------------------------*/ | ||||
|  | ||||
| // Init these in dcd_init | ||||
| static uint8_t *next_buffer_ptr; | ||||
| static uint8_t* next_buffer_ptr; | ||||
|  | ||||
| // USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in. | ||||
| static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; | ||||
| @@ -56,79 +56,70 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; | ||||
| // SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd | ||||
| static bool _sof_enable = false; | ||||
|  | ||||
| TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) | ||||
| { | ||||
| TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) { | ||||
|   return &hw_endpoints[num][dir]; | ||||
| } | ||||
|  | ||||
| static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr) | ||||
| { | ||||
| TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) { | ||||
|   uint8_t num = tu_edpt_number(ep_addr); | ||||
|   tusb_dir_t dir = tu_edpt_dir(ep_addr); | ||||
|   return hw_endpoint_get_by_num(num, dir); | ||||
| } | ||||
|  | ||||
| static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type) | ||||
| { | ||||
| static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) { | ||||
|   // size must be multiple of 64 | ||||
|   uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u; | ||||
|  | ||||
|   // double buffered Bulk endpoint | ||||
|   if ( transfer_type == TUSB_XFER_BULK ) | ||||
|   { | ||||
|   if (transfer_type == TUSB_XFER_BULK) { | ||||
|     size *= 2u; | ||||
|   } | ||||
|  | ||||
|   ep->hw_data_buf = next_buffer_ptr; | ||||
|   next_buffer_ptr += size; | ||||
|  | ||||
|   assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0); | ||||
|   assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0); | ||||
|   uint dpram_offset = hw_data_offset(ep->hw_data_buf); | ||||
|   hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); | ||||
|  | ||||
|   pico_info("  Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf); | ||||
|  | ||||
|   // Fill in endpoint control register with buffer offset | ||||
|   uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; | ||||
|   uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; | ||||
|  | ||||
|   *ep->endpoint_control = reg; | ||||
| } | ||||
|  | ||||
| static void _hw_endpoint_close(struct hw_endpoint *ep) | ||||
| { | ||||
|     // Clear hardware registers and then zero the struct | ||||
|     // Clears endpoint enable | ||||
|     *ep->endpoint_control = 0; | ||||
|     // Clears buffer available, etc | ||||
|     *ep->buffer_control = 0; | ||||
|     // Clear any endpoint state | ||||
|     memset(ep, 0, sizeof(struct hw_endpoint)); | ||||
| static void _hw_endpoint_close(struct hw_endpoint* ep) { | ||||
|   // Clear hardware registers and then zero the struct | ||||
|   // Clears endpoint enable | ||||
|   *ep->endpoint_control = 0; | ||||
|   // Clears buffer available, etc | ||||
|   *ep->buffer_control = 0; | ||||
|   // Clear any endpoint state | ||||
|   memset(ep, 0, sizeof(struct hw_endpoint)); | ||||
|  | ||||
|     // Reclaim buffer space if all endpoints are closed | ||||
|     bool reclaim_buffers = true; | ||||
|     for ( uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++ ) | ||||
|     { | ||||
|         if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) | ||||
|         { | ||||
|             reclaim_buffers = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (reclaim_buffers) | ||||
|     { | ||||
|         next_buffer_ptr = &usb_dpram->epx_data[0]; | ||||
|   // Reclaim buffer space if all endpoints are closed | ||||
|   bool reclaim_buffers = true; | ||||
|   for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) { | ||||
|     if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || | ||||
|         hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) { | ||||
|       reclaim_buffers = false; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if (reclaim_buffers) { | ||||
|     next_buffer_ptr = &usb_dpram->epx_data[0]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void hw_endpoint_close(uint8_t ep_addr) | ||||
| { | ||||
|     struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|     _hw_endpoint_close(ep); | ||||
| static void hw_endpoint_close(uint8_t ep_addr) { | ||||
|   struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|   _hw_endpoint_close(ep); | ||||
| } | ||||
|  | ||||
| static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) | ||||
| { | ||||
|   struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); | ||||
| static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { | ||||
|   struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|  | ||||
|   const uint8_t num = tu_edpt_number(ep_addr); | ||||
|   const tusb_dir_t dir = tu_edpt_dir(ep_addr); | ||||
| @@ -143,35 +134,26 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t | ||||
|   ep->transfer_type = transfer_type; | ||||
|  | ||||
|   // Every endpoint has a buffer control register in dpram | ||||
|   if ( dir == TUSB_DIR_IN ) | ||||
|   { | ||||
|   if (dir == TUSB_DIR_IN) { | ||||
|     ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|   } else { | ||||
|     ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out; | ||||
|   } | ||||
|  | ||||
|   // Clear existing buffer control state | ||||
|   *ep->buffer_control = 0; | ||||
|  | ||||
|   if ( num == 0 ) | ||||
|   { | ||||
|   if (num == 0) { | ||||
|     // EP0 has no endpoint control register because the buffer offsets are fixed | ||||
|     ep->endpoint_control = NULL; | ||||
|  | ||||
|     // Buffer offset is fixed (also double buffered) | ||||
|     ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|   } else { | ||||
|     // Set the endpoint control register (starts at EP1, hence num-1) | ||||
|     if ( dir == TUSB_DIR_IN ) | ||||
|     { | ||||
|     if (dir == TUSB_DIR_IN) { | ||||
|       ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     } else { | ||||
|       ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; | ||||
|     } | ||||
|  | ||||
| @@ -180,76 +162,82 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) | ||||
| { | ||||
|     struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|     hw_endpoint_xfer_start(ep, buffer, total_bytes); | ||||
| static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { | ||||
|   struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|   hw_endpoint_xfer_start(ep, buffer, total_bytes); | ||||
| } | ||||
|  | ||||
| static void __tusb_irq_path_func(hw_handle_buff_status)(void) | ||||
| { | ||||
|     uint32_t remaining_buffers = usb_hw->buf_status; | ||||
|     pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); | ||||
|     uint bit = 1u; | ||||
|     for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) | ||||
|     { | ||||
|         if (remaining_buffers & bit) | ||||
|         { | ||||
|             // clear this in advance | ||||
|             usb_hw_clear->buf_status = bit; | ||||
| static void __tusb_irq_path_func(hw_handle_buff_status)(void) { | ||||
|   uint32_t remaining_buffers = usb_hw->buf_status; | ||||
|   pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); | ||||
|   uint bit = 1u; | ||||
|   for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) { | ||||
|     if (remaining_buffers & bit) { | ||||
|       // clear this in advance | ||||
|       usb_hw_clear->buf_status = bit; | ||||
|  | ||||
|             // IN transfer for even i, OUT transfer for odd i | ||||
|             struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); | ||||
|       // IN transfer for even i, OUT transfer for odd i | ||||
|       struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); | ||||
|  | ||||
|             // Continue xfer | ||||
|             bool done = hw_endpoint_xfer_continue(ep); | ||||
|             if (done) | ||||
|             { | ||||
|                 // Notify | ||||
|                 dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true); | ||||
|                 hw_endpoint_reset_transfer(ep); | ||||
|             } | ||||
|             remaining_buffers &= ~bit; | ||||
|         } | ||||
|         bit <<= 1u; | ||||
|       // Continue xfer | ||||
|       bool done = hw_endpoint_xfer_continue(ep); | ||||
|       if (done) { | ||||
|         // Notify | ||||
|         dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true); | ||||
|         hw_endpoint_reset_transfer(ep); | ||||
|       } | ||||
|       remaining_buffers &= ~bit; | ||||
|     } | ||||
|     bit <<= 1u; | ||||
|   } | ||||
| } | ||||
|  | ||||
| TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void) | ||||
| { | ||||
|     // If we have finished this transfer on EP0 set pid back to 1 for next | ||||
|     // setup transfer. Also clear a stall in case | ||||
|     uint8_t addrs[] = {0x0, 0x80}; | ||||
|     for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++) | ||||
|     { | ||||
|         struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]); | ||||
|         ep->next_pid = 1u; | ||||
| TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) { | ||||
|   // If we have finished this transfer on EP0 set pid back to 1 for next | ||||
|   // setup transfer. Also clear a stall in case | ||||
|   for (uint8_t dir = 0; dir < 2; dir++) { | ||||
|     struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir); | ||||
|     if (ep->active) { | ||||
|       // Abort any pending transfer from a prior control transfer per USB specs | ||||
|       // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1). | ||||
|       // Which means we are not guaranteed to safely abort pending transfer on B0 and B1. | ||||
|       uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS); | ||||
|       if (rp2040_chip_version() >= 2) { | ||||
|         usb_hw_set->abort = abort_mask; | ||||
|         while ((usb_hw->abort_done & abort_mask) != abort_mask) {} | ||||
|       } | ||||
|  | ||||
|       _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL); | ||||
|       hw_endpoint_reset_transfer(ep); | ||||
|  | ||||
|       if (rp2040_chip_version() >= 2) { | ||||
|         usb_hw_clear->abort_done = abort_mask; | ||||
|         usb_hw_clear->abort = abort_mask; | ||||
|       } | ||||
|     } | ||||
|     ep->next_pid = 1u; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void __tusb_irq_path_func(reset_non_control_endpoints)(void) | ||||
| { | ||||
| static void __tusb_irq_path_func(reset_non_control_endpoints)(void) { | ||||
|   // Disable all non-control | ||||
|   for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ ) | ||||
|   { | ||||
|   for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) { | ||||
|     usb_dpram->ep_ctrl[i].in = 0; | ||||
|     usb_dpram->ep_ctrl[i].out = 0; | ||||
|   } | ||||
|  | ||||
|   // clear non-control hw endpoints | ||||
|   tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t)); | ||||
|   tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t)); | ||||
|  | ||||
|   // reclaim buffer space | ||||
|   next_buffer_ptr = &usb_dpram->epx_data[0]; | ||||
| } | ||||
|  | ||||
| static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
| { | ||||
| static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { | ||||
|   uint32_t const status = usb_hw->ints; | ||||
|   uint32_t handled = 0; | ||||
|  | ||||
|   if ( status & USB_INTF_DEV_SOF_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTF_DEV_SOF_BITS) { | ||||
|     bool keep_sof_alive = false; | ||||
|  | ||||
|     handled |= USB_INTF_DEV_SOF_BITS; | ||||
| @@ -258,20 +246,17 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
|     // Errata 15 workaround for Device Bulk-In endpoint | ||||
|     e15_last_sof = time_us_32(); | ||||
|  | ||||
|     for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++ ) | ||||
|     { | ||||
|       struct hw_endpoint * ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN); | ||||
|     for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) { | ||||
|       struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN); | ||||
|  | ||||
|       // Active Bulk IN endpoint requires SOF | ||||
|       if ( (ep->transfer_type == TUSB_XFER_BULK) && ep->active ) | ||||
|       { | ||||
|       if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) { | ||||
|         keep_sof_alive = true; | ||||
|  | ||||
|         hw_endpoint_lock_update(ep, 1); | ||||
|  | ||||
|         // Deferred enable? | ||||
|         if ( ep->pending ) | ||||
|         { | ||||
|         if (ep->pending) { | ||||
|           ep->pending = 0; | ||||
|           hw_endpoint_start_next_buffer(ep); | ||||
|         } | ||||
| @@ -282,26 +267,24 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
| #endif | ||||
|  | ||||
|     // disable SOF interrupt if it is used for RESUME in remote wakeup | ||||
|     if ( !keep_sof_alive && !_sof_enable ) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; | ||||
|     if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; | ||||
|  | ||||
|     dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true); | ||||
|   } | ||||
|  | ||||
|   // xfer events are handled before setup req. So if a transfer completes immediately | ||||
|   // before closing the EP, the events will be delivered in same order. | ||||
|   if ( status & USB_INTS_BUFF_STATUS_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTS_BUFF_STATUS_BITS) { | ||||
|     handled |= USB_INTS_BUFF_STATUS_BITS; | ||||
|     hw_handle_buff_status(); | ||||
|   } | ||||
|  | ||||
|   if ( status & USB_INTS_SETUP_REQ_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTS_SETUP_REQ_BITS) { | ||||
|     handled |= USB_INTS_SETUP_REQ_BITS; | ||||
|     uint8_t const * setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet); | ||||
|     uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet); | ||||
|  | ||||
|     // reset pid to both 1 (data and ack) | ||||
|     reset_ep0_pid(); | ||||
|     reset_ep0(); | ||||
|  | ||||
|     // Pass setup packet to tiny usb | ||||
|     dcd_event_setup_received(0, setup, true); | ||||
| @@ -329,8 +312,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
| #endif | ||||
|  | ||||
|   // SE0 for 2.5 us or more (will last at least 10ms) | ||||
|   if ( status & USB_INTS_BUS_RESET_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTS_BUS_RESET_BITS) { | ||||
|     pico_trace("BUS RESET\r\n"); | ||||
|  | ||||
|     handled |= USB_INTS_BUS_RESET_BITS; | ||||
| @@ -342,7 +324,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
|  | ||||
| #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX | ||||
|     // Only run enumeration workaround if pull up is enabled | ||||
|     if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix(); | ||||
|     if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix(); | ||||
| #endif | ||||
|   } | ||||
|  | ||||
| @@ -354,22 +336,19 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
|    * because without VBUS detection, it is impossible to tell the difference between | ||||
|    * being disconnected and suspended. | ||||
|    */ | ||||
|   if ( status & USB_INTS_DEV_SUSPEND_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTS_DEV_SUSPEND_BITS) { | ||||
|     handled |= USB_INTS_DEV_SUSPEND_BITS; | ||||
|     dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); | ||||
|     usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS; | ||||
|   } | ||||
|  | ||||
|   if ( status & USB_INTS_DEV_RESUME_FROM_HOST_BITS ) | ||||
|   { | ||||
|   if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) { | ||||
|     handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS; | ||||
|     dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); | ||||
|     usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS; | ||||
|   } | ||||
|  | ||||
|   if ( status ^ handled ) | ||||
|   { | ||||
|   if (status ^ handled) { | ||||
|     panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); | ||||
|   } | ||||
| } | ||||
| @@ -390,10 +369,11 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) | ||||
| #define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff | ||||
| #endif | ||||
|  | ||||
| void dcd_init (uint8_t rhport) | ||||
| { | ||||
| void dcd_init(uint8_t rhport) { | ||||
|   assert(rhport == 0); | ||||
|  | ||||
|   TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version()); | ||||
|  | ||||
|   // Reset hardware to default state | ||||
|   rp2040_usb_init(); | ||||
|  | ||||
| @@ -405,7 +385,7 @@ void dcd_init (uint8_t rhport) | ||||
|   irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY); | ||||
|  | ||||
|   // Init control endpoints | ||||
|   tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t)); | ||||
|   tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t)); | ||||
|   hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL); | ||||
|   hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL); | ||||
|  | ||||
| @@ -420,27 +400,24 @@ void dcd_init (uint8_t rhport) | ||||
|   // for the global interrupt enable... | ||||
|   // Note: Force VBUS detect cause disconnection not detectable | ||||
|   usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; | ||||
|   usb_hw->inte     = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS | | ||||
|                      USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS | | ||||
|                      (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS); | ||||
|   usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS | | ||||
|                  USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS | | ||||
|                  (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS); | ||||
|  | ||||
|   dcd_connect(rhport); | ||||
| } | ||||
|  | ||||
| void dcd_int_enable(__unused uint8_t rhport) | ||||
| { | ||||
|     assert(rhport == 0); | ||||
|     irq_set_enabled(USBCTRL_IRQ, true); | ||||
| void dcd_int_enable(__unused uint8_t rhport) { | ||||
|   assert(rhport == 0); | ||||
|   irq_set_enabled(USBCTRL_IRQ, true); | ||||
| } | ||||
|  | ||||
| void dcd_int_disable(__unused uint8_t rhport) | ||||
| { | ||||
|     assert(rhport == 0); | ||||
|     irq_set_enabled(USBCTRL_IRQ, false); | ||||
| void dcd_int_disable(__unused uint8_t rhport) { | ||||
|   assert(rhport == 0); | ||||
|   irq_set_enabled(USBCTRL_IRQ, false); | ||||
| } | ||||
|  | ||||
| void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr) | ||||
| { | ||||
| void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) { | ||||
|   assert(rhport == 0); | ||||
|  | ||||
|   // Can't set device address in hardware until status xfer has complete | ||||
| @@ -448,8 +425,7 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr) | ||||
|   hw_endpoint_xfer(0x80, NULL, 0); | ||||
| } | ||||
|  | ||||
| void dcd_remote_wakeup(__unused uint8_t rhport) | ||||
| { | ||||
| void dcd_remote_wakeup(__unused uint8_t rhport) { | ||||
|   pico_info("dcd_remote_wakeup %d\n", rhport); | ||||
|   assert(rhport == 0); | ||||
|  | ||||
| @@ -460,100 +436,88 @@ void dcd_remote_wakeup(__unused uint8_t rhport) | ||||
| } | ||||
|  | ||||
| // disconnect by disabling internal pull-up resistor on D+/D- | ||||
| void dcd_disconnect(__unused uint8_t rhport) | ||||
| { | ||||
| void dcd_disconnect(__unused uint8_t rhport) { | ||||
|   (void) rhport; | ||||
|   usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; | ||||
| } | ||||
|  | ||||
| // connect by enabling internal pull-up resistor on D+/D- | ||||
| void dcd_connect(__unused uint8_t rhport) | ||||
| { | ||||
| void dcd_connect(__unused uint8_t rhport) { | ||||
|   (void) rhport; | ||||
|   usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; | ||||
| } | ||||
|  | ||||
| void dcd_sof_enable(uint8_t rhport, bool en) | ||||
| { | ||||
| void dcd_sof_enable(uint8_t rhport, bool en) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   _sof_enable = en; | ||||
|  | ||||
|   if (en) | ||||
|   { | ||||
|   if (en) { | ||||
|     usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; | ||||
|   }else | ||||
|   { | ||||
|   } | ||||
| #if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX | ||||
|   else { | ||||
|     // Don't clear immediately if the SOF workaround is in use. | ||||
|     // The SOF handler will conditionally disable the interrupt. | ||||
| #if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX | ||||
|     usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; | ||||
| #endif | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /*------------------------------------------------------------------*/ | ||||
| /* DCD Endpoint port | ||||
|  *------------------------------------------------------------------*/ | ||||
|  | ||||
| void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) | ||||
| { | ||||
| void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   if ( request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && | ||||
|        request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && | ||||
|        request->bRequest == TUSB_REQ_SET_ADDRESS ) | ||||
|   { | ||||
|   if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && | ||||
|       request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && | ||||
|       request->bRequest == TUSB_REQ_SET_ADDRESS) { | ||||
|     usb_hw->dev_addr_ctrl = (uint8_t) request->wValue; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_open (__unused uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) | ||||
| { | ||||
|     assert(rhport == 0); | ||||
|     hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); | ||||
|     return true; | ||||
| bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { | ||||
|   assert(rhport == 0); | ||||
|   hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void dcd_edpt_close_all (uint8_t rhport) | ||||
| { | ||||
| void dcd_edpt_close_all(uint8_t rhport) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // may need to use EP Abort | ||||
|   reset_non_control_endpoints(); | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) | ||||
| { | ||||
|     assert(rhport == 0); | ||||
|     hw_endpoint_xfer(ep_addr, buffer, total_bytes); | ||||
|     return true; | ||||
| bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { | ||||
|   assert(rhport == 0); | ||||
|   hw_endpoint_xfer(ep_addr, buffer, total_bytes); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   if ( tu_edpt_number(ep_addr) == 0 ) | ||||
|   { | ||||
|   if (tu_edpt_number(ep_addr) == 0) { | ||||
|     // A stall on EP0 has to be armed so it can be cleared on the next setup packet | ||||
|     usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS; | ||||
|     usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS | ||||
|                                                                      : USB_EP_STALL_ARM_EP0_OUT_BITS; | ||||
|   } | ||||
|  | ||||
|   struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|   struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|  | ||||
|   // stall and clear current pending buffer | ||||
|   // may need to use EP_ABORT | ||||
|   _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL); | ||||
| } | ||||
|  | ||||
| void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
| void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|  | ||||
|   if (tu_edpt_number(ep_addr)) | ||||
|   { | ||||
|     struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|   if (tu_edpt_number(ep_addr)) { | ||||
|     struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); | ||||
|  | ||||
|     // clear stall also reset toggle to DATA0, ready for next transfer | ||||
|     ep->next_pid = 0; | ||||
| @@ -561,16 +525,13 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
|   } | ||||
| } | ||||
|  | ||||
| void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|     (void) rhport; | ||||
|  | ||||
|     pico_trace("dcd_edpt_close %02x\r\n", ep_addr); | ||||
|     hw_endpoint_close(ep_addr); | ||||
| void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { | ||||
|   (void) rhport; | ||||
|   pico_trace("dcd_edpt_close %02x\r\n", ep_addr); | ||||
|   hw_endpoint_close(ep_addr); | ||||
| } | ||||
|  | ||||
| void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) | ||||
| { | ||||
| void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) { | ||||
|   (void) rhport; | ||||
|   dcd_rp2040_irq(); | ||||
| } | ||||
|   | ||||
| @@ -35,26 +35,18 @@ | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF PROTOTYPE | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Direction strings for debug | ||||
| const char *ep_dir_string[] = { | ||||
|         "out", | ||||
|         "in", | ||||
| }; | ||||
|  | ||||
| static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep); | ||||
| static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep); | ||||
|  | ||||
| #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX | ||||
|   static bool e15_is_bulkin_ep(struct hw_endpoint *ep); | ||||
|   static bool e15_is_critical_frame_period(struct hw_endpoint *ep); | ||||
|   static bool e15_is_bulkin_ep(struct hw_endpoint* ep); | ||||
|   static bool e15_is_critical_frame_period(struct hw_endpoint* ep); | ||||
| #else | ||||
|   #define e15_is_bulkin_ep(x)             (false) | ||||
|   #define e15_is_critical_frame_period(x) (false) | ||||
| #endif | ||||
|  | ||||
| // if usb hardware is in host mode | ||||
| TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) | ||||
| { | ||||
| TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) { | ||||
|   return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; | ||||
| } | ||||
|  | ||||
| @@ -62,8 +54,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) | ||||
| // Implementation | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| void rp2040_usb_init(void) | ||||
| { | ||||
| void rp2040_usb_init(void) { | ||||
|   // Reset usb controller | ||||
|   reset_block(RESETS_RESET_USBCTRL_BITS); | ||||
|   unreset_block_wait(RESETS_RESET_USBCTRL_BITS); | ||||
| @@ -88,46 +79,33 @@ void rp2040_usb_init(void) | ||||
|   TU_LOG2_INT(sizeof(hw_endpoint_t)); | ||||
| } | ||||
|  | ||||
| void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep) | ||||
| { | ||||
| void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { | ||||
|   ep->active = false; | ||||
|   ep->remaining_len = 0; | ||||
|   ep->xferred_len = 0; | ||||
|   ep->user_buf = 0; | ||||
| } | ||||
|  | ||||
| void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) | ||||
| { | ||||
| void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask, | ||||
|                                                                uint32_t or_mask) { | ||||
|   uint32_t value = 0; | ||||
|  | ||||
|   if ( and_mask ) | ||||
|   { | ||||
|   if (and_mask) { | ||||
|     value = *ep->buffer_control & and_mask; | ||||
|   } | ||||
|  | ||||
|   if ( or_mask ) | ||||
|   { | ||||
|   if (or_mask) { | ||||
|     value |= or_mask; | ||||
|     if ( or_mask & USB_BUF_CTRL_AVAIL ) | ||||
|     { | ||||
|       if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL ) | ||||
|       { | ||||
|         panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); | ||||
|     if (or_mask & USB_BUF_CTRL_AVAIL) { | ||||
|       if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) { | ||||
|         panic("ep %02X was already available", ep->ep_addr); | ||||
|       } | ||||
|       *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL; | ||||
|       // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz) | ||||
|       // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control | ||||
|       // Don't need delay in host mode as host is in charge | ||||
| #if !CFG_TUH_ENABLED | ||||
|             __asm volatile ( | ||||
|                     "b 1f\n" | ||||
|                     "1: b 1f\n" | ||||
|                     "1: b 1f\n" | ||||
|                     "1: b 1f\n" | ||||
|                     "1: b 1f\n" | ||||
|                     "1: b 1f\n" | ||||
|                     "1:\n" | ||||
|                     : : : "memory"); | ||||
| #endif | ||||
|       if ( !is_host_mode()) { | ||||
|         busy_wait_at_least_cycles(12); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -135,10 +113,9 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi | ||||
| } | ||||
|  | ||||
| // prepare buffer, return buffer control | ||||
| static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id) | ||||
| { | ||||
| static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { | ||||
|   uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); | ||||
|   ep->remaining_len = (uint16_t)(ep->remaining_len - buflen); | ||||
|   ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); | ||||
|  | ||||
|   uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; | ||||
|  | ||||
| @@ -146,10 +123,9 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, | ||||
|   buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; | ||||
|   ep->next_pid ^= 1u; | ||||
|  | ||||
|   if ( !ep->rx ) | ||||
|   { | ||||
|   if (!ep->rx) { | ||||
|     // Copy data from user buffer to hw buffer | ||||
|     memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen); | ||||
|     memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); | ||||
|     ep->user_buf += buflen; | ||||
|  | ||||
|     // Mark as full | ||||
| @@ -159,8 +135,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, | ||||
|   // Is this the last buffer? Only really matters for host mode. Will trigger | ||||
|   // the trans complete irq but also stop it polling. We only really care about | ||||
|   // trans complete for setup packets being sent | ||||
|   if (ep->remaining_len == 0) | ||||
|   { | ||||
|   if (ep->remaining_len == 0) { | ||||
|     buf_ctrl |= USB_BUF_CTRL_LAST; | ||||
|   } | ||||
|  | ||||
| @@ -170,8 +145,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, | ||||
| } | ||||
|  | ||||
| // Prepare buffer control register value | ||||
| void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) | ||||
| { | ||||
| void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { | ||||
|   uint32_t ep_ctrl = *ep->endpoint_control; | ||||
|  | ||||
|   // always compute and start with buffer 0 | ||||
| @@ -186,8 +160,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) | ||||
|   bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) || | ||||
|                             (is_host && tu_edpt_number(ep->ep_addr) != 0); | ||||
|  | ||||
|   if(ep->remaining_len && !force_single) | ||||
|   { | ||||
|   if (ep->remaining_len && !force_single) { | ||||
|     // Use buffer 1 (double buffered) if there is still data | ||||
|     // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) | ||||
|  | ||||
| @@ -196,8 +169,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) | ||||
|     // Set endpoint control double buffered bit if needed | ||||
|     ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; | ||||
|     ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; | ||||
|   }else | ||||
|   { | ||||
|   } else { | ||||
|     // Single buffered since 1 is enough | ||||
|     ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); | ||||
|     ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; | ||||
| @@ -212,35 +184,28 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) | ||||
|   _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl); | ||||
| } | ||||
|  | ||||
| void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len) | ||||
| { | ||||
| void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { | ||||
|   hw_endpoint_lock_update(ep, 1); | ||||
|  | ||||
|   if ( ep->active ) | ||||
|   { | ||||
|   if (ep->active) { | ||||
|     // TODO: Is this acceptable for interrupt packets? | ||||
|     TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\r\n", tu_edpt_number(ep->ep_addr), | ||||
|               ep_dir_string[tu_edpt_dir(ep->ep_addr)]); | ||||
|  | ||||
|     TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr); | ||||
|     hw_endpoint_reset_transfer(ep); | ||||
|   } | ||||
|  | ||||
|   // Fill in info now that we're kicking off the hw | ||||
|   ep->remaining_len = total_len; | ||||
|   ep->xferred_len   = 0; | ||||
|   ep->active        = true; | ||||
|   ep->user_buf      = buffer; | ||||
|   ep->xferred_len = 0; | ||||
|   ep->active = true; | ||||
|   ep->user_buf = buffer; | ||||
|  | ||||
|   if ( e15_is_bulkin_ep(ep) ) | ||||
|   { | ||||
|   if (e15_is_bulkin_ep(ep)) { | ||||
|     usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; | ||||
|   } | ||||
|  | ||||
|   if ( e15_is_critical_frame_period(ep) ) | ||||
|   { | ||||
|   if (e15_is_critical_frame_period(ep)) { | ||||
|     ep->pending = 1; | ||||
|   } else | ||||
|   { | ||||
|   } else { | ||||
|     hw_endpoint_start_next_buffer(ep); | ||||
|   } | ||||
|  | ||||
| @@ -248,34 +213,30 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to | ||||
| } | ||||
|  | ||||
| // sync endpoint buffer and return transferred bytes | ||||
| static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id) | ||||
| { | ||||
| static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { | ||||
|   uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); | ||||
|   if (buf_id)  buf_ctrl = buf_ctrl >> 16; | ||||
|   if (buf_id) buf_ctrl = buf_ctrl >> 16; | ||||
|  | ||||
|   uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; | ||||
|  | ||||
|   if ( !ep->rx ) | ||||
|   { | ||||
|   if (!ep->rx) { | ||||
|     // We are continuing a transfer here. If we are TX, we have successfully | ||||
|     // sent some data can increase the length we have sent | ||||
|     assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); | ||||
|  | ||||
|     ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes); | ||||
|   }else | ||||
|   { | ||||
|     ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); | ||||
|   } else { | ||||
|     // If we have received some data, so can increase the length | ||||
|     // we have received AFTER we have copied it to the user buffer at the appropriate offset | ||||
|     assert(buf_ctrl & USB_BUF_CTRL_FULL); | ||||
|  | ||||
|     memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes); | ||||
|     ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes); | ||||
|     memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes); | ||||
|     ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); | ||||
|     ep->user_buf += xferred_bytes; | ||||
|   } | ||||
|  | ||||
|   // Short packet | ||||
|   if (xferred_bytes < ep->wMaxPacketSize) | ||||
|   { | ||||
|   if (xferred_bytes < ep->wMaxPacketSize) { | ||||
|     pico_trace("  Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes); | ||||
|     // Reduce total length as this is last packet | ||||
|     ep->remaining_len = 0; | ||||
| @@ -284,8 +245,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uin | ||||
|   return xferred_bytes; | ||||
| } | ||||
|  | ||||
| static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep) | ||||
| { | ||||
| static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) { | ||||
|   // Update hw endpoint struct with info from hardware | ||||
|   // after a buff status interrupt | ||||
|  | ||||
| @@ -296,14 +256,11 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep | ||||
|   uint16_t buf0_bytes = sync_ep_buffer(ep, 0); | ||||
|  | ||||
|   // sync buffer 1 if double buffered | ||||
|   if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS ) | ||||
|   { | ||||
|     if (buf0_bytes == ep->wMaxPacketSize) | ||||
|     { | ||||
|   if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) { | ||||
|     if (buf0_bytes == ep->wMaxPacketSize) { | ||||
|       // sync buffer 1 if not short packet | ||||
|       sync_ep_buffer(ep, 1); | ||||
|     }else | ||||
|     { | ||||
|     } else { | ||||
|       // short packet on buffer 0 | ||||
|       // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example | ||||
|       // At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from | ||||
| @@ -335,14 +292,12 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep | ||||
| } | ||||
|  | ||||
| // Returns true if transfer is complete | ||||
| bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) | ||||
| { | ||||
| bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { | ||||
|   hw_endpoint_lock_update(ep, 1); | ||||
|  | ||||
|   // Part way through a transfer | ||||
|   if (!ep->active) | ||||
|   { | ||||
|     panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); | ||||
|   if (!ep->active) { | ||||
|     panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); | ||||
|   } | ||||
|  | ||||
|   // Update EP struct from hardware state | ||||
| @@ -350,21 +305,15 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) | ||||
|  | ||||
|   // Now we have synced our state with the hardware. Is there more data to transfer? | ||||
|   // If we are done then notify tinyusb | ||||
|   if (ep->remaining_len == 0) | ||||
|   { | ||||
|     pico_trace("Completed transfer of %d bytes on ep %d %s\r\n", | ||||
|                ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); | ||||
|   if (ep->remaining_len == 0) { | ||||
|     pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr); | ||||
|     // Notify caller we are done so it can notify the tinyusb stack | ||||
|     hw_endpoint_lock_update(ep, -1); | ||||
|     return true; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     if ( e15_is_critical_frame_period(ep) ) | ||||
|     { | ||||
|   } else { | ||||
|     if (e15_is_critical_frame_period(ep)) { | ||||
|       ep->pending = 1; | ||||
|     } else | ||||
|     { | ||||
|     } else { | ||||
|       hw_endpoint_start_next_buffer(ep); | ||||
|     } | ||||
|   } | ||||
| @@ -399,16 +348,14 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) | ||||
| volatile uint32_t e15_last_sof = 0; | ||||
|  | ||||
| // check if Errata 15 is needed for this endpoint i.e device bulk-in | ||||
| static bool __tusb_irq_path_func(e15_is_bulkin_ep) (struct hw_endpoint *ep) | ||||
| { | ||||
| static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) { | ||||
|   return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN && | ||||
|           ep->transfer_type == TUSB_XFER_BULK); | ||||
| } | ||||
|  | ||||
| // check if we need to apply Errata 15 workaround : i.e | ||||
| // Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame | ||||
| static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoint *ep) | ||||
| { | ||||
| static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) { | ||||
|   TU_VERIFY(e15_is_bulkin_ep(ep)); | ||||
|  | ||||
|   /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point. | ||||
| @@ -419,11 +366,10 @@ static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoi | ||||
|   if (delta < 800 || delta > 998) { | ||||
|     return false; | ||||
|   } | ||||
|   TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), e15_last_sof); | ||||
|   TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), | ||||
|          e15_last_sof); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										12
									
								
								src/tusb.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/tusb.c
									
									
									
									
									
								
							| @@ -112,8 +112,7 @@ uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t b | ||||
| // Endpoint Helper for both Host and Device stack | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) | ||||
| { | ||||
| bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { | ||||
|   (void) mutex; | ||||
|  | ||||
|   // pre-check to help reducing mutex lock | ||||
| @@ -122,8 +121,7 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) | ||||
|  | ||||
|   // can only claim the endpoint if it is not busy and not claimed yet. | ||||
|   bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); | ||||
|   if (available) | ||||
|   { | ||||
|   if (available) { | ||||
|     ep_state->claimed = 1; | ||||
|   } | ||||
|  | ||||
| @@ -132,16 +130,14 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) | ||||
|   return available; | ||||
| } | ||||
|  | ||||
| bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) | ||||
| { | ||||
| bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { | ||||
|   (void) mutex; | ||||
|  | ||||
|   (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); | ||||
|  | ||||
|   // can only release the endpoint if it is claimed and not busy | ||||
|   bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); | ||||
|   if (ret) | ||||
|   { | ||||
|   if (ret) { | ||||
|     ep_state->claimed = 0; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -44,12 +44,13 @@ | ||||
|   # in order to add common defines: | ||||
|   #  1) remove the trailing [] from the :common: section | ||||
|   #  2) add entries to the :common: section (e.g. :test: has TEST defined) | ||||
|   :common: &common_defines | ||||
|     - _UNITY_TEST_ | ||||
|   :common: &common_defines [] | ||||
|   :test: | ||||
|     - *common_defines | ||||
|     - _UNITY_TEST_ | ||||
|     #- *common_defines | ||||
|   :test_preprocess: | ||||
|     - *common_defines | ||||
|     - _UNITY_TEST_ | ||||
|     #- *common_defines | ||||
|  | ||||
| :cmock: | ||||
|   :mock_prefix: mock_ | ||||
| @@ -106,9 +107,9 @@ | ||||
|   :flag: "${1}"  # or "-L ${1}" for example | ||||
|   :common: &common_libraries [] | ||||
|   :test: | ||||
|     - *common_libraries | ||||
|     #- *common_libraries | ||||
|   :release: | ||||
|     - *common_libraries | ||||
|     #- *common_libraries | ||||
|  | ||||
| :plugins: | ||||
|   :load_paths: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 IngHK
					IngHK