From 206d63e038744b228cfa6051d701cab50b520fe6 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 11 May 2023 14:26:12 +0700 Subject: [PATCH 01/16] correct EHCI reporting failed xfer (instead of stalled) when device is unplugged --- src/class/cdc/cdc_host.c | 21 +++++++++++---------- src/class/hid/hid_host.c | 27 ++++++++++++++++----------- src/class/msc/msc_host.c | 10 +++++++--- src/common/tusb_debug.h | 1 + src/device/usbd_control.c | 9 ++++++--- src/host/hub.c | 9 ++++++++- src/host/usbh.c | 20 +++++++++++++------- src/portable/ehci/ehci.c | 11 ++++++++--- src/tusb.c | 4 ++++ 9 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index fe3691bf4c..9ff666ed49 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -35,8 +35,7 @@ // Debug level, TUSB_CFG_DEBUG must be at least this level for debug message #define CDCH_DEBUG 2 - -#define TU_LOG_CDCH(...) TU_LOG(CDCH_DEBUG, __VA_ARGS__) +#define TU_LOG_DRV(...) TU_LOG(CDCH_DEBUG, __VA_ARGS__) //--------------------------------------------------------------------+ // Host CDC Interface @@ -537,6 +536,8 @@ void cdch_close(uint8_t daddr) cdch_interface_t* p_cdc = &cdch_data[idx]; if (p_cdc->daddr == daddr) { + TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx); + // Invoke application callback if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx); @@ -804,7 +805,7 @@ static void acm_process_config(tuh_xfer_t* xfer) static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); - TU_LOG_CDCH("CDC ACM Set Control Line State\r\n"); + TU_LOG_DRV("CDC ACM Set Control Line State\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = { @@ -834,7 +835,7 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st } static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_CDCH("CDC ACM Set Line Conding\r\n"); + TU_LOG_DRV("CDC ACM Set Line Conding\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = { @@ -894,7 +895,7 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); - TU_LOG_CDCH("FTDI opened\r\n"); + TU_LOG_DRV("FTDI opened\r\n"); p_cdc->serial_drid = SERIAL_DRIVER_FTDI; @@ -938,7 +939,7 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_CDCH("CDC FTDI Set Control Line State\r\n"); + TU_LOG_DRV("CDC FTDI Set Control Line State\r\n"); p_cdc->user_control_cb = complete_cb; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, complete_cb ? cdch_internal_control_complete : NULL, user_data)); @@ -974,7 +975,7 @@ static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); - TU_LOG_CDCH("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\n", baudrate, divisor); + TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\n", baudrate, divisor); p_cdc->user_control_cb = complete_cb; _ftdi_requested_baud = baudrate; @@ -1061,7 +1062,7 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); - TU_LOG_CDCH("CP210x opened\r\n"); + TU_LOG_DRV("CP210x opened\r\n"); p_cdc->serial_drid = SERIAL_DRIVER_CP210X; // endpoint pair @@ -1109,7 +1110,7 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe } static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_CDCH("CDC CP210x Set BaudRate = %lu\n", baudrate); + TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\n", baudrate); uint32_t baud_le = tu_htole32(baudrate); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, @@ -1118,7 +1119,7 @@ static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_ static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_CDCH("CDC CP210x Set Control Line State\r\n"); + TU_LOG_DRV("CDC CP210x Set Control Line State\r\n"); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, complete_cb ? cdch_internal_control_complete : NULL, user_data); diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index d95d3ef354..3a491937a3 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -33,6 +33,10 @@ #include "hid_host.h" +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define HIDH_DEBUG 2 +#define TU_LOG_DRV(...) TU_LOG(HIDH_DEBUG, __VA_ARGS__) + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ @@ -207,7 +211,7 @@ static void set_protocol_complete(tuh_xfer_t* xfer) 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_LOG2("HID Set Protocol = %d\r\n", protocol); + TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol); tusb_control_request_t const request = { @@ -246,7 +250,7 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) static void set_report_complete(tuh_xfer_t* xfer) { - TU_LOG2("HID Set Report complete\r\n"); + TU_LOG_DRV("HID Set Report complete\r\n"); if (tuh_hid_set_report_complete_cb) { @@ -266,7 +270,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r hidh_interface_t* p_hid = get_hid_itf(daddr, idx); TU_VERIFY(p_hid); - TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len); + 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 = { @@ -298,7 +302,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r 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_LOG2("HID Set Idle \r\n"); + TU_LOG_DRV("HID Set Idle \r\n"); tusb_control_request_t const request = { @@ -367,7 +371,7 @@ bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) { - TU_LOG2("HID Send Report %d\r\n", report_id); + 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); @@ -430,7 +434,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t if ( dir == TUSB_DIR_IN ) { - TU_LOG2(" Get Report callback (%u, %u)\r\n", daddr, idx); + 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 @@ -448,8 +452,9 @@ void hidh_close(uint8_t daddr) hidh_interface_t* p_hid = &_hidh_itf[i]; if (p_hid->daddr == daddr) { - if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i); - p_hid->daddr = 0; + TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i); + if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i); + p_hid->daddr = 0; } } } @@ -465,7 +470,7 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_ TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); - TU_LOG2("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber); + 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) + @@ -592,7 +597,7 @@ static void process_set_config(tuh_xfer_t* xfer) // using usbh enumeration buffer since report descriptor can be very long if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) { - TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len); + 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); @@ -763,7 +768,7 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, for ( uint8_t i = 0; i < report_num; i++ ) { info = report_info_arr+i; - TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage); + TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage); } return report_num; diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 1b48813ec9..138443de4d 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -35,7 +35,6 @@ // Debug level, TUSB_CFG_DEBUG must be at least this level for debug message #define MSCH_DEBUG 2 - #define TU_LOG_MSCH(...) TU_LOG(MSCH_DEBUG, __VA_ARGS__) //--------------------------------------------------------------------+ @@ -82,6 +81,7 @@ CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX]; CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)]; +// FIXME potential nul reference TU_ATTR_ALWAYS_INLINE static inline msch_interface_t* get_itf(uint8_t dev_addr) { @@ -305,11 +305,15 @@ void msch_init(void) void msch_close(uint8_t dev_addr) { TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); - msch_interface_t* p_msc = get_itf(dev_addr); + TU_VERIFY(p_msc->configured, ); + + TU_LOG_MSCH(" MSCh close addr = %d\r\n", dev_addr); // invoke Application Callback - if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr); + if (p_msc->mounted) { + if(tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr); + } tu_memclr(p_msc, sizeof(msch_interface_t)); } diff --git a/src/common/tusb_debug.h b/src/common/tusb_debug.h index 82f6820438..36507041fb 100644 --- a/src/common/tusb_debug.h +++ b/src/common/tusb_debug.h @@ -46,6 +46,7 @@ #if CFG_TUSB_DEBUG >= 2 extern char const* const tu_str_speed[]; extern char const* const tu_str_std_request[]; +extern char const* const tu_str_xfer_result[]; #endif void tu_print_mem(void const *buf, uint32_t count, uint8_t indent); diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index ea8eef2859..2afe967b57 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -32,7 +32,10 @@ #include "tusb.h" #include "device/usbd_pvt.h" -#if CFG_TUSB_DEBUG >= 2 +// Debug level of USBD Control +#define USBD_CONTROL_DEBUG 2 + +#if CFG_TUSB_DEBUG >= USBD_CONTROL_DEBUG extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback); #endif @@ -188,7 +191,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result { TU_VERIFY(_ctrl_xfer.buffer); memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes); - TU_LOG_MEM(2, _usbd_ctrl_buf, xferred_bytes, 2); + TU_LOG_MEM(USBD_CONTROL_DEBUG, _usbd_ctrl_buf, xferred_bytes, 2); } _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; @@ -205,7 +208,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result // callback can still stall control in status phase e.g out data does not make sense if ( _ctrl_xfer.complete_cb ) { - #if CFG_TUSB_DEBUG >= 2 + #if CFG_TUSB_DEBUG >= USBD_CONTROL_DEBUG usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); #endif diff --git a/src/host/hub.c b/src/host/hub.c index 386ad6aae7..85bf22b3ee 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -33,6 +33,10 @@ #include "usbh_classdriver.h" #include "hub.h" +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define HUB_DEBUG 2 +#define TU_LOG_DRV(...) TU_LOG(HUB_DEBUG, __VA_ARGS__) + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ @@ -218,7 +222,10 @@ void hub_close(uint8_t dev_addr) TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, ); hub_interface_t* p_hub = get_itf(dev_addr); - if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t)); + if (p_hub->ep_in) { + TU_LOG_DRV(" HUB close addr = %d\r\n", dev_addr); + tu_memclr(p_hub, sizeof( hub_interface_t)); + } } bool hub_edpt_status_xfer(uint8_t dev_addr) diff --git a/src/host/usbh.c b/src/host/usbh.c index 24ce47a7f2..4cfc7c5c2f 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -61,6 +61,8 @@ typedef struct uint8_t hub_addr; uint8_t hub_port; uint8_t speed; + + // enumeration is in progress, done when all interfaces are configured volatile uint8_t enumerating; // struct TU_ATTR_PACKED { @@ -436,7 +438,8 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const ep_dir = tu_edpt_dir(ep_addr); - TU_LOG_USBH("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); + TU_LOG_USBH("on EP %02X with %u bytes %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, + tu_str_xfer_result[event.xfer_complete.result]); if (event.dev_addr == 0) { @@ -866,6 +869,10 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) { switch (event->event_id) { +// case HCD_EVENT_DEVICE_REMOVE: +// +// break; + default: osal_queue_send(_usbh_q, event, in_isr); break; @@ -1128,30 +1135,28 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h usbh_device_t* dev = &_usbh_devices[dev_id]; uint8_t const dev_addr = dev_id+1; - // TODO Hub multiple level if (dev->rhport == rhport && (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub dev->connected) { - TU_LOG_USBH(" Address = %u\r\n", dev_addr); + TU_LOG_USBH("Device unplugged address = %u\r\n", dev_addr); if (is_hub_addr(dev_addr)) { - TU_LOG(USBH_DEBUG, "HUB address = %u is unmounted\r\n", dev_addr); + TU_LOG(USBH_DEBUG, " is a HUB device\r\n", dev_addr); // If the device itself is a usb hub, unplug downstream devices. // FIXME un-roll recursive calls to prevent potential stack overflow process_device_unplugged(rhport, dev_addr, 0); }else { - // Invoke callback before closing driver + // Invoke callback before closing driver (maybe call it later ?) if (tuh_umount_cb) tuh_umount_cb(dev_addr); } // Close class driver for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { - TU_LOG_USBH("%s close\r\n", usbh_class_drivers[drv_id].name); usbh_class_drivers[drv_id].close(dev_addr); } @@ -1449,6 +1454,7 @@ static uint8_t get_new_address(bool is_hub) { uint8_t start; uint8_t end; + if ( is_hub ) { start = CFG_TUH_DEVICE_MAX; @@ -1459,7 +1465,7 @@ static uint8_t get_new_address(bool is_hub) end = start + CFG_TUH_DEVICE_MAX; } - for ( uint8_t idx = start; idx < end; idx++) + for (uint8_t idx = start; idx < end; idx++) { if (!_usbh_devices[idx].connected) return (idx+1); } diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 494e2e50f1..69e59ce658 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -142,8 +142,11 @@ static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); // determine if a queue head has bus-related error static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd) { - return (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err); - //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error + volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay; + + // Error count = 0 often occurs when device disconnected + return (qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err || qtd_overlay->xact_err); + //qtd_overlay->non_hs_period_missed_uframe || qtd_overlay->pingstate_err TODO split transaction error } static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); @@ -630,7 +633,9 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; -// if ( XFER_RESULT_FAILED == error_event ) TU_BREAKPOINT(); // TODO skip unplugged device +// if ( XFER_RESULT_FAILED == error_event ) { +// TU_BREAKPOINT(); // TODO skip unplugged device +// } p_qhd->p_qtd_list_head->used = 0; // free QTD qtd_remove_1st_from_qhd(p_qhd); diff --git a/src/tusb.c b/src/tusb.c index 85fe5a3ccf..7327db6853 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -439,6 +439,10 @@ char const* const tu_str_std_request[] = "Synch Frame" }; +char const* const tu_str_xfer_result[] = { + "OK", "Failed", "Stalled", "Timeout" +}; + #endif static void dump_str_line(uint8_t const* buf, uint16_t count) From 1c4f22a54cf60e6221995f9b64e2e765fd0a5bfe Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 11 May 2023 22:18:40 +0700 Subject: [PATCH 02/16] EHCI: fix xfer failed with disconnected device as stalled - change CFG_TUH_ENDPOINT_MAX to 16 (max endpoint pair per device) if not defined - change QHD_MAX for EHCI, should be user configurable and more optimized in the future --- examples/host/cdc_msc_hid/src/hid_app.c | 2 +- hw/bsp/imxrt/family.c | 5 ++- src/class/hid/hid_host.c | 2 +- src/host/hcd.h | 4 ++- src/portable/ehci/ehci.c | 48 ++++++++++--------------- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c index ed53c502dd..87e110ab23 100644 --- a/examples/host/cdc_msc_hid/src/hid_app.c +++ b/examples/host/cdc_msc_hid/src/hid_app.c @@ -263,7 +263,7 @@ static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t c if (!rpt_info) { - printf("Couldn't find the report info for this report !\r\n"); + printf("Couldn't find report info !\r\n"); return; } diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c index 52d3bb91be..46adabf0a9 100644 --- a/hw/bsp/imxrt/family.c +++ b/hw/bsp/imxrt/family.c @@ -131,7 +131,10 @@ void board_init(void) freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U); } - LPUART_Init(UART_PORT, &uart_config, freq); + if ( kStatus_Success != LPUART_Init(UART_PORT, &uart_config, freq) ) { + // failed to init uart, probably baudrate is not supported + // TU_BREAKPOINT(); + } //------------- USB -------------// // Note: RT105x RT106x and later have dual USB controllers. diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 3a491937a3..6abe298e5f 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -72,7 +72,7 @@ tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID]; TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) { - TU_ASSERT(daddr && idx < CFG_TUH_HID, NULL); + 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; } diff --git a/src/host/hcd.h b/src/host/hcd.h index 623c12a12e..f4e76f9ef7 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -39,8 +39,10 @@ // Configuration //--------------------------------------------------------------------+ +// Max number of endpoints per device +// TODO optimize memory usage #ifndef CFG_TUH_ENDPOINT_MAX - #define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3) + #define CFG_TUH_ENDPOINT_MAX 16 // #ifdef TUP_HCD_ENDPOINT_MAX // #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX // #else diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 69e59ce658..080ee9bbc7 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -58,7 +58,8 @@ #define FRAMELIST_SIZE (1024 >> FRAMELIST_SIZE_BIT_VALUE) -#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX) +// Total queue head pool. TODO should be user configurable and more optimize memory usage in the future +#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) #define QTD_MAX QHD_MAX typedef struct @@ -138,17 +139,6 @@ static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd); static inline ehci_qhd_t* qhd_find_free (void); static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); - -// determine if a queue head has bus-related error -static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd) -{ - volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay; - - // Error count = 0 often occurs when device disconnected - return (qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err || qtd_overlay->xact_err); - //qtd_overlay->non_hs_period_missed_uframe || qtd_overlay->pingstate_err TODO split transaction error -} - static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); static inline ehci_qtd_t* qtd_find_free (void); @@ -392,15 +382,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); //------------- Prepare Queue Head -------------// - ehci_qhd_t * p_qhd; - - if ( ep_desc->bEndpointAddress == 0 ) - { - p_qhd = qhd_control(dev_addr); - }else - { - p_qhd = qhd_find_free(); - } + ehci_qhd_t *p_qhd = (ep_desc->bEndpointAddress == 0) ? qhd_control(dev_addr) : qhd_find_free(); TU_ASSERT(p_qhd); qhd_init(p_qhd, dev_addr, ep_desc); @@ -622,18 +604,23 @@ static void period_list_xfer_complete_isr(uint8_t hostid, uint32_t interval_ms) static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) { - if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL - qhd_has_xact_error(p_qhd) ) - { - // current qhd has error in transaction - xfer_result_t error_event; + volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay; + + // TD has error + if (qtd_overlay->halted) { + xfer_result_t xfer_result; - // no error bits are set, endpoint is halted due to STALL - error_event = qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED; + if (qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err || qtd_overlay->xact_err) { + // Error count = 0 often occurs when device disconnected, or other bus-related error + xfer_result = XFER_RESULT_FAILED; + }else { + // no error bits are set, endpoint is halted due to STALL + xfer_result = XFER_RESULT_STALLED; + } p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; -// if ( XFER_RESULT_FAILED == error_event ) { +// if (XFER_RESULT_FAILED == xfer_result ) { // TU_BREAKPOINT(); // TODO skip unplugged device // } @@ -655,7 +642,8 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) } // call USBH callback - hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), p_qhd->total_xferred_bytes, error_event, true); + uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0); + hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, p_qhd->total_xferred_bytes, xfer_result, true); p_qhd->total_xferred_bytes = 0; } From 2c48050993777c586210af503504e612f31228d8 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 11 May 2023 22:21:18 +0700 Subject: [PATCH 03/16] add various check for disconncted device, also fix #1511 un-roll recursive hub removal with usbh queue --- src/host/usbh.c | 124 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 42 deletions(-) diff --git a/src/host/usbh.c b/src/host/usbh.c index 4cfc7c5c2f..c56ff94597 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -81,10 +81,12 @@ typedef struct { // Device State struct TU_ATTR_PACKED { - volatile uint8_t connected : 1; - volatile uint8_t addressed : 1; - volatile uint8_t configured : 1; - volatile uint8_t suspended : 1; + volatile uint8_t connected : 1; // After 1st transfer + volatile uint8_t addressed : 1; // After SET_ADDR + volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured + volatile uint8_t suspended : 1; // Bus suspended + + // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh }; // Device Descriptor @@ -248,7 +250,7 @@ static inline usbh_device_t* get_device(uint8_t dev_addr) } static bool enum_new_device(hcd_event_t* event); -static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); +static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); @@ -420,7 +422,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) case HCD_EVENT_DEVICE_REMOVE: TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port); - process_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port); + process_removing_device(event.rhport, event.connection.hub_addr, event.connection.hub_port); #if CFG_TUH_HUB // TODO remove @@ -450,7 +452,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) else { usbh_device_t* dev = get_device(event.dev_addr); - TU_ASSERT(dev, ); + TU_VERIFY(dev && dev->connected, ); dev->ep_status[epnum][ep_dir].busy = 0; dev->ep_status[epnum][ep_dir].claimed = 0; @@ -739,29 +741,33 @@ void usbh_int_set(bool enabled) // TODO has some duplication code with device, refactor later bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) { + // Note: addr0 only use tuh_control_xfer usbh_device_t* dev = get_device(dev_addr); - - // addr0 only use tuh_control_xfer - TU_ASSERT(dev); + TU_ASSERT(dev && dev->connected); uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex); + TU_VERIFY(tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex)); + TU_LOG_USBH("[%u] Claimed EP 0x%02x\r\n", dev_addr, ep_addr); + + return true; } // TODO has some duplication code with device, refactor later bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) { + // Note: addr0 only use tuh_control_xfer usbh_device_t* dev = get_device(dev_addr); - - // addr0 only use tuh_control_xfer - TU_ASSERT(dev); + TU_VERIFY(dev && dev->connected); uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex); + TU_VERIFY(tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex)); + TU_LOG_USBH("[%u] Released EP 0x%02x\r\n", dev_addr, ep_addr); + + return true; } // TODO has some duplication code with device, refactor later @@ -870,7 +876,7 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) switch (event->event_id) { // case HCD_EVENT_DEVICE_REMOVE: -// +// // mark device as removing to prevent further xfer before the event is processed in usbh task // break; default: @@ -1116,7 +1122,7 @@ uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_i } //--------------------------------------------------------------------+ -// +// Detaching //--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE @@ -1125,45 +1131,79 @@ static inline bool is_hub_addr(uint8_t daddr) return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX); } +//static void mark_removing_device_isr(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) { +// for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) { +// usbh_device_t *dev = &_usbh_devices[dev_id]; +// uint8_t const daddr = dev_id + 1; +// +// // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub +// if (dev->rhport == rhport && dev->connected && +// (hub_addr == 0 || dev->hub_addr == hub_addr) && +// (hub_port == 0 || dev->hub_port == hub_port)) { +// if (is_hub_addr(daddr)) { +// // If the device itself is a usb hub, mark all downstream devices. +// // FIXME recursive calls +// mark_removing_device_isr(rhport, daddr, 0); +// } +// +// dev->removing = 1; +// } +// } +//} + // a device unplugged from rhport:hub_addr:hub_port -static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) +static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) { //------------- find the all devices (star-network) under port that is unplugged -------------// // TODO mark as disconnected in ISR, also handle dev0 - for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ ) - { - usbh_device_t* dev = &_usbh_devices[dev_id]; - uint8_t const dev_addr = dev_id+1; - if (dev->rhport == rhport && - (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub - (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub - dev->connected) - { - TU_LOG_USBH("Device unplugged address = %u\r\n", dev_addr); +#if 0 + // index as hub addr, value is hub port (0xFF for invalid) + uint8_t removing_hubs[CFG_TUH_HUB]; + memset(removing_hubs, TUSB_INDEX_INVALID_8, sizeof(removing_hubs)); - if (is_hub_addr(dev_addr)) - { - TU_LOG(USBH_DEBUG, " is a HUB device\r\n", dev_addr); - // If the device itself is a usb hub, unplug downstream devices. - // FIXME un-roll recursive calls to prevent potential stack overflow - process_device_unplugged(rhport, dev_addr, 0); - }else - { + removing_hubs[hub_addr-CFG_TUH_DEVICE_MAX] = hub_port; + + // consecutive non-removing hub + uint8_t nop_count = 0; +#endif + + for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) { + usbh_device_t *dev = &_usbh_devices[dev_id]; + uint8_t const daddr = dev_id + 1; + + // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub + if (dev->rhport == rhport && dev->connected && + (hub_addr == 0 || dev->hub_addr == hub_addr) && + (hub_port == 0 || dev->hub_port == hub_port)) { + TU_LOG_USBH("Device unplugged address = %u\r\n", daddr); + + if (is_hub_addr(daddr)) { + TU_LOG(USBH_DEBUG, " is a HUB device\r\n", daddr); + + // Submit removed event If the device itself is a hub (un-rolled recursive) + // TODO a better to unroll recursrive is using array of removing_hubs and mark it here + hcd_event_t event; + event.rhport = rhport; + event.event_id = HCD_EVENT_DEVICE_REMOVE; + event.connection.hub_addr = daddr; + event.connection.hub_port = 0; + + hcd_event_handler(&event, false); + } else { // Invoke callback before closing driver (maybe call it later ?) - if (tuh_umount_cb) tuh_umount_cb(dev_addr); + if (tuh_umount_cb) tuh_umount_cb(daddr); } // Close class driver - for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) - { - usbh_class_drivers[drv_id].close(dev_addr); + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { + usbh_class_drivers[drv_id].close(daddr); } - hcd_device_close(rhport, dev_addr); + hcd_device_close(rhport, daddr); clear_device(dev); // abort on-going control xfer if any - if (_ctrl_xfer.daddr == dev_addr) _set_control_xfer_stage(CONTROL_STAGE_IDLE); + if (_ctrl_xfer.daddr == daddr) _set_control_xfer_stage(CONTROL_STAGE_IDLE); } } } From 1e998ce3bd1ecdf535a8a9feb6823627f3cf7c14 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 12 May 2023 10:58:42 +0700 Subject: [PATCH 04/16] usbd: fix control transfer issue for chipidea hs when previous status and new setup complete in the same isr frame change usbd edpt busy/stalled/claimed value to 0/1 instead of (true/false) since they are 1-bit field. --- .idea/runConfigurations/rt1010.xml | 10 ------ .idea/runConfigurations/rt1010_nxplink.xml | 10 ++++++ src/device/usbd.c | 39 +++++++++++----------- src/portable/chipidea/ci_hs/dcd_ci_hs.c | 18 +++++----- 4 files changed, 39 insertions(+), 38 deletions(-) delete mode 100644 .idea/runConfigurations/rt1010.xml create mode 100644 .idea/runConfigurations/rt1010_nxplink.xml diff --git a/.idea/runConfigurations/rt1010.xml b/.idea/runConfigurations/rt1010.xml deleted file mode 100644 index 700cb57322..0000000000 --- a/.idea/runConfigurations/rt1010.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/rt1010_nxplink.xml b/.idea/runConfigurations/rt1010_nxplink.xml new file mode 100644 index 0000000000..cf3bf842fc --- /dev/null +++ b/.idea/runConfigurations/rt1010_nxplink.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/device/usbd.c b/src/device/usbd.c index cee56af60e..409a5ec100 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -516,9 +516,9 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) _usbd_dev.connected = 1; // mark both in & out control as free - _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = false; + _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 = false; + _usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = 0; _usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0; // Process control request @@ -540,12 +540,13 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) TU_LOG(USBD_DBG, "on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); - _usbd_dev.ep_status[epnum][ep_dir].busy = false; + _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); + usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete + .len); } else { @@ -553,7 +554,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) TU_ASSERT(driver, ); TU_LOG(USBD_DBG, " %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); + driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); } } break; @@ -1244,7 +1245,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() // could return and USBD task can preempt and clear the busy - _usbd_dev.ep_status[epnum][dir].busy = true; + _usbd_dev.ep_status[epnum][dir].busy = 1; if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ) { @@ -1252,7 +1253,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t }else { // DCD error, mark endpoint as ready to allow next transfer - _usbd_dev.ep_status[epnum][dir].busy = false; + _usbd_dev.ep_status[epnum][dir].busy = 0; _usbd_dev.ep_status[epnum][dir].claimed = 0; TU_LOG(USBD_DBG, "FAILED\r\n"); TU_BREAKPOINT(); @@ -1278,7 +1279,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return // and usbd task can preempt and clear the busy - _usbd_dev.ep_status[epnum][dir].busy = true; + _usbd_dev.ep_status[epnum][dir].busy = 1; if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) { @@ -1287,7 +1288,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 }else { // DCD error, mark endpoint as ready to allow next transfer - _usbd_dev.ep_status[epnum][dir].busy = false; + _usbd_dev.ep_status[epnum][dir].busy = 0; _usbd_dev.ep_status[epnum][dir].claimed = 0; TU_LOG(USBD_DBG, "failed\r\n"); TU_BREAKPOINT(); @@ -1317,8 +1318,8 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { TU_LOG(USBD_DBG, " Stall EP %02X\r\n", ep_addr); dcd_edpt_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = true; - _usbd_dev.ep_status[epnum][dir].busy = true; + _usbd_dev.ep_status[epnum][dir].stalled = 1; + _usbd_dev.ep_status[epnum][dir].busy = 1; } } @@ -1334,8 +1335,8 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { TU_LOG(USBD_DBG, " Clear Stall EP %02X\r\n", ep_addr); dcd_edpt_clear_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = false; - _usbd_dev.ep_status[epnum][dir].busy = false; + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; } } @@ -1366,9 +1367,9 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); dcd_edpt_close(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = false; - _usbd_dev.ep_status[epnum][dir].busy = false; - _usbd_dev.ep_status[epnum][dir].claimed = false; + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; return; } @@ -1403,9 +1404,9 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); - _usbd_dev.ep_status[epnum][dir].stalled = false; - _usbd_dev.ep_status[epnum][dir].busy = false; - _usbd_dev.ep_status[epnum][dir].claimed = false; + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; return dcd_edpt_iso_activate(rhport, desc_ep); } diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c index bc6736cf2d..9be79a2f19 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -616,15 +616,6 @@ void dcd_int_handler(uint8_t rhport) uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge - if (dcd_reg->ENDPTSETUPSTAT) - { - //------------- Set up Received -------------// - // 23.10.10.2 Operational model for setup transfers - dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; - - dcd_event_setup_received(rhport, (uint8_t*)(uintptr_t) &_dcd_data.qhd[0][0].setup_request, true); - } - // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set // nothing to do, we will submit xfer as error to usbd // if (int_status & INTR_ERROR) { } @@ -637,6 +628,15 @@ void dcd_int_handler(uint8_t rhport) if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN); } } + + // Set up Received + // 23.10.10.2 Operational model for setup transfers + // Must be after normal transfer complete since it is possible to have both previous control status + new setup + // in the same frame and we should handle previous status first. + if (dcd_reg->ENDPTSETUPSTAT) { + dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; + dcd_event_setup_received(rhport, (uint8_t *) (uintptr_t) &_dcd_data.qhd[0][0].setup_request, true); + } } if (int_status & INTR_SOF) From a9aa0e3a1a9323df16c91d7705aad480033f962a Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 13 May 2023 13:20:09 +0700 Subject: [PATCH 05/16] fix error on EHCI causes xfer error in non-queued qhd which cause memory fault --- .idea/cmake.xml | 2 + .../host_hid_to_device_cdc/src/tusb_config.h | 6 +-- src/portable/ehci/ehci.c | 46 +++++++++++++------ src/portable/ehci/ehci.h | 2 +- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/.idea/cmake.xml b/.idea/cmake.xml index bbef861645..7dacd0003c 100644 --- a/.idea/cmake.xml +++ b/.idea/cmake.xml @@ -2,6 +2,7 @@ + @@ -27,6 +28,7 @@ + \ No newline at end of file diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h index 2185cd1f11..8133ed4187 100644 --- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h +++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h @@ -84,10 +84,6 @@ #define CFG_TUH_RPI_PIO_USB 1 #endif - -// CFG_TUSB_DEBUG is defined by compiler in DEBUG build -// #define CFG_TUSB_DEBUG 0 - /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put * into those specific section. @@ -133,7 +129,7 @@ #endif #ifndef CFG_TUH_MEM_ALIGN -#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) +#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) #endif #define CFG_TUH_HUB 1 diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 080ee9bbc7..8203b0f06f 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -93,16 +93,30 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; // Debug //--------------------------------------------------------------------+ #if CFG_TUSB_DEBUG >= EHCI_DBG -static inline void print_portsc(ehci_registers_t* regs) -{ +static inline void print_portsc(ehci_registers_t* regs) { TU_LOG_HEX(EHCI_DBG, regs->portsc); - TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status); - TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change); - TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled); - TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change); - - TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset); - TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power); + TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status); + TU_LOG(EHCI_DBG, " Connect Change : %u\r\n", regs->portsc_bm.connect_status_change); + TU_LOG(EHCI_DBG, " Enabled : %u\r\n", regs->portsc_bm.port_enabled); + TU_LOG(EHCI_DBG, " Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change); + + TU_LOG(EHCI_DBG, " OverCurr Change: %u\r\n", regs->portsc_bm.over_current_change); + TU_LOG(EHCI_DBG, " Force Resume : %u\r\n", regs->portsc_bm.force_port_resume); + TU_LOG(EHCI_DBG, " Suspend : %u\r\n", regs->portsc_bm.suspend); + TU_LOG(EHCI_DBG, " Reset : %u\r\n", regs->portsc_bm.port_reset); + TU_LOG(EHCI_DBG, " Power : %u\r\n", regs->portsc_bm.port_power); +} + +static inline void print_intr(uint32_t intr) { + TU_LOG_HEX(EHCI_DBG, intr); + TU_LOG(EHCI_DBG, " USB Interrupt : %u\r\n", (intr & EHCI_INT_MASK_USB) ? 1 : 0); + TU_LOG(EHCI_DBG, " USB Error : %u\r\n", (intr & EHCI_INT_MASK_ERROR) ? 1 : 0); + TU_LOG(EHCI_DBG, " Port Change Detect : %u\r\n", (intr & EHCI_INT_MASK_PORT_CHANGE) ? 1 : 0); + TU_LOG(EHCI_DBG, " Frame List Rollover: %u\r\n", (intr & EHCI_INT_MASK_FRAMELIST_ROLLOVER) ? 1 : 0); + TU_LOG(EHCI_DBG, " Host System Error : %u\r\n", (intr & EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR) ? 1 : 0); + TU_LOG(EHCI_DBG, " Async Advance : %u\r\n", (intr & EHCI_INT_MASK_ASYNC_ADVANCE) ? 1 : 0); +// TU_LOG(EHCI_DBG, " Interrupt on Async: %u\r\n", (intr & EHCI_INT_MASK_NXP_ASYNC)); +// TU_LOG(EHCI_DBG, " Periodic Schedule : %u\r\n", (intr & EHCI_INT_MASK_NXP_PERIODIC)); } #else @@ -166,8 +180,8 @@ void hcd_port_reset(uint8_t rhport) ehci_registers_t* regs = ehci_data.regs; - // mask out all change bits since they are Write 1 to clear - uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_CHANGE_ALL; + // mask out Write-1-to-Clear bits + uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C; // EHCI Table 2-16 PortSC // when software writes Port Reset bit to a one, it must also write a zero to the Port Enable bit. @@ -347,7 +361,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) // Power Control (PPC) field in the HCSPARAMS register. if (ehci_data.cap_regs->hcsparams_bm.port_power_control) { // mask out all change bits since they are Write 1 to clear - uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_CHANGE_ALL); + uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_W1C); portsc |= ECHI_PORTSC_MASK_PORT_POWER; regs->portsc = portsc; @@ -621,9 +635,14 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; // if (XFER_RESULT_FAILED == xfer_result ) { +// TU_LOG1(" QHD xfer err count: %d\n", qtd_overlay->err_count); // TU_BREAKPOINT(); // TODO skip unplugged device +// while(1){} // } + // No TD, probably an signal noise ? + TU_VERIFY(p_qhd->p_qtd_list_head, ); + p_qhd->p_qtd_list_head->used = 0; // free QTD qtd_remove_1st_from_qhd(p_qhd); @@ -710,7 +729,8 @@ void hcd_int_handler(uint8_t rhport) if (int_status & EHCI_INT_MASK_PORT_CHANGE) { - uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_CHANGE_ALL; + // Including: Force port resume, over-current change, enable/disable change and connect status change. + uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_W1C; print_portsc(regs); if (regs->portsc_bm.connect_status_change) diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index dd090cb36a..a73e437071 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -306,7 +306,7 @@ enum { EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8), ECHI_PORTSC_MASK_PORT_POWER = TU_BIT(12), - EHCI_PORTSC_MASK_CHANGE_ALL = + EHCI_PORTSC_MASK_W1C = EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE | EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE | EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE From eb89df411540cb8cf5323991516a45590fa0b81d Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 17 May 2023 16:14:35 +0700 Subject: [PATCH 06/16] adding hcd_dcache_clean/hcd_dcache_invalidate --- hw/bsp/imxrt/family.cmake | 15 +++++++++++++++ src/host/hcd.h | 10 ++++++++++ src/portable/chipidea/ci_hs/hcd_ci_hs.c | 9 +++++++++ src/portable/ehci/ehci.h | 7 +++++++ 4 files changed, 41 insertions(+) diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake index 77ac05c872..4628abc341 100644 --- a/hw/bsp/imxrt/family.cmake +++ b/hw/bsp/imxrt/family.cmake @@ -144,6 +144,21 @@ function(family_configure_target TARGET) COMMAND ${LINKSERVER_PATH} flash ${NXPLINK_DEVICE} load $ ) + # Flash using jlink + set(JLINKEXE JLinkExe) + file(GENERATE + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.jlink + CONTENT "halt +loadfile $ +r +go +exit" + ) + add_custom_target(${TARGET}-jlink + DEPENDS ${TARGET} + COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if swd -JTAGConf -1,-1 -speed auto -CommandFile ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.jlink + ) + endfunction() diff --git a/src/host/hcd.h b/src/host/hcd.h index f4e76f9ef7..38c89a1d22 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -104,6 +104,16 @@ typedef struct uint8_t speed; } hcd_devtree_info_t; +//--------------------------------------------------------------------+ +// Memory API +//--------------------------------------------------------------------+ + +// clean/flush data cache: write cache -> memory +void hcd_dcache_clean(void* addr, uint32_t data_size) TU_ATTR_WEAK; + +// invalidate data cache: mark cache as invalid, next read will read from memory +void hcd_dcache_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK; + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c index b06633f308..56ca01f85c 100644 --- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c @@ -41,6 +41,15 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT #include "ci_hs_imxrt.h" + + void hcd_dcache_clean(void* addr, uint32_t data_size) { + SCB_CleanDCache_by_Addr((uint32_t*) addr, (int32_t) data_size); + } + + void hcd_dcache_invalidate(void* addr, uint32_t data_size) { + SCB_InvalidateDCache_by_Addr(addr, (int32_t) data_size); + } + #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) #include "ci_hs_lpc18_43.h" #else diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index a73e437071..56befd3063 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -268,6 +268,7 @@ TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" ); // EHCI Operational Register //--------------------------------------------------------------------+ enum { + // Bit 0-5 has maskable in interrupt enabled register EHCI_INT_MASK_USB = TU_BIT(0), EHCI_INT_MASK_ERROR = TU_BIT(1), EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2), @@ -276,6 +277,12 @@ enum { EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5), EHCI_INT_MASK_NXP_SOF = TU_BIT(7), + + EHCI_INT_MASK_HC_HALTED = TU_BIT(12), + EHCI_INT_MASK_RECLAIMATION = TU_BIT(13), + EHCI_INT_MASK_PERIODIC_SCHED_STATUS = TU_BIT(14), + EHCI_INT_MASK_ASYNC_SCHED_STATUS = TU_BIT(15), + EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18), EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19), From a3e017bfd2923dcb1ce86d395606a24e2285e54a Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 10:04:48 +0700 Subject: [PATCH 07/16] EHCI adding dcahe support, passing enumertaion --- src/host/hcd.h | 10 +- src/portable/chipidea/ci_hs/hcd_ci_hs.c | 4 + src/portable/ehci/ehci.c | 167 +++++++++++++++--------- src/portable/ehci/ehci.h | 1 + 4 files changed, 117 insertions(+), 65 deletions(-) diff --git a/src/host/hcd.h b/src/host/hcd.h index 38c89a1d22..c6fde2adc7 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -108,12 +108,18 @@ typedef struct // Memory API //--------------------------------------------------------------------+ -// clean/flush data cache: write cache -> memory +// clean/flush data cache: write cache -> memory. +// Required before an DMA TX transfer to make sure data is in memory void hcd_dcache_clean(void* addr, uint32_t data_size) TU_ATTR_WEAK; // invalidate data cache: mark cache as invalid, next read will read from memory +// Required BOTH before and after an DMA RX transfer void hcd_dcache_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK; +// clean and invalidate data cache +// Required before an DMA transfer where memory is both read/write by DMA +void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK; + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ @@ -194,6 +200,7 @@ void hcd_event_device_attach(uint8_t rhport, bool in_isr) event.event_id = HCD_EVENT_DEVICE_ATTACH; event.connection.hub_addr = 0; event.connection.hub_port = 0; + hcd_event_handler(&event, in_isr); } @@ -224,7 +231,6 @@ void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred event.xfer_complete.result = result; event.xfer_complete.len = xferred_bytes; - hcd_event_handler(&event, in_isr); } diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c index 56ca01f85c..ecb1a621c6 100644 --- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c @@ -50,6 +50,10 @@ SCB_InvalidateDCache_by_Addr(addr, (int32_t) data_size); } +void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) { + SCB_CleanInvalidateDCache_by_Addr(addr, (int32_t) data_size); + } + #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) #include "ci_hs_lpc18_43.h" #else diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 8203b0f06f..9023e4b359 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -92,7 +92,7 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= EHCI_DBG +#if CFG_TUSB_DEBUG >= (EHCI_DBG + 1) static inline void print_portsc(ehci_registers_t* regs) { TU_LOG_HEX(EHCI_DBG, regs->portsc); TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status); @@ -159,7 +159,7 @@ static inline ehci_qtd_t* qtd_find_free (void); static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd); static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new); static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd); -static void qtd_init (ehci_qtd_t* p_qtd, void const* buffer, uint16_t total_bytes); +static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes); static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer); @@ -325,28 +325,27 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) // 3 --> period_head_arr[3] (8ms) // TODO EHCI_FRAMELIST_SIZE with other size than 8 - for(uint32_t i=0; iterminate = 1; regs->periodic_list_base = (uint32_t) framelist; + if(hcd_dcache_clean) { + hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t)); + } + //------------- TT Control (NXP only) -------------// regs->nxp_tt_control = 0; @@ -430,6 +429,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const // TODO might need to disable async/period list list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD); + if(hcd_dcache_clean) { + hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t)); + hcd_dcache_clean(list_head, sizeof(ehci_link_t)); + } + return true; } @@ -441,9 +445,12 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd; qtd_init(td, setup_packet, 8); - td->pid = EHCI_PID_SETUP; - td->int_on_complete = 1; - td->next.terminate = 1; + td->pid = EHCI_PID_SETUP; + + if (hcd_dcache_clean && hcd_dcache_clean_invalidate) { + hcd_dcache_clean((void *) setup_packet, 8); + hcd_dcache_clean_invalidate(td, sizeof(ehci_qtd_t)); + } // sw region qhd->p_qtd_list_head = td; @@ -452,6 +459,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet // attach TD qhd->qtd_overlay.next.address = (uint32_t) td; + if (hcd_dcache_clean_invalidate) { + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); + } + return true; } @@ -462,41 +473,48 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); + ehci_qhd_t* qhd; + ehci_qtd_t* qtd; + if ( epnum == 0 ) { - ehci_qhd_t* qhd = qhd_control(dev_addr); - ehci_qtd_t* qtd = qtd_control(dev_addr); + qhd = qhd_control(dev_addr); + qtd = qtd_control(dev_addr); qtd_init(qtd, buffer, buflen); // first first data toggle is always 1 (data & setup stage) qtd->data_toggle = 1; qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT; - qtd->int_on_complete = 1; - qtd->next.terminate = 1; - - // sw region - qhd->p_qtd_list_head = qtd; - qhd->p_qtd_list_tail = qtd; - - // attach TD - qhd->qtd_overlay.next.address = (uint32_t) qtd; }else { - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - ehci_qtd_t *p_qtd = qtd_find_free(); - TU_ASSERT(p_qtd); + qhd = qhd_get_from_addr(dev_addr, ep_addr); + qtd = qtd_find_free(); + TU_ASSERT(qtd); + + qtd_init(qtd, buffer, buflen); + qtd->pid = qhd->pid; + } - qtd_init(p_qtd, buffer, buflen); - p_qtd->pid = p_qhd->pid; + if (hcd_dcache_clean && hcd_dcache_clean_invalidate) { + // IN transfer: invalidate buffer, OUT transfer: clean buffer + if (dir) { + hcd_dcache_invalidate(buffer, buflen); + }else { + hcd_dcache_clean(buffer, buflen); + } + hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); + } - // Insert TD to QH - qtd_insert_to_qhd(p_qhd, p_qtd); + // Software: assign TD to QHD + qhd->p_qtd_list_head = qtd; + qhd->p_qtd_list_tail = qtd; - p_qhd->p_qtd_list_tail->int_on_complete = 1; + // attach TD to QHD start transferring + qhd->qtd_overlay.next.address = (uint32_t) qtd; - // attach head QTD to QHD start transferring - p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head; + if (hcd_dcache_clean_invalidate) { + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); } return true; @@ -551,6 +569,11 @@ static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) { ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; + + if (hcd_dcache_invalidate) { + hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); + } + bool const is_ioc = (qtd->int_on_complete != 0); uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0); @@ -573,8 +596,12 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) ehci_qhd_t *p_qhd = async_head; do { - if ( !p_qhd->qtd_overlay.halted ) // halted or error is processed in error isr - { + if (hcd_dcache_invalidate) { + hcd_dcache_invalidate(p_qhd, sizeof(ehci_qhd_t)); + } + + // halted or error is processed in error isr + if ( !p_qhd->qtd_overlay.halted ) { qhd_xfer_complete_isr(p_qhd); } p_qhd = qhd_next(p_qhd); @@ -640,8 +667,8 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) // while(1){} // } - // No TD, probably an signal noise ? - TU_VERIFY(p_qhd->p_qtd_list_head, ); + // No TD yet, it is probably the probably an signal noise ? + TU_ASSERT(p_qhd->p_qtd_list_head, ); p_qhd->p_qtd_list_head->used = 0; // free QTD qtd_remove_1st_from_qhd(p_qhd); @@ -714,17 +741,19 @@ static void xfer_error_isr(uint8_t hostid) void hcd_int_handler(uint8_t rhport) { ehci_registers_t* regs = ehci_data.regs; + uint32_t const int_status = regs->status; - uint32_t int_status = regs->status; - int_status &= regs->inten; - - regs->status = int_status; // Acknowledge handled interrupt - - if (int_status == 0) return; + if (int_status & EHCI_INT_MASK_HC_HALTED) { + // something seriously wrong, maybe forget to flush/invalidate cache + TU_BREAKPOINT(); + TU_LOG1(" HC halted\n"); + return; + } if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) { ehci_data.uframe_number += (FRAMELIST_SIZE << 3); + regs->status = EHCI_INT_MASK_FRAMELIST_ROLLOVER; // Acknowledge } if (int_status & EHCI_INT_MASK_PORT_CHANGE) @@ -739,31 +768,41 @@ void hcd_int_handler(uint8_t rhport) } regs->portsc |= port_status; // Acknowledge change bits in portsc + regs->status = EHCI_INT_MASK_PORT_CHANGE; // Acknowledge } if (int_status & EHCI_INT_MASK_ERROR) { xfer_error_isr(rhport); + regs->status = EHCI_INT_MASK_ERROR; // Acknowledge } //------------- some QTD/SITD/ITD with IOC set is completed -------------// if (int_status & EHCI_INT_MASK_NXP_ASYNC) { - async_list_xfer_complete_isr( qhd_async_head(rhport) ); + async_list_xfer_complete_isr(qhd_async_head(rhport)); + regs->status = EHCI_INT_MASK_NXP_ASYNC; // Acknowledge } if (int_status & EHCI_INT_MASK_NXP_PERIODIC) { for (uint32_t i=1; i <= FRAMELIST_SIZE; i *= 2) { - period_list_xfer_complete_isr( rhport, i ); + period_list_xfer_complete_isr(rhport, i); } + regs->status = EHCI_INT_MASK_NXP_PERIODIC; // Acknowledge + } + + if (int_status & EHCI_INT_MASK_USB) { + // TODO standard EHCI xfer complete + regs->status = EHCI_INT_MASK_USB; // Acknowledge } //------------- There is some removed async previously -------------// - if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC - { + // need to place after EHCI_INT_MASK_NXP_ASYNC + if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) { async_advance_isr(rhport); + regs->status = EHCI_INT_MASK_ASYNC_ADVANCE; // Acknowledge } } @@ -918,28 +957,30 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c } } -static void qtd_init(ehci_qtd_t* p_qtd, void const* buffer, uint16_t total_bytes) +static void qtd_init(ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes) { - tu_memclr(p_qtd, sizeof(ehci_qtd_t)); + tu_memclr(qtd, sizeof(ehci_qtd_t)); + qtd->used = 1; - p_qtd->used = 1; + qtd->next.terminate = 1; // init to null + qtd->alternate.terminate = 1; // not used, always set to terminated + qtd->active = 1; + qtd->err_count = 3; // TODO 3 consecutive errors tolerance + qtd->data_toggle = 0; + qtd->int_on_complete = 1; + qtd->total_bytes = total_bytes; + qtd->expected_bytes = total_bytes; - p_qtd->next.terminate = 1; // init to null - p_qtd->alternate.terminate = 1; // not used, always set to terminated - p_qtd->active = 1; - p_qtd->err_count = 3; // TODO 3 consecutive errors tolerance - p_qtd->data_toggle = 0; - p_qtd->total_bytes = total_bytes; - p_qtd->expected_bytes = total_bytes; - - p_qtd->buffer[0] = (uint32_t) buffer; + qtd->buffer[0] = (uint32_t) buffer; for(uint8_t i=1; i<5; i++) { - p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; + qtd->buffer[i] |= tu_align4k(qtd->buffer[i - 1] ) + 4096; } } //------------- List Managing Helper -------------// + +// insert at head static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) { new->address = current->address; diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index 56befd3063..e76a59401a 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -165,6 +165,7 @@ typedef struct TU_ATTR_ALIGNED(32) uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set uint8_t reserved2[2]; + // TODO USBH will only queue 1 TD per QHD, thus we can remove the list ehci_qtd_t * volatile p_qtd_list_head; // head of the scheduled TD list ehci_qtd_t * volatile p_qtd_list_tail; // tail of the scheduled TD list } ehci_qhd_t; From e4f4ad5bc3a24f619b9cf6c542aaa3ff8cb37ec4 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 10:21:11 +0700 Subject: [PATCH 08/16] use weak local for dcache function to skip if() --- src/portable/ehci/ehci.c | 65 +++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 9023e4b359..ac715aa60c 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -164,6 +164,25 @@ static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes) static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer); +TU_ATTR_WEAK void hcd_dcache_clean(void* addr, uint32_t data_size) { + (void) addr; + (void) data_size; +} + +// invalidate data cache: mark cache as invalid, next read will read from memory +// Required BOTH before and after an DMA RX transfer +TU_ATTR_WEAK void hcd_dcache_invalidate(void* addr, uint32_t data_size) { + (void) addr; + (void) data_size; +} + +// clean and invalidate data cache +// Required before an DMA transfer where memory is both read/write by DMA +TU_ATTR_WEAK void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) { + (void) addr; + (void) data_size; +} + //--------------------------------------------------------------------+ // HCD API //--------------------------------------------------------------------+ @@ -342,9 +361,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) regs->periodic_list_base = (uint32_t) framelist; - if(hcd_dcache_clean) { - hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t)); - } + hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t)); //------------- TT Control (NXP only) -------------// regs->nxp_tt_control = 0; @@ -429,10 +446,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const // TODO might need to disable async/period list list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD); - if(hcd_dcache_clean) { - hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t)); - hcd_dcache_clean(list_head, sizeof(ehci_link_t)); - } + hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t)); + hcd_dcache_clean(list_head, sizeof(ehci_link_t)); return true; } @@ -447,10 +462,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet qtd_init(td, setup_packet, 8); td->pid = EHCI_PID_SETUP; - if (hcd_dcache_clean && hcd_dcache_clean_invalidate) { - hcd_dcache_clean((void *) setup_packet, 8); - hcd_dcache_clean_invalidate(td, sizeof(ehci_qtd_t)); - } + hcd_dcache_clean((void *) setup_packet, 8); + hcd_dcache_clean_invalidate(td, sizeof(ehci_qtd_t)); // sw region qhd->p_qtd_list_head = td; @@ -459,9 +472,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet // attach TD qhd->qtd_overlay.next.address = (uint32_t) td; - if (hcd_dcache_clean_invalidate) { - hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); - } + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); return true; } @@ -496,15 +507,13 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * qtd->pid = qhd->pid; } - if (hcd_dcache_clean && hcd_dcache_clean_invalidate) { - // IN transfer: invalidate buffer, OUT transfer: clean buffer - if (dir) { - hcd_dcache_invalidate(buffer, buflen); - }else { - hcd_dcache_clean(buffer, buflen); - } - hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); + // IN transfer: invalidate buffer, OUT transfer: clean buffer + if (dir) { + hcd_dcache_invalidate(buffer, buflen); + }else { + hcd_dcache_clean(buffer, buflen); } + hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); // Software: assign TD to QHD qhd->p_qtd_list_head = qtd; @@ -513,9 +522,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // attach TD to QHD start transferring qhd->qtd_overlay.next.address = (uint32_t) qtd; - if (hcd_dcache_clean_invalidate) { - hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); - } + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); return true; } @@ -570,9 +577,7 @@ static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) { ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; - if (hcd_dcache_invalidate) { - hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); - } + hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); bool const is_ioc = (qtd->int_on_complete != 0); uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0); @@ -596,9 +601,7 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) ehci_qhd_t *p_qhd = async_head; do { - if (hcd_dcache_invalidate) { - hcd_dcache_invalidate(p_qhd, sizeof(ehci_qhd_t)); - } + hcd_dcache_invalidate(p_qhd, sizeof(ehci_qhd_t)); // halted or error is processed in error isr if ( !p_qhd->qtd_overlay.halted ) { From a0aea52a117246f246529459f9203ce36d2fcd81 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 12:39:53 +0700 Subject: [PATCH 09/16] more cache, fix an similar issue with OHCI when removing an queue head --- src/portable/ehci/ehci.c | 50 ++++++++++++++++++++++------------------ src/portable/ehci/ehci.h | 2 ++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index ac715aa60c..0b4202788a 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -162,7 +162,7 @@ static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd); static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes); static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); -static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer); +static inline ehci_link_t* list_next (ehci_link_t const *p_link); TU_ATTR_WEAK void hcd_dcache_clean(void* addr, uint32_t data_size) { (void) addr; @@ -237,24 +237,22 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed } -static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) -{ - for(ehci_link_t* prev = list_head; - !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head) && prev != NULL; - prev = list_next(prev) ) - { - // TODO check type for ISO iTD and siTD - // TODO Suppress cast-align warning - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wcast-align" - ehci_qhd_t* qhd = (ehci_qhd_t*) list_next(prev); - #pragma GCC diagnostic pop - if ( qhd->dev_addr == dev_addr ) - { +static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) { + ehci_link_t* prev = list_head; + + while (prev && !prev->terminate) { + ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev); + + // done if loop back to head + if ( (uintptr_t) qhd == (uintptr_t) list_head) { + break; + } + + if ( qhd->dev_addr == dev_addr ) { // TODO deactivate all TD, wait for QHD to inactive before removal prev->address = qhd->next.address; - // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller) + // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller) qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1); if ( qhd->int_smask ) @@ -267,6 +265,11 @@ static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) // mark as removing, will completely re-usable when async advance isr occurs qhd->removing = 1; } + + hcd_dcache_clean(qhd, sizeof(ehci_qhd_t)); + hcd_dcache_clean(prev, sizeof(ehci_qhd_t)); + }else { + prev = list_next(prev); } } } @@ -275,15 +278,16 @@ static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // skip dev0 - if (dev_addr == 0) return; + if (dev_addr == 0) { + return; + } // Remove from async list - list_remove_qhd_by_addr( (ehci_link_t*) qhd_async_head(rhport), dev_addr ); + list_remove_qhd_by_daddr((ehci_link_t *) qhd_async_head(rhport), dev_addr); // Remove from all interval period list - for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) - { - list_remove_qhd_by_addr( (ehci_link_t*) &ehci_data.period_head_arr[i], dev_addr); + for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) { + list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], dev_addr); } // Async doorbell (EHCI 4.8.2 for operational details) @@ -990,9 +994,9 @@ static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t n current->address = ((uint32_t) new) | (new_type << 1); } -static inline ehci_link_t* list_next(ehci_link_t *p_link_pointer) +static inline ehci_link_t* list_next(ehci_link_t const *p_link) { - return (ehci_link_t*) tu_align32(p_link_pointer->address); + return (ehci_link_t*) tu_align32(p_link->address); } #endif diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index e76a59401a..d7d37d627b 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -81,6 +81,8 @@ typedef union { }; }ehci_link_t; +TU_VERIFY_STATIC( sizeof(ehci_link_t) == 4, "size is not correct" ); + /// Queue Element Transfer Descriptor /// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32) typedef struct From 49e2aabc819f761284dd490fd4f1c5d4ad5c3695 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 13:45:38 +0700 Subject: [PATCH 10/16] EHCI more improvement - more dcache clean/invalidate - extract init_periodic_list() - improve isr list handling --- src/host/hcd.h | 2 +- src/portable/ehci/ehci.c | 196 +++++++++++++++++++++------------------ 2 files changed, 105 insertions(+), 93 deletions(-) diff --git a/src/host/hcd.h b/src/host/hcd.h index c6fde2adc7..5a3b0a087e 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -175,7 +175,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]); // clear stall, data toggle is also reset to DATA0 -bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr); +bool hcd_edpt_clear_stall(uint8_t daddr, uint8_t ep_addr); //--------------------------------------------------------------------+ // USBH implemented API diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 0b4202788a..0b45d7b939 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -294,6 +294,40 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) ehci_data.regs->command_bm.async_adv_doorbell = 1; } +static void init_periodic_list(uint8_t rhport) { + // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only + for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) { + ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero + ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive + } + + ehci_link_t * const framelist = ehci_data.period_framelist; + ehci_link_t * const period_1ms = get_period_head(rhport, 1u); + + // all links --> period_head_arr[0] (1ms) + // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms) + // 1, 5 --> period_head_arr[2] (4ms) + // 3 --> period_head_arr[3] (8ms) + + // TODO EHCI_FRAMELIST_SIZE with other size than 8 + for (uint32_t i = 0; i < FRAMELIST_SIZE; i++) { + framelist[i].address = (uint32_t) period_1ms; + framelist[i].type = EHCI_QTYPE_QHD; + } + + for (uint32_t i = 0; i < FRAMELIST_SIZE; i += 2) { + list_insert(framelist + i, get_period_head(rhport, 2u), EHCI_QTYPE_QHD); + } + + for (uint32_t i = 1; i < FRAMELIST_SIZE; i += 4) { + list_insert(framelist + i, get_period_head(rhport, 4u), EHCI_QTYPE_QHD); + } + + list_insert(framelist + 3, get_period_head(rhport, 8u), EHCI_QTYPE_QHD); + + period_1ms->terminate = 1; +} + bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) { tu_memclr(&ehci_data, sizeof(ehci_data_t)); @@ -332,38 +366,8 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) regs->async_list_addr = (uint32_t) async_head; //------------- Periodic List -------------// - // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only - for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) - { - ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero - ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive - } - - ehci_link_t * const framelist = ehci_data.period_framelist; - ehci_link_t * const period_1ms = get_period_head(rhport, 1u); - - // all links --> period_head_arr[0] (1ms) - // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms) - // 1, 5 --> period_head_arr[2] (4ms) - // 3 --> period_head_arr[3] (8ms) - - // TODO EHCI_FRAMELIST_SIZE with other size than 8 - for (uint32_t i = 0; i < FRAMELIST_SIZE; i++) { - framelist[i].address = (uint32_t) period_1ms; - framelist[i].type = EHCI_QTYPE_QHD; - } - - for (uint32_t i = 0; i < FRAMELIST_SIZE; i += 2) { - list_insert(framelist + i, get_period_head(rhport, 2u), EHCI_QTYPE_QHD); - } - - for (uint32_t i = 1; i < FRAMELIST_SIZE; i += 4) { - list_insert(framelist + i, get_period_head(rhport, 4u), EHCI_QTYPE_QHD); - } - list_insert(framelist + 3, get_period_head(rhport, 8u), EHCI_QTYPE_QHD); - period_1ms->terminate = 1; - - regs->periodic_list_base = (uint32_t) framelist; + init_periodic_list(rhport); + regs->periodic_list_base = (uint32_t) ehci_data.period_framelist; hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t)); @@ -491,8 +495,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * ehci_qhd_t* qhd; ehci_qtd_t* qtd; - if ( epnum == 0 ) - { + if (epnum == 0) { qhd = qhd_control(dev_addr); qtd = qtd_control(dev_addr); @@ -501,8 +504,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // first first data toggle is always 1 (data & setup stage) qtd->data_toggle = 1; qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT; - }else - { + } else { qhd = qhd_get_from_addr(dev_addr, ep_addr); qtd = qtd_find_free(); TU_ASSERT(qtd); @@ -531,10 +533,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * return true; } -bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) +bool hcd_edpt_clear_stall(uint8_t daddr, uint8_t ep_addr) { - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - p_qhd->qtd_overlay.halted = 0; + ehci_qhd_t *qhd = qhd_get_from_addr(daddr, ep_addr); + qhd->qtd_overlay.halted = 0; + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); // TODO reset data toggle ? return true; } @@ -546,22 +549,22 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) // async_advance is handshake between usb stack & ehci controller. // This isr mean it is safe to modify previously removed queue head from async list. // In tinyusb, queue head is only removed when device is unplugged. -static void async_advance_isr(uint8_t rhport) +TU_ATTR_ALWAYS_INLINE static inline +void async_advance_isr(uint8_t rhport) { (void) rhport; - ehci_qhd_t* qhd_pool = ehci_data.qhd_pool; - for(uint32_t i = 0; i < QHD_MAX; i++) - { - if ( qhd_pool[i].removing ) - { + ehci_qhd_t *qhd_pool = ehci_data.qhd_pool; + for (uint32_t i = 0; i < QHD_MAX; i++) { + if (qhd_pool[i].removing) { qhd_pool[i].removing = 0; - qhd_pool[i].used = 0; + qhd_pool[i].used = 0; } } } -static void port_connect_status_change_isr(uint8_t rhport) +TU_ATTR_ALWAYS_INLINE static inline +void port_connect_status_change_isr(uint8_t rhport) { // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device if (ehci_data.regs->portsc_bm.current_connect_status) @@ -574,13 +577,13 @@ static void port_connect_status_change_isr(uint8_t rhport) } } -static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) +TU_ATTR_ALWAYS_INLINE static inline +void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) { // free all TDs from the head td to the first active TD while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) { ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; - hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); bool const is_ioc = (qtd->int_on_complete != 0); @@ -600,7 +603,8 @@ static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) } } -static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) +TU_ATTR_ALWAYS_INLINE static inline +void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) { ehci_qhd_t *p_qhd = async_head; do @@ -611,46 +615,55 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) if ( !p_qhd->qtd_overlay.halted ) { qhd_xfer_complete_isr(p_qhd); } + p_qhd = qhd_next(p_qhd); }while(p_qhd != async_head); // async list traversal, stop if loop around } -static void period_list_xfer_complete_isr(uint8_t hostid, uint32_t interval_ms) +TU_ATTR_ALWAYS_INLINE static inline +void period_list_xfer_complete_isr(uint8_t rhport, uint32_t interval_ms) { - uint16_t max_loop = 0; - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u); - ehci_link_t next_item = * get_period_head(hostid, interval_ms); + uint32_t const period_1ms_addr = (uint32_t) get_period_head(rhport, 1u); + ehci_link_t next_link = * get_period_head(rhport, interval_ms); - // TODO abstract max loop guard for period - while( !next_item.terminate && - !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) && - max_loop < (QHD_MAX + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUH_DEVICE_MAX) - { - switch ( next_item.type ) - { - case EHCI_QTYPE_QHD: - { - ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); - if ( !p_qhd_int->qtd_overlay.halted ) - { - qhd_xfer_complete_isr(p_qhd_int); + while (!next_link.terminate) { + if (interval_ms > 1 && period_1ms_addr == tu_align32(next_link.address)) { + // 1ms period list is end of list for all larger interval + break; + } + + uintptr_t const entry_addr = tu_align32(next_link.address); + + switch (next_link.type) { + case EHCI_QTYPE_QHD: { + ehci_qhd_t *qhd = (ehci_qhd_t *) entry_addr; + hcd_dcache_invalidate(qhd, sizeof(ehci_qhd_t)); + + if (!qhd->qtd_overlay.halted) { + qhd_xfer_complete_isr(qhd); } } - break; + break; + + case EHCI_QTYPE_ITD: + // TODO support hs ISO + break; - case EHCI_QTYPE_ITD: // TODO support hs/fs ISO case EHCI_QTYPE_SITD: - case EHCI_QTYPE_FSTN: + // TODO support split ISO + break; - default: break; + case EHCI_QTYPE_FSTN: + default: + break; } - next_item = *list_next(&next_item); - max_loop++; + next_link = *list_next(&next_link); } } -static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) +TU_ATTR_ALWAYS_INLINE static inline +void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) { volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay; @@ -666,22 +679,22 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) xfer_result = XFER_RESULT_STALLED; } - p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; - // if (XFER_RESULT_FAILED == xfer_result ) { // TU_LOG1(" QHD xfer err count: %d\n", qtd_overlay->err_count); // TU_BREAKPOINT(); // TODO skip unplugged device // while(1){} // } - // No TD yet, it is probably the probably an signal noise ? - TU_ASSERT(p_qhd->p_qtd_list_head, ); + ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; + TU_ASSERT(qtd, ); // No TD yet, probably a race condition or cache issue !? - p_qhd->p_qtd_list_head->used = 0; // free QTD + hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); + p_qhd->total_xferred_bytes += qtd->expected_bytes - qtd->total_bytes; + + qtd->used = 0; // free QTD qtd_remove_1st_from_qhd(p_qhd); - if ( 0 == p_qhd->ep_number ) - { + if ( 0 == p_qhd->ep_number ) { // control cannot be halted --> clear all qtd list p_qhd->p_qtd_list_head = NULL; p_qhd->p_qtd_list_tail = NULL; @@ -702,13 +715,15 @@ static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) } } -static void xfer_error_isr(uint8_t hostid) +TU_ATTR_ALWAYS_INLINE static inline +void xfer_error_isr(uint8_t hostid) { //------------- async list -------------// ehci_qhd_t * const async_head = qhd_async_head(hostid); ehci_qhd_t *p_qhd = async_head; do { + hcd_dcache_invalidate(p_qhd, sizeof(ehci_qhd_t)); qhd_xfer_error_isr( p_qhd ); p_qhd = qhd_next(p_qhd); }while(p_qhd != async_head); // async list traversal, stop if loop around @@ -728,6 +743,8 @@ static void xfer_error_isr(uint8_t hostid) case EHCI_QTYPE_QHD: { ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); + hcd_dcache_invalidate(p_qhd_int, sizeof(ehci_qhd_t)); + qhd_xfer_error_isr(p_qhd_int); } break; @@ -757,20 +774,17 @@ void hcd_int_handler(uint8_t rhport) return; } - if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) - { + if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) { ehci_data.uframe_number += (FRAMELIST_SIZE << 3); regs->status = EHCI_INT_MASK_FRAMELIST_ROLLOVER; // Acknowledge } - if (int_status & EHCI_INT_MASK_PORT_CHANGE) - { + if (int_status & EHCI_INT_MASK_PORT_CHANGE) { // Including: Force port resume, over-current change, enable/disable change and connect status change. uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_W1C; print_portsc(regs); - if (regs->portsc_bm.connect_status_change) - { + if (regs->portsc_bm.connect_status_change) { port_connect_status_change_isr(rhport); } @@ -778,15 +792,13 @@ void hcd_int_handler(uint8_t rhport) regs->status = EHCI_INT_MASK_PORT_CHANGE; // Acknowledge } - if (int_status & EHCI_INT_MASK_ERROR) - { + if (int_status & EHCI_INT_MASK_ERROR) { xfer_error_isr(rhport); regs->status = EHCI_INT_MASK_ERROR; // Acknowledge } //------------- some QTD/SITD/ITD with IOC set is completed -------------// - if (int_status & EHCI_INT_MASK_NXP_ASYNC) - { + if (int_status & EHCI_INT_MASK_NXP_ASYNC) { async_list_xfer_complete_isr(qhd_async_head(rhport)); regs->status = EHCI_INT_MASK_NXP_ASYNC; // Acknowledge } From 27acaa013bd8af8ac29e966d7dc145b33dd6bc73 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 15:44:08 +0700 Subject: [PATCH 11/16] refactor ehci, since usbh only queue 1 TD per queue head --- src/portable/ehci/ehci.c | 113 ++++++++++++--------------------------- src/portable/ehci/ehci.h | 16 ++---- 2 files changed, 37 insertions(+), 92 deletions(-) diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 0b45d7b939..a08731667e 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -156,9 +156,6 @@ static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); static inline ehci_qtd_t* qtd_find_free (void); -static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd); -static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new); -static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd); static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes); static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); @@ -473,11 +470,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet hcd_dcache_clean((void *) setup_packet, 8); hcd_dcache_clean_invalidate(td, sizeof(ehci_qtd_t)); - // sw region - qhd->p_qtd_list_head = td; - qhd->p_qtd_list_tail = td; - // attach TD + qhd->p_attached_qtd = td; // software management qhd->qtd_overlay.next.address = (uint32_t) td; hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); @@ -501,7 +495,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * qtd_init(qtd, buffer, buflen); - // first first data toggle is always 1 (data & setup stage) + // first data toggle is always 1 (data & setup stage) qtd->data_toggle = 1; qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT; } else { @@ -521,11 +515,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * } hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); - // Software: assign TD to QHD - qhd->p_qtd_list_head = qtd; - qhd->p_qtd_list_tail = qtd; - // attach TD to QHD start transferring + qhd->p_attached_qtd = qtd; // software management qhd->qtd_overlay.next.address = (uint32_t) qtd; hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); @@ -580,27 +571,25 @@ void port_connect_status_change_isr(uint8_t rhport) TU_ATTR_ALWAYS_INLINE static inline void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) { - // free all TDs from the head td to the first active TD - while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) - { - ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; - hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); + // examine TD attached to queue head + ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_attached_qtd; + if (qtd == NULL) return; // no TD attached + hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); - bool const is_ioc = (qtd->int_on_complete != 0); - uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0); + // TD is still active, no need to process + if (qtd->active) { + return; + } - p_qhd->total_xferred_bytes += qtd->expected_bytes - qtd->total_bytes; + uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; + uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0); - // TD need to be freed and removed from qhd, before invoking callback - qtd->used = 0; // free QTD - qtd_remove_1st_from_qhd(p_qhd); + // remove and free TD before invoking callback + p_qhd->p_attached_qtd = NULL; + qtd->used = 0; // free QTD - if (is_ioc) - { - hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, p_qhd->total_xferred_bytes, XFER_RESULT_SUCCESS, true); - p_qhd->total_xferred_bytes = 0; - } - } + // IOC is always set + hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, xferred_bytes, XFER_RESULT_SUCCESS, true); } TU_ATTR_ALWAYS_INLINE static inline @@ -685,20 +674,17 @@ void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) // while(1){} // } - ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head; + ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_attached_qtd; TU_ASSERT(qtd, ); // No TD yet, probably a race condition or cache issue !? hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); - p_qhd->total_xferred_bytes += qtd->expected_bytes - qtd->total_bytes; + uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; + p_qhd->p_attached_qtd = NULL; qtd->used = 0; // free QTD - qtd_remove_1st_from_qhd(p_qhd); if ( 0 == p_qhd->ep_number ) { - // control cannot be halted --> clear all qtd list - p_qhd->p_qtd_list_head = NULL; - p_qhd->p_qtd_list_tail = NULL; - + // control cannot be halted p_qhd->qtd_overlay.next.terminate = 1; p_qhd->qtd_overlay.alternate.terminate = 1; p_qhd->qtd_overlay.halted = 0; @@ -707,19 +693,17 @@ void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) p_setup->used = 0; } - // call USBH callback + // notify usbh uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0); - hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, p_qhd->total_xferred_bytes, xfer_result, true); - - p_qhd->total_xferred_bytes = 0; + hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, xferred_bytes, xfer_result, true); } } TU_ATTR_ALWAYS_INLINE static inline -void xfer_error_isr(uint8_t hostid) +void xfer_error_isr(uint8_t rhport) { //------------- async list -------------// - ehci_qhd_t * const async_head = qhd_async_head(hostid); + ehci_qhd_t * const async_head = qhd_async_head(rhport); ehci_qhd_t *p_qhd = async_head; do { @@ -729,10 +713,10 @@ void xfer_error_isr(uint8_t hostid) }while(p_qhd != async_head); // async list traversal, stop if loop around //------------- TODO refractor period list -------------// - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u); + uint32_t const period_1ms_addr = (uint32_t) get_period_head(rhport, 1u); for (uint32_t interval_ms=1; interval_ms <= FRAMELIST_SIZE; interval_ms *= 2) { - ehci_link_t next_item = * get_period_head(hostid, interval_ms); + ehci_link_t next_item = * get_period_head(rhport, interval_ms); // TODO abstract max loop guard for period while( !next_item.terminate && @@ -873,34 +857,6 @@ static inline ehci_qtd_t* qtd_find_free(void) return NULL; } -static inline ehci_qtd_t* qtd_next(ehci_qtd_t const * p_qtd ) -{ - return (ehci_qtd_t*) tu_align32(p_qtd->next.address); -} - -static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd) -{ - if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL - { - p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL; - }else - { - p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head ); - } -} - -static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) -{ - if (p_qhd->p_qtd_list_head == NULL) // empty list - { - p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = p_qtd_new; - }else - { - p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new; - p_qhd->p_qtd_list_tail = p_qtd_new; - } -} - static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { // address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise) @@ -955,15 +911,14 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c p_qhd->int_smask = p_qhd->fl_int_cmask = 0; } - p_qhd->fl_hub_addr = devtree_info.hub_addr; - p_qhd->fl_hub_port = devtree_info.hub_port; - p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet + p_qhd->fl_hub_addr = devtree_info.hub_addr; + p_qhd->fl_hub_port = devtree_info.hub_port; + p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet //------------- HCD Management Data -------------// - p_qhd->used = 1; - p_qhd->removing = 0; - p_qhd->p_qtd_list_head = NULL; - p_qhd->p_qtd_list_tail = NULL; + p_qhd->used = 1; + p_qhd->removing = 0; + p_qhd->p_attached_qtd = NULL; p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint //------------- active, but no TD list -------------// diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index d7d37d627b..b525131c65 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -164,12 +164,10 @@ typedef struct TU_ATTR_ALIGNED(32) uint8_t pid; uint8_t interval_ms; // polling interval in frames (or millisecond) - uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set - uint8_t reserved2[2]; + uint8_t TU_RESERVED[8]; - // TODO USBH will only queue 1 TD per QHD, thus we can remove the list - ehci_qtd_t * volatile p_qtd_list_head; // head of the scheduled TD list - ehci_qtd_t * volatile p_qtd_list_tail; // tail of the scheduled TD list + // usbh will only queue 1 TD per QHD + ehci_qtd_t * volatile p_attached_qtd; } ehci_qhd_t; TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" ); @@ -248,14 +246,6 @@ typedef struct TU_ATTR_ALIGNED(32) /// Word 4-5: Buffer Pointer List uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count -// union{ -// uint32_t BufferPointer1; -// struct { -// volatile uint32_t TCount : 3; -// volatile uint32_t TPosition : 2; -// }; -// }; - /*---------- Word 6 ----------*/ ehci_link_t back; From ec4bd39a9209f37b02edac2f6a1ea2ef27f6b785 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 16:41:06 +0700 Subject: [PATCH 12/16] refactor ehci: add attached_buffer for dcache invalidate for IN transfer --- src/portable/ehci/ehci.c | 116 ++++++++++++++++++++++----------------- src/portable/ehci/ehci.h | 8 ++- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index a08731667e..cc825d5498 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -154,6 +154,7 @@ static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd); static inline ehci_qhd_t* qhd_find_free (void); static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); +static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd); static inline ehci_qtd_t* qtd_find_free (void); static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes); @@ -468,13 +469,9 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet td->pid = EHCI_PID_SETUP; hcd_dcache_clean((void *) setup_packet, 8); - hcd_dcache_clean_invalidate(td, sizeof(ehci_qtd_t)); - // attach TD - qhd->p_attached_qtd = td; // software management - qhd->qtd_overlay.next.address = (uint32_t) td; - - hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); + // attach TD to QHD -> start transferring + qhd_attach_qtd(qhd, td); return true; } @@ -513,13 +510,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * }else { hcd_dcache_clean(buffer, buflen); } - hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); - // attach TD to QHD start transferring - qhd->p_attached_qtd = qtd; // software management - qhd->qtd_overlay.next.address = (uint32_t) qtd; - - hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); + // attach TD to QHD -> start transferring + qhd_attach_qtd(qhd, qtd); return true; } @@ -569,10 +562,9 @@ void port_connect_status_change_isr(uint8_t rhport) } TU_ATTR_ALWAYS_INLINE static inline -void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) -{ +void qhd_xfer_complete_isr(ehci_qhd_t * qhd) { // examine TD attached to queue head - ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_attached_qtd; + ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) qhd->attached_qtd; if (qtd == NULL) return; // no TD attached hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); @@ -581,15 +573,22 @@ void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) return; } + uint8_t dir = (qtd->pid == EHCI_PID_IN) ? 1 : 0; uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; - uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0); + + // invalidate dcache if IN transfer + if (dir == 1 && qhd->attached_buffer != 0) { + hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes); + } // remove and free TD before invoking callback - p_qhd->p_attached_qtd = NULL; + qhd->attached_qtd = NULL; + qhd->attached_buffer = 0; qtd->used = 0; // free QTD - // IOC is always set - hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, xferred_bytes, XFER_RESULT_SUCCESS, true); + // notify usbh + uint8_t const ep_addr = tu_edpt_addr(qhd->ep_number, dir); + hcd_event_xfer_complete(qhd->dev_addr, ep_addr, xferred_bytes, XFER_RESULT_SUCCESS, true); } TU_ATTR_ALWAYS_INLINE static inline @@ -651,10 +650,11 @@ void period_list_xfer_complete_isr(uint8_t rhport, uint32_t interval_ms) } } +// TODO merge with qhd_xfer_complete_isr() TU_ATTR_ALWAYS_INLINE static inline -void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) +void qhd_xfer_error_isr(ehci_qhd_t * qhd) { - volatile ehci_qtd_t *qtd_overlay = &p_qhd->qtd_overlay; + volatile ehci_qtd_t *qtd_overlay = &qhd->qtd_overlay; // TD has error if (qtd_overlay->halted) { @@ -674,28 +674,37 @@ void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) // while(1){} // } - ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_attached_qtd; + ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) qhd->attached_qtd; TU_ASSERT(qtd, ); // No TD yet, probably a race condition or cache issue !? hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); + + uint8_t dir = (qtd->pid == EHCI_PID_IN) ? 1 : 0; uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; - p_qhd->p_attached_qtd = NULL; + // invalidate dcache if IN transfer + if (dir == 1 && qhd->attached_buffer != 0) { + hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes); + } + + // remove and free TD before invoking callback + qhd->attached_qtd = NULL; + qhd->attached_buffer = 0; qtd->used = 0; // free QTD - if ( 0 == p_qhd->ep_number ) { + if (0 == qhd->ep_number ) { // control cannot be halted - p_qhd->qtd_overlay.next.terminate = 1; - p_qhd->qtd_overlay.alternate.terminate = 1; - p_qhd->qtd_overlay.halted = 0; + qhd->qtd_overlay.next.terminate = 1; + qhd->qtd_overlay.alternate.terminate = 1; + qhd->qtd_overlay.halted = 0; - ehci_qtd_t *p_setup = qtd_control(p_qhd->dev_addr); + ehci_qtd_t *p_setup = qtd_control(qhd->dev_addr); p_setup->used = 0; } // notify usbh - uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0); - hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, xferred_bytes, xfer_result, true); + uint8_t const ep_addr = tu_edpt_addr(qhd->ep_number, dir); + hcd_event_xfer_complete(qhd->dev_addr, ep_addr, xferred_bytes, xfer_result, true); } } @@ -846,22 +855,10 @@ static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) return NULL; } -//------------- TD helper -------------// -static inline ehci_qtd_t* qtd_find_free(void) -{ - for (uint32_t i=0; i cannot be cleared (ehci halted otherwise) - if (dev_addr != 0) - { + if (dev_addr != 0) { tu_memclr(p_qhd, sizeof(ehci_qhd_t)); } @@ -911,26 +908,47 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c p_qhd->int_smask = p_qhd->fl_int_cmask = 0; } - p_qhd->fl_hub_addr = devtree_info.hub_addr; - p_qhd->fl_hub_port = devtree_info.hub_port; - p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet + p_qhd->fl_hub_addr = devtree_info.hub_addr; + p_qhd->fl_hub_port = devtree_info.hub_port; + p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet //------------- HCD Management Data -------------// - p_qhd->used = 1; - p_qhd->removing = 0; - p_qhd->p_attached_qtd = NULL; + p_qhd->used = 1; + p_qhd->removing = 0; + p_qhd->attached_qtd = NULL; p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint //------------- active, but no TD list -------------// p_qhd->qtd_overlay.halted = 0; p_qhd->qtd_overlay.next.terminate = 1; p_qhd->qtd_overlay.alternate.terminate = 1; + if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT) { p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11 } } +static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) { + qhd->attached_qtd = qtd; + qhd->attached_buffer = qtd->buffer[0]; + + // clean and invalidate cache before physically write + hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t)); + + qhd->qtd_overlay.next.address = (uint32_t) qtd; + hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t)); +} + + +//------------- TD helper -------------// +static inline ehci_qtd_t *qtd_find_free(void) { + for (uint32_t i = 0; i < QTD_MAX; i++) { + if (!ehci_data.qtd_pool[i].used) return &ehci_data.qtd_pool[i]; + } + return NULL; +} + static void qtd_init(ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes) { tu_memclr(qtd, sizeof(ehci_qtd_t)); diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index b525131c65..8338fb419c 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -164,10 +164,12 @@ typedef struct TU_ATTR_ALIGNED(32) uint8_t pid; uint8_t interval_ms; // polling interval in frames (or millisecond) - uint8_t TU_RESERVED[8]; + uint8_t TU_RESERVED[4]; - // usbh will only queue 1 TD per QHD - ehci_qtd_t * volatile p_attached_qtd; + // Attached TD management, note usbh will only queue 1 TD per QHD. + // buffer for dcache invalidate since td's buffer is modified by HC and finding initial buffer address is not trivial + uint32_t attached_buffer; + ehci_qtd_t * volatile attached_qtd; } ehci_qhd_t; TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" ); From f22d8ee3b93000769ac8d177cd29467c81143f5e Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 May 2023 16:46:02 +0700 Subject: [PATCH 13/16] add rt1060 jlink config --- .idea/runConfigurations/rt1060_jlink.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .idea/runConfigurations/rt1060_jlink.xml diff --git a/.idea/runConfigurations/rt1060_jlink.xml b/.idea/runConfigurations/rt1060_jlink.xml new file mode 100644 index 0000000000..eabadaf590 --- /dev/null +++ b/.idea/runConfigurations/rt1060_jlink.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file From f26a93908ef9dcde6818c9b271727ed6c77e87d8 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 May 2023 10:56:52 +0700 Subject: [PATCH 14/16] only clean/invalidate dcache on imxrt if memory is not in DTCM --- src/portable/chipidea/ci_hs/hcd_ci_hs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c index ecb1a621c6..ab6a42e118 100644 --- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c @@ -42,17 +42,28 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT #include "ci_hs_imxrt.h" + // check if memory is cacheable i.e not in DTCM + TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uint32_t addr) { + return !(0x20000000 <= addr && addr < 0x20100000); + } + void hcd_dcache_clean(void* addr, uint32_t data_size) { - SCB_CleanDCache_by_Addr((uint32_t*) addr, (int32_t) data_size); + if (is_cache_mem((uint32_t) addr)) { + SCB_CleanDCache_by_Addr((uint32_t *) addr, (int32_t) data_size); + } } void hcd_dcache_invalidate(void* addr, uint32_t data_size) { - SCB_InvalidateDCache_by_Addr(addr, (int32_t) data_size); + if (is_cache_mem((uint32_t) addr)) { + SCB_InvalidateDCache_by_Addr(addr, (int32_t) data_size); + } } void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) { + if (is_cache_mem((uint32_t) addr)) { SCB_CleanInvalidateDCache_by_Addr(addr, (int32_t) data_size); } +} #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) #include "ci_hs_lpc18_43.h" From 5dae5e12928ba789cd7e248edd9fa91376902925 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 May 2023 13:32:49 +0700 Subject: [PATCH 15/16] ehci fix dcache clean when control endpoint failed --- hw/bsp/imxrt/family.cmake | 6 +++++- src/host/usbh.c | 3 ++- src/portable/ehci/ehci.c | 10 ++++------ src/tusb.c | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake index 4628abc341..a475b47219 100644 --- a/hw/bsp/imxrt/family.cmake +++ b/hw/bsp/imxrt/family.cmake @@ -57,12 +57,16 @@ if (NOT TARGET ${BOARD_TARGET}) ) update_board(${BOARD_TARGET}) + if (NOT DEFINED LD_FILE_${TOOLCHAIN}) + set(LD_FILE_gcc ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx_flexspi_nor.ld) + endif () + if (TOOLCHAIN STREQUAL "gcc") target_sources(${BOARD_TARGET} PUBLIC ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT}.S ) target_link_options(${BOARD_TARGET} PUBLIC - "LINKER:--script=${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx_flexspi_nor.ld" + "LINKER:--script=${LD_FILE_gcc}" "LINKER:-Map=$>,$,$>${CMAKE_EXECUTABLE_SUFFIX}.map" # nanolib --specs=nosys.specs diff --git a/src/host/usbh.c b/src/host/usbh.c index c56ff94597..7b265c742f 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -440,7 +440,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const ep_dir = tu_edpt_dir(ep_addr); - TU_LOG_USBH("on EP %02X with %u bytes %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, + TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, tu_str_xfer_result[event.xfer_complete.result]); if (event.dev_addr == 0) @@ -1255,6 +1255,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { failed_count++; osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit + TU_LOG1("Enumeration attempt %u\r\n", failed_count); TU_ASSERT(tuh_control_xfer(xfer), ); }else { diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index cc825d5498..10e2db7b57 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -577,7 +577,7 @@ void qhd_xfer_complete_isr(ehci_qhd_t * qhd) { uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; // invalidate dcache if IN transfer - if (dir == 1 && qhd->attached_buffer != 0) { + if (dir == 1 && qhd->attached_buffer != 0 && xferred_bytes > 0) { hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes); } @@ -660,7 +660,7 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd) if (qtd_overlay->halted) { xfer_result_t xfer_result; - if (qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err || qtd_overlay->xact_err) { + if (qtd_overlay->xact_err || qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err) { // Error count = 0 often occurs when device disconnected, or other bus-related error xfer_result = XFER_RESULT_FAILED; }else { @@ -671,7 +671,6 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd) // if (XFER_RESULT_FAILED == xfer_result ) { // TU_LOG1(" QHD xfer err count: %d\n", qtd_overlay->err_count); // TU_BREAKPOINT(); // TODO skip unplugged device -// while(1){} // } ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) qhd->attached_qtd; @@ -683,7 +682,7 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd) uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes; // invalidate dcache if IN transfer - if (dir == 1 && qhd->attached_buffer != 0) { + if (dir == 1 && qhd->attached_buffer != 0 && xferred_bytes > 0) { hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes); } @@ -698,8 +697,7 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd) qhd->qtd_overlay.alternate.terminate = 1; qhd->qtd_overlay.halted = 0; - ehci_qtd_t *p_setup = qtd_control(qhd->dev_addr); - p_setup->used = 0; + hcd_dcache_clean(qhd, sizeof(ehci_qhd_t)); } // notify usbh diff --git a/src/tusb.c b/src/tusb.c index 7327db6853..465b608b03 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -440,7 +440,7 @@ char const* const tu_str_std_request[] = }; char const* const tu_str_xfer_result[] = { - "OK", "Failed", "Stalled", "Timeout" + "OK", "FAILED", "STALLED", "TIMEOUT" }; #endif From 7211dd18b46affdf027522e34ff3aa3b5d8d5df5 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 May 2023 13:42:26 +0700 Subject: [PATCH 16/16] more dcache fix --- src/portable/ehci/ehci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 10e2db7b57..852e7f4fee 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -273,19 +273,19 @@ static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) { } // Close all opened endpoint belong to this device -void hcd_device_close(uint8_t rhport, uint8_t dev_addr) +void hcd_device_close(uint8_t rhport, uint8_t daddr) { // skip dev0 - if (dev_addr == 0) { + if (daddr == 0) { return; } // Remove from async list - list_remove_qhd_by_daddr((ehci_link_t *) qhd_async_head(rhport), dev_addr); + list_remove_qhd_by_daddr((ehci_link_t *) qhd_async_head(rhport), daddr); // Remove from all interval period list for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) { - list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], dev_addr); + list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], daddr); } // Async doorbell (EHCI 4.8.2 for operational details) @@ -453,7 +453,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD); hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t)); - hcd_dcache_clean(list_head, sizeof(ehci_link_t)); + hcd_dcache_clean(list_head, sizeof(ehci_qhd_t)); return true; }