From 1330b98ad8b570c51059ede98c9f68c6b1d8cbea Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Sun, 5 Feb 2023 15:16:30 +0800 Subject: [PATCH] update ehci driver --- port/ehci/usb_hc_ehci.c | 1452 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1452 insertions(+) create mode 100644 port/ehci/usb_hc_ehci.c diff --git a/port/ehci/usb_hc_ehci.c b/port/ehci/usb_hc_ehci.c new file mode 100644 index 00000000..78d70ac7 --- /dev/null +++ b/port/ehci/usb_hc_ehci.c @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_hub.h" +#include "usb_hc_ehci.h" + +#ifndef USBH_IRQHandler +#define USBH_IRQHandler USBH_IRQHandler +#endif + +#define EHCI_HCCR ((struct ehci_hccr *)CONFIG_USB_EHCI_HCCR_BASE) +#define EHCI_HCOR ((struct ehci_hcor *)CONFIG_USB_EHCI_HCOR_BASE) + +#define EHCI_PTR2ADDR(x) ((uint32_t)x) +#define EHCI_ADDRALIGN32(x) ((uint32_t)(x) & ~0x1F) +#define EHCI_ADDR2QH(x) ((struct ehci_qh_hw *)((uint32_t)(x) & ~0x1F)) +#define EHCI_ADDR2ITD(x) ((struct ehci_itd_hw *)((uint32_t)(x) & ~0x1F)) + +#if CONFIG_USB_EHCI_FRAME_LIST_SIZE == 1024 +#define EHCI_PERIOIDIC_QH_NUM 11 +#elif CONFIG_USB_EHCI_FRAME_LIST_SIZE == 512 +#define EHCI_PERIOIDIC_QH_NUM 10 +#elif CONFIG_USB_EHCI_FRAME_LIST_SIZE == 256 +#define EHCI_PERIOIDIC_QH_NUM 9 +#else +#error Unsupported frame size list size +#endif + +#define CONFIG_USB_EHCI_QH_NUM CONFIG_USBHOST_PIPE_NUM +#define CONFIG_USB_EHCI_QTD_NUM (CONFIG_USBHOST_PIPE_NUM * 3) +#define CONFIG_USB_EHCI_ITD_NUM 10 + +extern uint8_t usbh_get_port_speed(const uint8_t port); + +struct ehci_qh_hw; +struct ehci_itd_hw; +struct ehci_pipe { + uint8_t dev_addr; + uint8_t ep_addr; + uint8_t ep_type; + uint8_t ep_interval; + uint8_t speed; + uint8_t mult; + uint16_t ep_mps; + bool toggle; + bool inuse; + uint32_t xfrd; + bool waiter; + usb_osal_sem_t waitsem; + struct usbh_hubport *hport; + struct ehci_qh_hw *qh; + struct usbh_urb *urb; + uint8_t used_itd_num; + uint8_t id; + uint8_t mf_unmask; + uint8_t mf_valid; + usb_slist_t iso_list; +}; + +struct ehci_qh_hw { + struct ehci_qh hw; + uint32_t first_qtd; + struct ehci_pipe *pipe; +} __attribute__((aligned(32))); + +struct ehci_qtd_hw { + struct ehci_qtd hw; +} __attribute__((aligned(32))); + +struct ehci_itd_hw { + struct ehci_itd hw; +} __attribute__((aligned(32))); + +struct ehci_hcd { + bool ehci_qh_used[CONFIG_USB_EHCI_QH_NUM]; + bool ehci_qtd_used[CONFIG_USB_EHCI_QTD_NUM]; + bool ehci_itd_pool_used[2]; + struct ehci_pipe pipe_pool[CONFIG_USB_EHCI_QH_NUM]; +} g_ehci_hcd; + +USB_NOCACHE_RAM_SECTION struct ehci_qh_hw ehci_qh_pool[CONFIG_USB_EHCI_QH_NUM]; +USB_NOCACHE_RAM_SECTION struct ehci_qtd_hw ehci_qtd_pool[CONFIG_USB_EHCI_QTD_NUM]; +USB_NOCACHE_RAM_SECTION struct ehci_itd_hw ehci_itd_pool[2][CONFIG_USB_EHCI_ITD_NUM]; + +/* The head of the asynchronous queue */ +USB_NOCACHE_RAM_SECTION struct ehci_qh_hw g_async_qh_head; +/* The head of the periodic queue */ +USB_NOCACHE_RAM_SECTION struct ehci_qh_hw g_periodic_qh_head[EHCI_PERIOIDIC_QH_NUM]; + +/* The frame list */ +USB_NOCACHE_RAM_SECTION uint32_t g_framelist[CONFIG_USB_EHCI_FRAME_LIST_SIZE] __attribute__((aligned(4096))); + +usb_slist_t iso_pipe_list_head = USB_SLIST_OBJECT_INIT(iso_pipe_list_head); + +typedef void (*ehci_qh_cb)(struct ehci_qh_hw *head, struct ehci_qh_hw *qh); + +static const uint8_t g_ehci_speed[4] = { + 0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED +}; + +static struct ehci_qh_hw *ehci_qh_alloc(void) +{ + struct ehci_qh_hw *qh; + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QH_NUM; i++) { + if (!g_ehci_hcd.ehci_qh_used[i]) { + g_ehci_hcd.ehci_qh_used[i] = true; + qh = &ehci_qh_pool[i]; + memset(qh, 0, sizeof(struct ehci_qh_hw)); + qh->hw.hlp = QTD_LIST_END; + qh->hw.overlay.next_qtd = QTD_LIST_END; + qh->hw.overlay.alt_next_qtd = QTD_LIST_END; + return qh; + } + } + return NULL; +} + +static void ehci_qh_free(struct ehci_qh_hw *qh) +{ + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QH_NUM; i++) { + if (&ehci_qh_pool[i] == qh) { + g_ehci_hcd.ehci_qh_used[i] = false; + return; + } + } +} + +static struct ehci_qtd_hw *ehci_qtd_alloc(void) +{ + struct ehci_qtd_hw *qtd; + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) { + if (!g_ehci_hcd.ehci_qtd_used[i]) { + g_ehci_hcd.ehci_qtd_used[i] = true; + qtd = &ehci_qtd_pool[i]; + memset(qtd, 0, sizeof(struct ehci_qtd_hw)); + qtd->hw.next_qtd = QTD_LIST_END; + qtd->hw.alt_next_qtd = QTD_LIST_END; + qtd->hw.token = QTD_TOKEN_STATUS_HALTED; + return qtd; + } + } + return NULL; +} + +static void ehci_qtd_free(struct ehci_qtd_hw *qtd) +{ + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) { + if (&ehci_qtd_pool[i] == qtd) { + g_ehci_hcd.ehci_qtd_used[i] = false; + return; + } + } +} + +// static int ehci_itd_pool_alloc(void) +// { +// for (uint32_t i = 0; i < 2; i++) { +// if (!g_ehci_hcd.ehci_itd_pool_used[i]) { +// g_ehci_hcd.ehci_itd_pool_used[i] = true; +// return i; +// } +// } +// return -1; +// } + +// static void ehci_itd_pool_free(uint8_t id) +// { +// g_ehci_hcd.ehci_itd_pool_used[id] = false; +// } + +static struct ehci_pipe *ehci_pipe_alloc(void) +{ + int pipe; + + for (pipe = 0; pipe < CONFIG_USB_EHCI_QH_NUM; pipe++) { + if (!g_ehci_hcd.pipe_pool[pipe].inuse) { + g_ehci_hcd.pipe_pool[pipe].inuse = true; + return &g_ehci_hcd.pipe_pool[pipe]; + } + } + return NULL; +} + +static void ehci_pipe_free(struct ehci_pipe *pipe) +{ + pipe->inuse = false; + ehci_itd_pool_free(pipe->id); +} + +// static inline void ehci_iso_pipe_add_tail(struct ehci_pipe *pipe) +// { +// usb_slist_add_tail(&iso_pipe_list_head, &pipe->iso_list); +// } + +// static inline void ehci_iso_pipe_remove(struct ehci_pipe *pipe) +// { +// usb_slist_remove(&iso_pipe_list_head, &pipe->iso_list); +// } + +static inline void ehci_qh_add_head(struct ehci_qh_hw *head, struct ehci_qh_hw *n) +{ + n->hw.hlp = head->hw.hlp; + head->hw.hlp = QH_HLP_QH(n); +} + +static inline void ehci_qh_remove(struct ehci_qh_hw *head, struct ehci_qh_hw *n) +{ + struct ehci_qh_hw *tmp = head; + + while (EHCI_ADDR2QH(tmp->hw.hlp) && EHCI_ADDR2QH(tmp->hw.hlp) != n) { + tmp = EHCI_ADDR2QH(tmp->hw.hlp); + } + + if (tmp) { + tmp->hw.hlp = n->hw.hlp; + } +} + +static inline void ehci_itd_add_head(struct ehci_itd_hw *itd, uint32_t sched_frame) +{ + itd->hw.nlp = g_framelist[sched_frame]; + g_framelist[sched_frame] = ITD_NLP_ITD(itd); +} + +static inline void ehci_itd_remove(struct ehci_itd_hw *itd, uint32_t sched_frame) +{ + if (g_framelist[sched_frame] == ITD_NLP_ITD(itd)) { + g_framelist[sched_frame] = itd->hw.nlp; + } else { + struct ehci_itd_hw *tmp_itd = EHCI_ADDR2ITD(g_framelist[sched_frame]); + while ((EHCI_ADDR2ITD(tmp_itd->hw.nlp) != itd) && tmp_itd) { + tmp_itd = EHCI_ADDR2ITD(tmp_itd->hw.nlp); + } + + if (tmp_itd) { + tmp_itd->hw.nlp = itd->hw.nlp; + } + } +} + +static int ehci_caculate_smask(int binterval) +{ + int order, interval; + + interval = 1; + while (binterval > 1) { + interval *= 2; + binterval--; + } + + if (interval < 2) /* interval 1 */ + return 0xFF; + if (interval < 4) /* interval 2 */ + return 0x55; + if (interval < 8) /* interval 4 */ + return 0x22; + for (order = 0; (interval > 1); order++) { + interval >>= 1; + } + return (0x1 << (order % 8)); +} + +static struct ehci_qh_hw *ehci_get_periodic_qhead(uint8_t interval) +{ + interval /= 8; + + for (uint8_t i = 0; i < EHCI_PERIOIDIC_QH_NUM - 1; i++) { + interval >>= 1; + if (interval == 0) { + return &g_periodic_qh_head[i]; + } + } + return &g_periodic_qh_head[EHCI_PERIOIDIC_QH_NUM - 1]; +} + +static void ehci_qh_fill(struct ehci_qh_hw *qh, + struct ehci_pipe *pipe) +{ + struct usbh_hub *hub; + uint32_t regval; + + /* QH endpoint characteristics: + * + * FIELD DESCRIPTION + * -------- ------------------------------- + * DEVADDR Device address + * I Inactivate on Next Transaction + * ENDPT Endpoint number + * EPS Endpoint speed + * DTC Data toggle control + * MAXPKT Max packet size + * C Control endpoint + * RL NAK count reloaded + */ + regval = ((uint32_t)pipe->dev_addr << QH_EPCHAR_DEVADDR_SHIFT) | + ((uint32_t)(pipe->ep_addr & 0xf) << QH_EPCHAR_ENDPT_SHIFT) | + ((uint32_t)g_ehci_speed[pipe->speed] << QH_EPCHAR_EPS_SHIFT) | + ((uint32_t)pipe->ep_mps << QH_EPCHAR_MAXPKT_SHIFT) | + QH_EPCHAR_DTC | + ((uint32_t)0 << QH_EPCHAR_RL_SHIFT); + + if (pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL && (pipe->speed != USB_SPEED_HIGH)) { + regval |= QH_EPCHAR_C; + } + qh->hw.epchar = regval; + + /* QH endpoint capabilities + * + * FIELD DESCRIPTION + * -------- ------------------------------- + * SSMASK Interrupt Schedule Mask + * SCMASK Split Completion Mask + * HUBADDR Hub Address + * PORT Port number + * MULT High band width multiplier + */ + regval = 0; + + hub = pipe->hport->parent; + + regval |= QH_EPCAPS_HUBADDR(hub->hub_addr); + regval |= QH_EPCAPS_PORT(pipe->hport->port); + + if (pipe->ep_type == USB_ENDPOINT_TYPE_INTERRUPT) { + if (pipe->speed == USB_SPEED_HIGH) { + regval |= ehci_caculate_smask(pipe->ep_interval); + } else { + regval |= QH_EPCAPS_SSMASK(2); + regval |= QH_EPCAPS_SCMASK(0x78); + } + regval |= QH_EPCAPS_MULT(1); + } + + qh->hw.epcap = regval; + + qh->pipe = pipe; + pipe->qh = qh; +} + +static void ehci_qtd_bpl_fill(struct ehci_qtd_hw *qtd, uint32_t bufaddr, size_t buflen) +{ + uint32_t rest; + + qtd->hw.bpl[0] = bufaddr; + rest = 0x1000 - (bufaddr & 0xfff); + + if (buflen < rest) { + rest = buflen; + } else { + bufaddr += 0x1000; + bufaddr &= ~0x0fff; + + for (int i = 1; rest < buflen && i < 5; i++) { + qtd->hw.bpl[i] = bufaddr; + bufaddr += 0x1000; + + if ((rest + 0x1000) < buflen) { + rest += 0x1000; + } else { + rest = buflen; + } + } + } +} + +static void ehci_qtd_fill(struct ehci_pipe *pipe, struct ehci_qtd_hw *qtd, uint32_t bufaddr, size_t buflen, uint32_t token) +{ + /* qTD token + * + * FIELD DESCRIPTION + * -------- ------------------------------- + * STATUS Status + * PID PID Code + * CERR Error Counter + * CPAGE Current Page + * IOC Interrupt on complete + * NBYTES Total Bytes to Transfer + * TOGGLE Data Toggle + */ + + qtd->hw.token = token; + + ehci_qtd_bpl_fill(qtd, bufaddr, buflen); + pipe->xfrd += buflen; +} + +// static void ehci_itd_bpl_fill(struct ehci_itd_hw *itd, struct ehci_pipe *pipe, uint32_t start_addr) +// { +// uint32_t buf_page_addr = start_addr & 0xFFFFF000; /* align 4k */ + +// for (uint32_t i = 0; i < 7; i++) { +// itd->hw.bpl[i] = buf_page_addr; +// buf_page_addr += 0x1000; +// } +// itd->hw.bpl[0] |= ((uint32_t)pipe->dev_addr << ITD_BUFPTR0_DEVADDR_SHIFT) | +// ((uint32_t)(pipe->ep_addr & 0x7f) << ITD_BUFPTR0_ENDPT_SHIFT); + +// itd->hw.bpl[1] |= (pipe->ep_mps << ITD_BUFPTR1_MAXPKT_SHIFT); +// if (pipe->ep_addr & 0x80) { +// itd->hw.bpl[1] |= ITD_BUFPTR1_DIRIN; +// } else { +// itd->hw.bpl[1] |= ITD_BUFPTR1_DIROUT; +// } +// itd->hw.bpl[2] |= (pipe->mult + 1); /* Mult */ +// } + +// static void ehci_itd_tscl_fill(struct ehci_itd_hw *itd, +// struct ehci_pipe *pipe, +// uint8_t mf, +// uint32_t start_addr, +// uint32_t bufaddr, +// uint32_t buflen) +// { +// uint32_t pg = (bufaddr & 0xFFFFF000) - (start_addr & 0xFFFFF000); +// itd->hw.tscl[mf] = (bufaddr & ITD_TSCL_XOFFS_MASK) | /* Transaction offset */ +// (pg & ITD_TSCL_PG_MASK) | /* PG */ +// (((uint32_t)buflen & 0xfff) << ITD_TSCL_LENGTH_SHIFT) | /* Transaction Length */ +// ITD_TSCL_STATUS_ACTIVE; /* Status */ +// } + +static struct ehci_qh_hw *ehci_control_pipe_init(struct ehci_pipe *pipe, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen) +{ + struct ehci_qh_hw *qh = NULL; + struct ehci_qtd_hw *qtd_setup = NULL; + struct ehci_qtd_hw *qtd_data = NULL; + struct ehci_qtd_hw *qtd_status = NULL; + uint32_t token; + + qh = ehci_qh_alloc(); + if (qh == NULL) { + return NULL; + } + + qtd_setup = ehci_qtd_alloc(); + if (buflen > 0) { + qtd_data = ehci_qtd_alloc(); + } + + qtd_status = ehci_qtd_alloc(); + if (qtd_status == NULL) { + ehci_qh_free(qh); + if (qtd_setup) { + ehci_qtd_free(qtd_setup); + } + if (qtd_data) { + ehci_qtd_free(qtd_data); + } + return NULL; + } + + ehci_qh_fill(qh, pipe); + + /* fill setup qtd */ + token = QTD_TOKEN_STATUS_ACTIVE | + QTD_TOKEN_PID_SETUP | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)8 << QTD_TOKEN_NBYTES_SHIFT); + + ehci_qtd_fill(pipe, qtd_setup, EHCI_PTR2ADDR(setup), 8, token); + + /* fill data qtd */ + if (setup->wLength > 0) { + if ((setup->bmRequestType & 0x80) == 0x80) { + token = QTD_TOKEN_PID_IN; + } else { + token = QTD_TOKEN_PID_OUT; + } + token |= QTD_TOKEN_STATUS_ACTIVE | + QTD_TOKEN_PID_OUT | + QTD_TOKEN_TOGGLE | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)buflen << QTD_TOKEN_NBYTES_SHIFT); + + ehci_qtd_fill(pipe, qtd_data, EHCI_PTR2ADDR(buffer), buflen, token); + qtd_setup->hw.next_qtd = EHCI_PTR2ADDR(qtd_data); + qtd_data->hw.next_qtd = EHCI_PTR2ADDR(qtd_status); + } else { + qtd_setup->hw.next_qtd = EHCI_PTR2ADDR(qtd_status); + } + + /* fill status qtd */ + if ((setup->bmRequestType & 0x80) == 0x80) { + token = QTD_TOKEN_PID_OUT; + } else { + token = QTD_TOKEN_PID_IN; + } + token |= QTD_TOKEN_STATUS_ACTIVE | + QTD_TOKEN_TOGGLE | + QTD_TOKEN_IOC | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)0 << QTD_TOKEN_NBYTES_SHIFT); + + ehci_qtd_fill(pipe, qtd_status, 0, 0, token); + qtd_status->hw.next_qtd = QTD_LIST_END; + + /* update qh first qtd */ + qh->hw.overlay.next_qtd = EHCI_PTR2ADDR(qtd_setup); + + /* record qh first qtd */ + qh->first_qtd = EHCI_PTR2ADDR(qtd_setup); + + /* add qh into async list */ + ehci_qh_add_head(&g_async_qh_head, qh); + return qh; +} + +static struct ehci_qh_hw *ehci_bulk_pipe_init(struct ehci_pipe *pipe, uint8_t *buffer, uint32_t buflen) +{ + struct ehci_qh_hw *qh = NULL; + struct ehci_qtd_hw *qtd = NULL; + struct ehci_qtd_hw *first_qtd = NULL; + struct ehci_qtd_hw *prev_qtd = NULL; + uint32_t qtd_num = 0; + uint32_t xfer_len = 0; + uint32_t token; + + qh = ehci_qh_alloc(); + if (qh == NULL) { + return NULL; + } + + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) { + if (!g_ehci_hcd.ehci_qtd_used[i]) { + qtd_num++; + } + } + + if (qtd_num < ((buflen + 0x3fff) / 0x4000)) { + ehci_qh_free(qh); + return NULL; + } + + ehci_qh_fill(qh, pipe); + + while (buflen >= 0) { + qtd = ehci_qtd_alloc(); + + if (buflen > 0x4000) { + xfer_len = 0x4000; + buflen -= 0x4000; + } else { + xfer_len = buflen; + buflen = 0; + } + + /* fill qtd */ + if (pipe->toggle) { + token = QTD_TOKEN_TOGGLE; + } else { + token = 0; + } + + if (pipe->ep_addr & 0x80) { + token |= QTD_TOKEN_PID_IN; + } else { + token |= QTD_TOKEN_PID_OUT; + } + + token |= QTD_TOKEN_STATUS_ACTIVE | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)xfer_len << QTD_TOKEN_NBYTES_SHIFT); + + if (buflen == 0) { + token |= QTD_TOKEN_IOC; + } + + ehci_qtd_fill(pipe, qtd, (uint32_t)buffer, xfer_len, token); + qtd->hw.next_qtd = QTD_LIST_END; + buffer += xfer_len; + + if (prev_qtd) { + prev_qtd->hw.next_qtd = EHCI_PTR2ADDR(qtd); + } else { + first_qtd = qtd; + } + prev_qtd = qtd; + + if (buflen == 0) { + break; + } + } + + /* update qh first qtd */ + qh->hw.overlay.next_qtd = EHCI_PTR2ADDR(first_qtd); + + /* record qh first qtd */ + qh->first_qtd = EHCI_PTR2ADDR(first_qtd); + + /* add qh into async list */ + ehci_qh_add_head(&g_async_qh_head, qh); + return qh; +} + +static struct ehci_qh_hw *ehci_intr_pipe_init(struct ehci_pipe *pipe, uint8_t *buffer, uint32_t buflen) +{ + struct ehci_qh_hw *qh = NULL; + struct ehci_qtd_hw *qtd = NULL; + struct ehci_qtd_hw *first_qtd = NULL; + struct ehci_qtd_hw *prev_qtd = NULL; + uint32_t qtd_num = 0; + uint32_t xfer_len = 0; + uint32_t token; + + qh = ehci_qh_alloc(); + if (qh == NULL) { + return NULL; + } + + for (uint32_t i = 0; i < CONFIG_USB_EHCI_QTD_NUM; i++) { + if (!g_ehci_hcd.ehci_qtd_used[i]) { + qtd_num++; + } + } + + if (qtd_num < ((buflen + 0x3fff) / 0x4000)) { + ehci_qh_free(qh); + return NULL; + } + + ehci_qh_fill(qh, pipe); + + while (buflen >= 0) { + qtd = ehci_qtd_alloc(); + + if (buflen > 0x4000) { + xfer_len = 0x4000; + buflen -= 0x4000; + } else { + xfer_len = buflen; + buflen = 0; + } + + /* fill qtd */ + if (pipe->toggle) { + token = QTD_TOKEN_TOGGLE; + } else { + token = 0; + } + + if (pipe->ep_addr & 0x80) { + token |= QTD_TOKEN_PID_IN; + } else { + token |= QTD_TOKEN_PID_OUT; + } + + token |= QTD_TOKEN_STATUS_ACTIVE | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)xfer_len << QTD_TOKEN_NBYTES_SHIFT); + + if (buflen == 0) { + token |= QTD_TOKEN_IOC; + } + + ehci_qtd_fill(pipe, qtd, (uint32_t)buffer, xfer_len, token); + qtd->hw.next_qtd = QTD_LIST_END; + buffer += xfer_len; + + if (prev_qtd) { + prev_qtd->hw.next_qtd = EHCI_PTR2ADDR(qtd); + } else { + first_qtd = qtd; + } + prev_qtd = qtd; + + if (buflen == 0) { + break; + } + } + + /* update qh first qtd */ + qh->hw.overlay.next_qtd = EHCI_PTR2ADDR(first_qtd); + + /* record qh first qtd */ + qh->first_qtd = EHCI_PTR2ADDR(first_qtd); + + /* add qh into periodic list */ + if (pipe->speed == USB_SPEED_HIGH) { + ehci_qh_add_head(ehci_get_periodic_qhead(pipe->ep_interval), qh); + } else { + ehci_qh_add_head(ehci_get_periodic_qhead(pipe->ep_interval * 8), qh); + } + + return qh; +} + +static int ehci_iso_pipe_init(struct ehci_pipe *pipe, struct usbh_iso_frame_packet *iso_packet, uint32_t num_of_iso_packets) +{ + /* if you want to use iso, please contact with me */ + return 0; +} + +static inline void ehci_pipe_waitup(struct ehci_pipe *pipe) +{ + struct usbh_urb *urb; + urb = pipe->urb; + pipe->urb = NULL; + + if (pipe->waiter) { + pipe->waiter = false; + usb_osal_sem_give(pipe->waitsem); + } + + if (urb->complete) { + if (urb->errorcode < 0) { + urb->complete(urb->arg, urb->errorcode); + } else { + urb->complete(urb->arg, urb->actual_length); + } + } +} + +static void ehci_qh_scan_qtds(struct ehci_qh_hw *qh, struct ehci_pipe *pipe) +{ + struct ehci_qtd_hw *qtd; + struct ehci_qtd_hw *next; + + if ((qh->first_qtd & QTD_LIST_END) == 0) { + qtd = (struct ehci_qtd_hw *)qh->first_qtd; + while (qtd) { + if (qtd->hw.next_qtd & QTD_LIST_END) { + next = NULL; + } else { + next = (struct ehci_qtd_hw *)qtd->hw.next_qtd; + } + + qh->first_qtd = qtd->hw.next_qtd; + + if (pipe) { + pipe->xfrd -= (qtd->hw.token & QTD_TOKEN_NBYTES_MASK) >> + QTD_TOKEN_NBYTES_SHIFT; + } + + ehci_qtd_free(qtd); + + qtd = next; + } + } +} + +static void ehci_check_qh(struct ehci_qh_hw *qhead, struct ehci_qh_hw *qh, struct ehci_pipe *pipe) +{ + struct usbh_urb *urb; + uint32_t token; + + token = qh->hw.overlay.token; + + if (token & QTD_TOKEN_STATUS_ACTIVE) { + } else { + urb = pipe->urb; + + ehci_qh_scan_qtds(qh, pipe); + if (qh->first_qtd & QTD_LIST_END) { + /* remove qh from list */ + ehci_qh_remove(qhead, qh); + + if ((token & QTD_TOKEN_STATUS_ERRORS) == 0) { + if (token & QTD_TOKEN_TOGGLE) { + pipe->toggle = true; + } else { + pipe->toggle = false; + } + urb->errorcode = 0; + } else { + if (token & QTD_TOKEN_STATUS_BABBLE) { + urb->errorcode = -EPERM; + pipe->toggle = 0; + } else if (token & QTD_TOKEN_STATUS_HALTED) { + urb->errorcode = -EPERM; + pipe->toggle = 0; + } else if (token & (QTD_TOKEN_STATUS_DBERR | QTD_TOKEN_STATUS_XACTERR)) { + urb->errorcode = -EIO; + } + } + + urb->actual_length = pipe->xfrd; + ehci_qh_free(qh); + pipe->qh = NULL; + + ehci_pipe_waitup(pipe); + } + } +} + +static void ehci_kill_qh(struct ehci_qh_hw *qhead, struct ehci_qh_hw *qh) +{ + ehci_qh_remove(qhead, qh); + ehci_qh_scan_qtds(qh, NULL); + ehci_qh_free(qh); +} + +static int usbh_reset_port(const uint8_t port) +{ + uint32_t timeout = 0; + uint32_t regval; + +#if CONFIG_USB_EHCI_HCOR_BASE == (0xF2020000UL + 0x140) + if ((*(volatile uint32_t *)(0xF2020000UL + 0x224) & 0xc0) == (2 << 6)) { /* Hardcode for hpm */ + EHCI_HCOR->portsc[port - 1] |= (1 << 29); + } else { + EHCI_HCOR->portsc[port - 1] &= ~(1 << 29); + } +#endif + regval = EHCI_HCOR->portsc[port - 1]; + regval &= ~EHCI_PORTSC_PE; + regval |= EHCI_PORTSC_RESET; + EHCI_HCOR->portsc[port - 1] = regval; + usb_osal_msleep(55); + + EHCI_HCOR->portsc[port - 1] &= ~EHCI_PORTSC_RESET; + while ((EHCI_HCOR->portsc[port - 1] & EHCI_PORTSC_RESET) != 0) { + usb_osal_msleep(1); + timeout++; + if (timeout > 100) { + return -ETIMEDOUT; + } + } + + return 0; +} + +__WEAK void usb_hc_low_level_init(void) +{ +} + +__WEAK void usb_hc_low_level2_init(void) +{ +} + +int usb_hc_init(void) +{ + uint32_t interval; + struct ehci_qh_hw *qh; + + uint32_t timeout = 0; + uint32_t regval; + + memset(&g_ehci_hcd, 0, sizeof(struct ehci_hcd)); + + if (sizeof(struct ehci_qh_hw) % 32) { + USB_LOG_ERR("struct ehci_qh_hw is not align 32\r\n"); + return -EINVAL; + } + if (sizeof(struct ehci_qtd_hw) % 32) { + USB_LOG_ERR("struct ehci_qtd_hw is not align 32\r\n"); + return -EINVAL; + } + + for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) { + struct ehci_pipe *pipe; + + pipe = &g_ehci_hcd.pipe_pool[index]; + pipe->waitsem = usb_osal_sem_create(0); + } + + memset(&g_async_qh_head, 0, sizeof(struct ehci_qh_hw)); + g_async_qh_head.hw.hlp = QH_HLP_QH(&g_async_qh_head); + g_async_qh_head.hw.epchar = QH_EPCHAR_H; + g_async_qh_head.hw.overlay.next_qtd = QTD_LIST_END; + g_async_qh_head.hw.overlay.alt_next_qtd = QTD_LIST_END; + g_async_qh_head.hw.overlay.token = QTD_TOKEN_STATUS_HALTED; + g_async_qh_head.first_qtd = QTD_LIST_END; + + memset(g_framelist, 0, sizeof(uint32_t) * CONFIG_USB_EHCI_FRAME_LIST_SIZE); + + for (int i = EHCI_PERIOIDIC_QH_NUM - 1; i >= 0; i--) { + memset(&g_periodic_qh_head[i], 0, sizeof(struct ehci_qh_hw)); + g_periodic_qh_head[i].hw.hlp = QH_HLP_END; + g_periodic_qh_head[i].hw.epchar = QH_EPCAPS_SSMASK(1); + g_periodic_qh_head[i].hw.overlay.next_qtd = QTD_LIST_END; + g_periodic_qh_head[i].hw.overlay.alt_next_qtd = QTD_LIST_END; + g_periodic_qh_head[i].hw.overlay.token = QTD_TOKEN_STATUS_HALTED; + g_periodic_qh_head[i].first_qtd = QTD_LIST_END; + + interval = 1 << i; + for (uint32_t j = interval - 1; j < CONFIG_USB_EHCI_FRAME_LIST_SIZE; j += interval) { + if (g_framelist[j] == 0) { + g_framelist[j] = QH_HLP_QH(&g_periodic_qh_head[i]); + } else { + qh = EHCI_ADDR2QH(g_framelist[j]); + while (1) { + if (qh == &g_periodic_qh_head[i]) { + break; + } + if (qh->hw.hlp == QH_HLP_END) { + qh->hw.hlp = QH_HLP_QH(&g_periodic_qh_head[i]); + break; + } + + qh = EHCI_ADDR2QH(qh->hw.hlp); + } + } + } + } + + usb_hc_low_level_init(); + + EHCI_HCOR->usbcmd |= EHCI_USBCMD_HCRESET; + while (EHCI_HCOR->usbcmd & EHCI_USBCMD_HCRESET) { + usb_osal_msleep(1); + timeout++; + if (timeout > 100) { + return -ETIMEDOUT; + } + } + + usb_hc_low_level2_init(); + + EHCI_HCOR->usbintr = 0; + EHCI_HCOR->usbsts = EHCI_HCOR->usbsts; +#if CONFIG_USB_EHCI_HCCR_BASE != 0 + USB_LOG_INFO("EHCI HCIVERSION:%04x\r\n", (int)EHCI_HCCR->hciversion); + USB_LOG_INFO("EHCI HCSPARAMS:%06x\r\n", (int)EHCI_HCCR->hcsparams); + USB_LOG_INFO("EHCI HCCPARAMS:%04x\r\n", (int)EHCI_HCCR->hccparams); +#endif + /* Set the Current Asynchronous List Address. */ + EHCI_HCOR->asynclistaddr = EHCI_PTR2ADDR(&g_async_qh_head); + /* Set the Periodic Frame List Base Address. */ + EHCI_HCOR->periodiclistbase = EHCI_PTR2ADDR(g_framelist); + + regval = 0; +#if CONFIG_USB_EHCI_FRAME_LIST_SIZE == 1024 + regval |= EHCI_USBCMD_FLSIZE_1024; +#elif CONFIG_USB_EHCI_FRAME_LIST_SIZE == 512 + regval |= EHCI_USBCMD_FLSIZE_512; +#elif CONFIG_USB_EHCI_FRAME_LIST_SIZE == 256 + regval |= EHCI_USBCMD_FLSIZE_256; +#else +#error Unsupported frame size list size +#endif + + regval |= EHCI_USBCMD_ITHRE_1MF; + regval |= EHCI_USBCMD_ASEN; + regval |= EHCI_USBCMD_PSEN; + regval |= EHCI_USBCMD_RUN; + EHCI_HCOR->usbcmd = regval; + +#ifdef CONFIG_USB_EHCI_CONFIGFLAG + EHCI_HCOR->configflag = EHCI_CONFIGFLAG; +#endif + /* Wait for the EHCI to run (no longer report halted) */ + timeout = 0; + while (EHCI_HCOR->usbsts & EHCI_USBSTS_HALTED) { + usb_osal_msleep(1); + timeout++; + if (timeout > 100) { + return -ETIMEDOUT; + } + } +#ifdef CONFIG_USB_EHCI_PORT_POWER + for (uint8_t port = 0; port < CONFIG_USBHOST_MAX_RHPORTS; port++) { + regval = EHCI_HCOR->portsc[port]; + regval |= EHCI_PORTSC_PP; + EHCI_HCOR->portsc[port] = regval; + } +#endif + + /* Enable EHCI interrupts. */ + EHCI_HCOR->usbintr = EHCI_USBIE_INT | EHCI_USBIE_ERR | EHCI_USBIE_PCD | EHCI_USBIE_FATAL | EHCI_USBIE_IAA; + return 0; +} + +uint16_t usbh_get_frame_number(void) +{ + return (((EHCI_HCOR->frindex & EHCI_FRINDEX_MASK) >> 3) & 0x3ff); +} + +int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) +{ + uint8_t nports; + uint8_t port; + uint32_t temp, status; + + nports = CONFIG_USBHOST_MAX_RHPORTS; + + port = setup->wIndex; + if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) { + switch (setup->bRequest) { + case HUB_REQUEST_CLEAR_FEATURE: + switch (setup->wValue) { + case HUB_FEATURE_HUB_C_LOCALPOWER: + break; + case HUB_FEATURE_HUB_C_OVERCURRENT: + break; + default: + return -EPIPE; + } + break; + case HUB_REQUEST_SET_FEATURE: + switch (setup->wValue) { + case HUB_FEATURE_HUB_C_LOCALPOWER: + break; + case HUB_FEATURE_HUB_C_OVERCURRENT: + break; + default: + return -EPIPE; + } + break; + case HUB_REQUEST_GET_DESCRIPTOR: + break; + case HUB_REQUEST_GET_STATUS: + memset(buf, 0, 4); + break; + default: + break; + } + } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) { + switch (setup->bRequest) { + case HUB_REQUEST_CLEAR_FEATURE: + if (!port || port > nports) { + return -EPIPE; + } + + switch (setup->wValue) { + case HUB_PORT_FEATURE_ENABLE: + EHCI_HCOR->portsc[port - 1] &= ~EHCI_PORTSC_PE; + break; + case HUB_PORT_FEATURE_SUSPEND: + case HUB_PORT_FEATURE_C_SUSPEND: + break; + case HUB_PORT_FEATURE_POWER: +#ifdef CONFIG_USB_EHCI_PORT_POWER + EHCI_HCOR->portsc[port - 1] &= ~EHCI_PORTSC_PP; +#endif + break; + case HUB_PORT_FEATURE_C_CONNECTION: + EHCI_HCOR->portsc[port - 1] |= EHCI_PORTSC_CSC; + break; + case HUB_PORT_FEATURE_C_ENABLE: + EHCI_HCOR->portsc[port - 1] |= EHCI_PORTSC_PEC; + break; + case HUB_PORT_FEATURE_C_OVER_CURREN: + EHCI_HCOR->portsc[port - 1] |= EHCI_PORTSC_OCC; + break; + case HUB_PORT_FEATURE_C_RESET: + break; + default: + return -EPIPE; + } + break; + case HUB_REQUEST_SET_FEATURE: + if (!port || port > nports) { + return -EPIPE; + } + + switch (setup->wValue) { + case HUB_PORT_FEATURE_SUSPEND: + break; + case HUB_PORT_FEATURE_POWER: +#ifdef CONFIG_USB_EHCI_PORT_POWER + EHCI_HCOR->portsc[port - 1] |= EHCI_PORTSC_PP; +#endif + break; + case HUB_PORT_FEATURE_RESET: + usbh_reset_port(port); + break; + + default: + return -EPIPE; + } + break; + case HUB_REQUEST_GET_STATUS: + if (!port || port > nports) { + return -EPIPE; + } + temp = EHCI_HCOR->portsc[port - 1]; + + status = 0; + if (temp & EHCI_PORTSC_CSC) { + status |= (1 << HUB_PORT_FEATURE_C_CONNECTION); + } + if (temp & EHCI_PORTSC_PEC) { + status |= (1 << HUB_PORT_FEATURE_C_ENABLE); + } + if (temp & EHCI_PORTSC_OCC) { + status |= (1 << HUB_PORT_FEATURE_C_OVER_CURREN); + } + + if (temp & EHCI_PORTSC_CCS) { + status |= (1 << HUB_PORT_FEATURE_CONNECTION); + } + if (temp & EHCI_PORTSC_PE) { + status |= (1 << HUB_PORT_FEATURE_ENABLE); + + if (usbh_get_port_speed(port) == USB_SPEED_LOW) { + status |= (1 << HUB_PORT_FEATURE_LOWSPEED); + } else if (usbh_get_port_speed(port) == USB_SPEED_HIGH) { + status |= (1 << HUB_PORT_FEATURE_HIGHSPEED); + } + } + if (temp & EHCI_PORTSC_SUSPEND) { + status |= (1 << HUB_PORT_FEATURE_SUSPEND); + } + if (temp & EHCI_PORTSC_OCA) { + status |= (1 << HUB_PORT_FEATURE_OVERCURRENT); + } + if (temp & EHCI_PORTSC_RESET) { + status |= (1 << HUB_PORT_FEATURE_RESET); + } + if (temp & EHCI_PORTSC_PP) { + status |= (1 << HUB_PORT_FEATURE_POWER); + } + memcpy(buf, &status, 4); + break; + default: + break; + } + } + return 0; +} + +int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed) +{ + struct ehci_pipe *ppipe = (struct ehci_pipe *)pipe; + + ppipe->dev_addr = dev_addr; + ppipe->ep_mps = ep_mps; + ppipe->speed = speed; + + return 0; +} + +int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg) +{ + struct ehci_pipe *ppipe; + usb_osal_sem_t waitsem; + + ppipe = ehci_pipe_alloc(); + if (ppipe == NULL) { + return -ENOMEM; + } + + /* store variables */ + waitsem = ppipe->waitsem; + + memset(ppipe, 0, sizeof(struct ehci_pipe)); + + ppipe->ep_addr = ep_cfg->ep_addr; + ppipe->ep_type = ep_cfg->ep_type; + ppipe->ep_mps = ep_cfg->ep_mps; + ppipe->ep_interval = ep_cfg->ep_interval; + ppipe->mult = ep_cfg->mult; + ppipe->speed = ep_cfg->hport->speed; + ppipe->dev_addr = ep_cfg->hport->dev_addr; + ppipe->hport = ep_cfg->hport; + + if ((ppipe->speed == USB_SPEED_HIGH) && (ppipe->ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS)) { + if (ep_cfg->ep_interval == 0x01) { /* transfer interval 1 mf */ + ppipe->mf_unmask = 0xff; + ppipe->mf_valid = 8; + } else if (ep_cfg->ep_interval == 0x02) { /* transfer interval 2 mf */ + ppipe->mf_unmask = 0x55; + ppipe->mf_valid = 4; + } else if (ep_cfg->ep_interval == 0x03) { /* transfer interval 4 mf */ + ppipe->mf_unmask = 0x44; + ppipe->mf_valid = 2; + } else if (ep_cfg->ep_interval == 0x04) { /* transfer interval 8 mf */ + ppipe->mf_unmask = 0x01; + ppipe->mf_valid = 1; + } + + ppipe->id = ehci_itd_pool_alloc(); + if (ppipe->id == -1) { + printf("fail to alloc itd pool\r\n"); + return -1; + } + } + /* restore variable */ + ppipe->inuse = true; + ppipe->waitsem = waitsem; + + *pipe = (usbh_pipe_t)ppipe; + + return 0; +} + +int usbh_pipe_free(usbh_pipe_t pipe) +{ + struct usbh_urb *urb; + size_t flags; + + struct ehci_pipe *ppipe = (struct ehci_pipe *)pipe; + + if (!ppipe) { + return -EINVAL; + } + + urb = ppipe->urb; + + if (urb) { + usbh_kill_urb(urb); + } + + flags = usb_osal_enter_critical_section(); + + ehci_pipe_free(ppipe); + usb_osal_leave_critical_section(flags); + return 0; +} + +int usbh_submit_urb(struct usbh_urb *urb) +{ + struct ehci_pipe *pipe; + struct ehci_qh_hw *qh = NULL; + size_t flags; + int ret = 0; + + if (!urb) { + return -EINVAL; + } + + pipe = urb->pipe; + + if (!pipe) { + return -EINVAL; + } + + if (!pipe->hport->connected) { + return -ENODEV; + } + + if (pipe->urb) { + return -EBUSY; + } + + flags = usb_osal_enter_critical_section(); + + pipe->waiter = false; + pipe->xfrd = 0; + pipe->qh = NULL; + pipe->urb = urb; + urb->errorcode = -EBUSY; + urb->actual_length = 0; + + if (urb->timeout > 0) { + pipe->waiter = true; + } + usb_osal_leave_critical_section(flags); + switch (pipe->ep_type) { + case USB_ENDPOINT_TYPE_CONTROL: + qh = ehci_control_pipe_init(pipe, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); + if (qh == NULL) { + return -ENOMEM; + } + break; + case USB_ENDPOINT_TYPE_BULK: + qh = ehci_bulk_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length); + if (qh == NULL) { + return -ENOMEM; + } + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + qh = ehci_intr_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length); + if (qh == NULL) { + return -ENOMEM; + } + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + ehci_iso_pipe_init(pipe, urb->iso_packet, urb->num_of_iso_packets); + break; + default: + break; + } + + if (urb->timeout > 0) { + /* wait until timeout or sem give */ + ret = usb_osal_sem_take(pipe->waitsem, urb->timeout); + if (ret < 0) { + goto errout_timeout; + } + ret = urb->errorcode; + } + return ret; +errout_timeout: + /* Timeout will run here */ + pipe->waiter = false; + usbh_kill_urb(urb); + return ret; +} + +int usbh_kill_urb(struct usbh_urb *urb) +{ + struct ehci_pipe *pipe; + struct ehci_pipe *iso_pipe; + struct ehci_qh_hw *qh = NULL; + struct ehci_itd_hw *itd; + uint32_t sched_frame; + usb_slist_t *i; + + size_t flags; + + if (!urb) { + return -EINVAL; + } + + pipe = urb->pipe; + + if (!pipe) { + return -EINVAL; + } + + flags = usb_osal_enter_critical_section(); + + EHCI_HCOR->usbcmd &= ~(EHCI_USBCMD_PSEN | EHCI_USBCMD_ASEN); + + if ((pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) || (pipe->ep_type == USB_ENDPOINT_TYPE_BULK)) { + qh = EHCI_ADDR2QH(g_async_qh_head.hw.hlp); + while (qh != &g_async_qh_head) { + if (qh == pipe->qh) { + ehci_kill_qh(&g_async_qh_head, qh); + } + qh = EHCI_ADDR2QH(qh->hw.hlp); + } + } else { + qh = EHCI_ADDR2QH(g_periodic_qh_head[EHCI_PERIOIDIC_QH_NUM - 1].hw.hlp); + while (qh) { + if (qh == pipe->qh) { + if (pipe->speed == USB_SPEED_HIGH) { + ehci_kill_qh(ehci_get_periodic_qhead(pipe->ep_interval), qh); + } else { + ehci_kill_qh(ehci_get_periodic_qhead(pipe->ep_interval * 8), qh); + } + } + qh = EHCI_ADDR2QH(qh->hw.hlp); + } + } + + // usb_slist_for_each(i, &iso_pipe_list_head) + // { + // iso_pipe = usb_slist_entry(i, struct ehci_pipe, iso_list); + + // if (pipe == iso_pipe) { + // sched_frame = urb->start_frame; + // for (uint32_t j = 0; j < pipe->used_itd_num; j++) { + // itd = &ehci_itd_pool[pipe->id][j]; + // /* remove itd from list */ + // ehci_itd_remove(itd, sched_frame); + // urb->start_frame = (sched_frame + 1) & 0x3ff; + // } + // pipe->used_itd_num = 0; + // ehci_iso_pipe_remove(pipe); + // } + // } + + EHCI_HCOR->usbcmd |= (EHCI_USBCMD_PSEN | EHCI_USBCMD_ASEN); + + pipe->qh = NULL; + pipe->urb = NULL; + + usb_osal_leave_critical_section(flags); + + return 0; +} + +static void ehci_scan_async_list(void) +{ + struct ehci_qh_hw *qh; + struct ehci_pipe *pipe; + + qh = EHCI_ADDR2QH(g_async_qh_head.hw.hlp); + while (qh != &g_async_qh_head) { + pipe = qh->pipe; + if (pipe) { + ehci_check_qh(&g_async_qh_head, qh, pipe); + } + qh = EHCI_ADDR2QH(qh->hw.hlp); + } +} + +static void ehci_scan_periodic_list(void) +{ + struct ehci_qh_hw *qh; + struct ehci_pipe *pipe; + + qh = EHCI_ADDR2QH(g_periodic_qh_head[EHCI_PERIOIDIC_QH_NUM - 1].hw.hlp); + while (qh) { + pipe = qh->pipe; + if (pipe) { + if (pipe->speed == USB_SPEED_HIGH) { + ehci_check_qh(ehci_get_periodic_qhead(pipe->ep_interval), qh, pipe); + } else { + ehci_check_qh(ehci_get_periodic_qhead(pipe->ep_interval * 8), qh, pipe); + } + } + qh = EHCI_ADDR2QH(qh->hw.hlp); + } +} + +static void ehci_scan_isochronous_list(void) +{ + +} + +void USBH_IRQHandler(void) +{ + uint32_t usbsts; + + usbsts = EHCI_HCOR->usbsts & EHCI_HCOR->usbintr; + EHCI_HCOR->usbsts = usbsts; + + if (usbsts & EHCI_USBSTS_INT) { + ehci_scan_async_list(); + ehci_scan_periodic_list(); + ehci_scan_isochronous_list(); + } + + if (usbsts & EHCI_USBSTS_ERR) { + ehci_scan_async_list(); + ehci_scan_periodic_list(); + ehci_scan_isochronous_list(); + } + + if (usbsts & EHCI_USBSTS_PCD) { + for (int port = 0; port < CONFIG_USBHOST_MAX_RHPORTS; port++) { + uint32_t portsc = EHCI_HCOR->portsc[port]; + + if (portsc & EHCI_PORTSC_CSC) { + if ((portsc & EHCI_PORTSC_CCS) == EHCI_PORTSC_CCS) { + } else { + for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) { + struct ehci_pipe *pipe = &g_ehci_hcd.pipe_pool[index]; + struct usbh_urb *urb = pipe->urb; + if (pipe->waiter) { + pipe->waiter = false; + urb->errorcode = -ESHUTDOWN; + usb_osal_sem_give(pipe->waitsem); + } + } + + for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) { + g_ehci_hcd.ehci_qh_used[index] = false; + } + for (uint8_t index = 0; index < CONFIG_USB_EHCI_QTD_NUM; index++) { + g_ehci_hcd.ehci_qtd_used[index] = false; + } + for (uint8_t index = 0; index < 2; index++) { + g_ehci_hcd.ehci_itd_pool_used[index] = false; + } + } + usbh_roothub_thread_wakeup(port + 1); + } + } + } + + if (usbsts & EHCI_USBSTS_IAA) { + } + + if (usbsts & EHCI_USBSTS_FATAL) { + } +} \ No newline at end of file