#include "usbh_core.h" #include "hpm_usb_host.h" #include "board.h" #define HCD_MAX_ENDPOINT 16 struct hpm_ehci_pipe { uint8_t ep_addr; uint8_t dev_addr; volatile int result; /* The result of the transfer */ volatile uint32_t xfrd; /* Bytes transferred (at end of transfer) */ volatile bool waiter; /* True: Thread is waiting for a channel event */ usb_osal_sem_t waitsem; /* Channel wait semaphore */ usb_osal_mutex_t exclsem; /* Support mutually exclusive access */ #ifdef CONFIG_USBHOST_ASYNCH usbh_asynch_callback_t callback; /* Transfer complete callback */ void *arg; /* Argument that accompanies the callback */ #endif }; struct hpm_ehci_hcd { struct hpm_ehci_pipe chan[HCD_MAX_ENDPOINT][2]; } g_hpm_ehci_hcd; static inline uint32_t tu_align32(uint32_t value) { return (value & 0xFFFFFFE0UL); } /*---------------------------------------------------------------------* * Enum Declaration *---------------------------------------------------------------------*/ typedef enum { hcd_int_mask_usb = HPM_BITSMASK(1, 0), hcd_int_mask_error = HPM_BITSMASK(1, 1), hcd_int_mask_port_change = HPM_BITSMASK(1, 2), hcd_int_mask_framelist_rollover = HPM_BITSMASK(1, 3), hcd_int_mask_pci_host_system_error = HPM_BITSMASK(1, 4), hcd_int_mask_async_advance = HPM_BITSMASK(1, 5), hcd_int_mask_sof = HPM_BITSMASK(1, 7), hcd_int_mask_async = HPM_BITSMASK(1, 18), hcd_int_mask_periodic = HPM_BITSMASK(1, 19), hcd_int_mask_all = hcd_int_mask_usb | hcd_int_mask_error | hcd_int_mask_port_change | hcd_int_mask_framelist_rollover | hcd_int_mask_pci_host_system_error | hcd_int_mask_async_advance | hcd_int_mask_sof | hcd_int_mask_async | hcd_int_mask_periodic } usb_interrupt_mask_t; typedef struct { USB_Type *regs; /* register base */ const uint32_t irqnum; /* IRQ number */ } hcd_controller_t; static const hcd_controller_t _hcd_controller[] = { { .regs = (USB_Type *)HPM_USB0_BASE, .irqnum = IRQn_USB0 }, #ifdef HPM_USB1_BASE { .regs = (USB_Type *)HPM_USB1_BASE, .irqnum = IRQn_USB1 } #endif }; /*---------------------------------------------------------------------* * Variable Definitions *---------------------------------------------------------------------*/ ATTR_PLACE_AT_NONCACHEABLE static usb_host_handle_t usb_host_handle; ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(USB_SOC_DCD_DATA_RAM_ADDRESS_ALIGNMENT) static hcd_data_t _hcd_data; bool hcd_init(uint8_t rhport) { uint32_t int_mask; if (rhport > USB_SOC_MAX_COUNT) { return false; } usb_host_handle.rhport = rhport; usb_host_handle.regs = _hcd_controller[rhport].regs; usb_host_handle.hcd_data = &_hcd_data; usb_host_handle.hcd_vbus_ctrl_cb = board_usb_vbus_ctrl; int_mask = hcd_int_mask_error | hcd_int_mask_port_change | hcd_int_mask_async_advance | hcd_int_mask_periodic | hcd_int_mask_async | hcd_int_mask_framelist_rollover; usb_host_init(&usb_host_handle, int_mask, USB_HOST_FRAMELIST_SIZE); intc_m_enable_irq(_hcd_controller[rhport].irqnum); return true; } /**************************************************************************** * Name: hpm_ehci_pipe_waitsetup * * Description: * Set the request for the transfer complete event well BEFORE enabling * the transfer (as soon as we are absolutely committed to the transfer). * We do this to minimize race conditions. This logic would have to be * expanded if we want to have more than one packet in flight at a time! * * Assumptions: * Called from a normal thread context BEFORE the transfer has been * started. * ****************************************************************************/ static int hpm_ehci_pipe_waitsetup(struct hpm_ehci_pipe *chan) { size_t flags; int ret = -ENODEV; flags = usb_osal_enter_critical_section(); /* Is the device still connected? */ if (usbh_get_port_connect_status(1)) { /* Yes.. then set waiter to indicate that we expect to be informed * when either (1) the device is disconnected, or (2) the transfer * completed. */ chan->waiter = true; chan->result = -EBUSY; chan->xfrd = 0; #ifdef CONFIG_USBHOST_ASYNCH chan->callback = NULL; chan->arg = NULL; #endif ret = 0; } usb_osal_leave_critical_section(flags); return ret; } /**************************************************************************** * Name: hpm_ehci_pipe_asynchsetup * * Description: * Set the request for the transfer complete event well BEFORE enabling * the transfer (as soon as we are absolutely committed to the to avoid * transfer). We do this to minimize race conditions. This logic would * have to be expanded if we want to have more than one packet in flight * at a time! * * Assumptions: * Might be called from the level of an interrupt handler * ****************************************************************************/ #ifdef CONFIG_USBHOST_ASYNCH static int hpm_ehci_pipe_asynchsetup(struct hpm_ehci_pipe *chan, usbh_asynch_callback_t callback, void *arg) { size_t flags; int ret = -ENODEV; flags = usb_osal_enter_critical_section(); /* Is the device still connected? */ if (usbh_get_port_connect_status(1)) { /* Yes.. then set waiter to indicate that we expect to be informed * when either (1) the device is disconnected, or (2) the transfer * completed. */ chan->waiter = false; chan->result = -EBUSY; chan->xfrd = 0; chan->callback = callback; chan->arg = arg; ret = 0; } usb_osal_leave_critical_section(flags); return ret; } #endif /**************************************************************************** * Name: hpm_ehci_pipe_wait * * Description: * Wait for a transfer on a channel to complete. * * Assumptions: * Called from a normal thread context * ****************************************************************************/ static int hpm_ehci_pipe_wait(struct hpm_ehci_pipe *chan, uint32_t timeout) { int ret; /* Loop, testing for an end of transfer condition. The channel 'result' * was set to EBUSY and 'waiter' was set to true before the transfer; * 'waiter' will be set to false and 'result' will be set appropriately * when the transfer is completed. */ if (chan->waiter) { ret = usb_osal_sem_take(chan->waitsem, timeout); if (ret < 0) { return ret; } } /* The transfer is complete re-enable interrupts and return the result */ ret = chan->result; if (ret < 0) { return ret; } return chan->xfrd; } /**************************************************************************** * Name: hpm_ehci_pipe_wakeup * * Description: * A channel transfer has completed... wakeup any threads waiting for the * transfer to complete. * * Assumptions: * This function is called from the transfer complete interrupt handler for * the channel. Interrupts are disabled. * ****************************************************************************/ static void hpm_ehci_pipe_wakeup(struct hpm_ehci_pipe *chan) { usbh_asynch_callback_t callback; void *arg; int nbytes; /* Is the transfer complete? */ if (chan->result != -EBUSY) { /* Is there a thread waiting for this transfer to complete? */ if (chan->waiter) { /* Wake'em up! */ chan->waiter = false; usb_osal_sem_give(chan->waitsem); } #ifdef CONFIG_USBHOST_ASYNCH /* No.. is an asynchronous callback expected when the transfer * completes? */ else if (chan->callback) { callback = chan->callback; arg = chan->arg; nbytes = chan->xfrd; chan->callback = NULL; chan->arg = NULL; if (chan->result < 0) { nbytes = chan->result; } callback(arg, nbytes); } #endif } } __WEAK void usb_hc_low_level_init(void) { } int usb_hc_sw_init(void) { memset(&g_hpm_ehci_hcd, 0, sizeof(struct hpm_ehci_hcd)); for (uint8_t chidx = 0; chidx < HCD_MAX_ENDPOINT; chidx++) { g_hpm_ehci_hcd.chan[chidx][0].exclsem = usb_osal_mutex_create(); g_hpm_ehci_hcd.chan[chidx][0].waitsem = usb_osal_sem_create(0); g_hpm_ehci_hcd.chan[chidx][1].exclsem = usb_osal_mutex_create(); g_hpm_ehci_hcd.chan[chidx][1].waitsem = usb_osal_sem_create(0); } return 0; } int usb_hc_hw_init(void) { usb_hc_low_level_init(); hcd_init(0); return 0; } int usbh_reset_port(const uint8_t port) { usb_host_port_reset(&usb_host_handle); return 0; } bool usbh_get_port_connect_status(const uint8_t port) { return usb_host_get_port_ccs(&usb_host_handle); } uint8_t usbh_get_port_speed(const uint8_t port) { uint8_t speed = usb_host_get_port_speed(&usb_host_handle); if (speed == 0) { speed = USB_SPEED_FULL; } else if (speed == 1) { speed = USB_SPEED_LOW; } else if (speed == 2) { speed = USB_SPEED_HIGH; } return speed; } int usbh_ep0_reconfigure(usbh_epinfo_t ep, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed) { struct hpm_ehci_pipe *chan; int ret; usb_desc_endpoint_t ep_desc; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } if (speed == 1) { usb_host_handle.ep_speed = 1; } else if (speed == 2) { usb_host_handle.ep_speed = 0; } else if (speed == 3) { usb_host_handle.ep_speed = 2; } usb_host_handle.hub_addr = 0; usb_host_handle.hub_port = 0; ep_desc.bEndpointAddress = 0x00; ep_desc.bmAttributes.xfer = 0x00; ep_desc.wMaxPacketSize.size = ep_mps; chan->dev_addr = dev_addr; usb_host_edpt_open(&usb_host_handle, dev_addr, &ep_desc); usb_osal_mutex_give(chan->exclsem); return ret; } int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg) { struct hpm_ehci_pipe *chan; struct usbh_hubport *hport; uint8_t speed; usb_osal_sem_t waitsem; usb_osal_mutex_t exclsem; usb_desc_endpoint_t ep_desc; hport = ep_cfg->hport; if (ep_desc.bmAttributes.xfer == 0) { chan = &g_hpm_ehci_hcd.chan[0][0]; } else { if (ep_cfg->ep_addr & 0x80) { chan = &g_hpm_ehci_hcd.chan[ep_cfg->ep_addr & 0x7f][1]; } else { chan = &g_hpm_ehci_hcd.chan[ep_cfg->ep_addr & 0x7f][0]; } } waitsem = chan->waitsem; exclsem = chan->exclsem; memset(chan, 0, sizeof(struct hpm_ehci_pipe)); if (hport->speed == 1) { usb_host_handle.ep_speed = 1; } else if (hport->speed == 2) { usb_host_handle.ep_speed = 0; } else if (hport->speed == 3) { usb_host_handle.ep_speed = 2; } usb_host_handle.hub_addr = 0; usb_host_handle.hub_port = 0; ep_desc.bEndpointAddress = ep_cfg->ep_addr; ep_desc.bmAttributes.xfer = ep_cfg->ep_type; ep_desc.wMaxPacketSize.size = ep_cfg->ep_mps; chan->ep_addr = ep_cfg->ep_addr; chan->dev_addr = hport->dev_addr; usb_host_edpt_open(&usb_host_handle, hport->dev_addr, &ep_desc); /* restore variable */ chan->waitsem = waitsem; chan->exclsem = exclsem; *ep = (usbh_epinfo_t)chan; return 0; } int usbh_ep_free(usbh_epinfo_t ep) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } usb_osal_mutex_give(chan->exclsem); return 0; } int usbh_control_transfer(usbh_epinfo_t ep, struct usb_setup_packet *setup, uint8_t *buffer) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } ret = hpm_ehci_pipe_waitsetup(chan); if (ret < 0) { goto error_out; } usb_host_setup_send(&usb_host_handle, chan->dev_addr, (uint8_t *)setup); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } if (setup->wLength && buffer) { if (setup->bmRequestType & 0x80) { chan->waiter = true; chan->result = -EBUSY; usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, 0x80, buffer, setup->wLength); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } chan->waiter = true; chan->result = -EBUSY; usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, 0x00, NULL, 0); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } } else { chan->waiter = true; chan->result = -EBUSY; usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, 0x00, buffer, setup->wLength); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } chan->waiter = true; chan->result = -EBUSY; usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, 0x00, NULL, 0); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } } } else { chan->waiter = true; chan->result = -EBUSY; usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, 0x80, NULL, 0); ret = hpm_ehci_pipe_wait(chan, CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT); if (ret < 0) { goto error_out; } } usb_osal_mutex_give(chan->exclsem); return ret; error_out: chan->waiter = false; usb_osal_mutex_give(chan->exclsem); return ret; } int usbh_ep_bulk_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, uint32_t timeout) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } ret = hpm_ehci_pipe_waitsetup(chan); if (ret < 0) { goto error_out; } usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, chan->ep_addr, buffer, buflen); ret = hpm_ehci_pipe_wait(chan, timeout); if (ret < 0) { goto error_out; } usb_osal_mutex_give(chan->exclsem); return ret; error_out: chan->waiter = false; usb_osal_mutex_give(chan->exclsem); return ret; } int usbh_ep_intr_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, uint32_t timeout) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } ret = hpm_ehci_pipe_waitsetup(chan); if (ret < 0) { goto error_out; } usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, chan->ep_addr, buffer, buflen); ret = hpm_ehci_pipe_wait(chan, timeout); if (ret < 0) { goto error_out; } usb_osal_mutex_give(chan->exclsem); return ret; error_out: chan->waiter = false; usb_osal_mutex_give(chan->exclsem); return ret; } int usbh_ep_bulk_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, usbh_asynch_callback_t callback, void *arg) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } ret = hpm_ehci_pipe_asynchsetup(chan, callback, arg); if (ret < 0) { goto error_out; } usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, chan->ep_addr, buffer, buflen); error_out: usb_osal_mutex_give(chan->exclsem); return ret; } int usbh_ep_intr_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, usbh_asynch_callback_t callback, void *arg) { struct hpm_ehci_pipe *chan; int ret; chan = (struct hpm_ehci_pipe *)ep; ret = usb_osal_mutex_take(chan->exclsem); if (ret < 0) { return ret; } ret = hpm_ehci_pipe_asynchsetup(chan, callback, arg); if (ret < 0) { goto error_out; } usb_host_edpt_xfer(&usb_host_handle, chan->dev_addr, chan->ep_addr, buffer, buflen); error_out: usb_osal_mutex_give(chan->exclsem); return ret; } int usb_ep_cancel(usbh_epinfo_t ep) { return 0; } /*---------------------------------------------------------------------* * HCD Interrupt Handler *---------------------------------------------------------------------*/ /* 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(usb_host_handle_t *handle) { hcd_qhd_t *qhd_pool = handle->hcd_data->qhd_pool; for (uint32_t i = 0; i < HCD_MAX_ENDPOINT; i++) { if (qhd_pool[i].removing) { qhd_pool[i].removing = 0; qhd_pool[i].used = 0; } } } static void port_connect_status_change_isr(usb_host_handle_t *handle) { struct hpm_ehci_pipe *chan; /* NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device */ if (usb_host_get_port_ccs(handle)) { usbh_event_notify_handler(USBH_EVENT_CONNECTED, 1); } else { /* device unplugged */ for (uint8_t chidx = 0; chidx < HCD_MAX_ENDPOINT; chidx++) { for (uint8_t j = 0; j < 2; j++) { chan = &g_hpm_ehci_hcd.chan[chidx][j]; if (chan->waiter) { chan->result = -ENXIO; hpm_ehci_pipe_wakeup(chan); } } } usb_host_device_close(&usb_host_handle, 1); usbh_event_notify_handler(USBH_EVENT_DISCONNECTED, 1); } } static void qhd_xfer_complete_isr(hcd_qhd_t *p_qhd) { struct hpm_ehci_pipe *chan; bool is_ioc; while (p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) { /* TD need to be freed and removed from qhd, before invoking callback */ is_ioc = (p_qhd->p_qtd_list_head->int_on_complete != 0); p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; p_qhd->p_qtd_list_head->used = 0; /* free QTD */ usb_host_qtd_remove_1st_from_qhd(p_qhd); if (is_ioc) { if (p_qhd->ep_number == 0) { chan = &g_hpm_ehci_hcd.chan[0][0]; } else { if (p_qhd->qtd_overlay.pid == usb_pid_in) { chan = &g_hpm_ehci_hcd.chan[p_qhd->ep_number][1]; } else { chan = &g_hpm_ehci_hcd.chan[p_qhd->ep_number][0]; } } chan->xfrd += p_qhd->total_xferred_bytes; chan->result = 0; p_qhd->total_xferred_bytes = 0; hpm_ehci_pipe_wakeup(chan); } } } static void async_list_xfer_complete_isr(hcd_qhd_t *const async_head) { hcd_qhd_t *p_qhd = async_head; do { if (!p_qhd->qtd_overlay.halted) { /* halted or error is processed in error isr */ qhd_xfer_complete_isr(p_qhd); } p_qhd = usb_host_qhd_next(p_qhd); p_qhd = (hcd_qhd_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p_qhd); } while (p_qhd != async_head); /* async list traversal, stop if loop around */ } static void period_list_xfer_complete_isr(usb_host_handle_t *handle, uint8_t interval_ms) { uint16_t max_loop = 0; uint32_t const period_1ms_addr = (uint32_t)usb_host_get_period_head(handle, 1); hcd_link_t next_item = *usb_host_get_period_head(handle, interval_ms); hcd_qhd_t *p_qhd_int; /* TODO abstract max loop guard for period */ while (!next_item.terminate && !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) && max_loop < (HCD_MAX_ENDPOINT + usb_max_itd + usb_max_sitd) * 1) { switch (next_item.type) { case usb_qtype_qhd: p_qhd_int = (hcd_qhd_t *)tu_align32(next_item.address); if (!p_qhd_int->qtd_overlay.halted) { qhd_xfer_complete_isr(p_qhd_int); } break; case usb_qtype_itd: case usb_qtype_sitd: case usb_qtype_fstn: default: break; } next_item = *usb_host_list_next(&next_item); max_loop++; } } static void qhd_xfer_error_isr(usb_host_handle_t *handle, hcd_qhd_t *p_qhd) { struct hpm_ehci_pipe *chan; hcd_qtd_t *p_setup; if ((p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || /* addr0 cannot be protocol STALL */ usb_host_qhd_has_xact_error(p_qhd)) { /* no error bits are set, endpoint is halted due to STALL */ p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; if (p_qhd->ep_number == 0) { chan = &g_hpm_ehci_hcd.chan[0][0]; } else { if (p_qhd->qtd_overlay.pid == usb_pid_in) { chan = &g_hpm_ehci_hcd.chan[p_qhd->ep_number][1]; } else { chan = &g_hpm_ehci_hcd.chan[p_qhd->ep_number][0]; } } if (p_qhd->qtd_overlay.babble_err) { chan->result = -EPERM; } if (p_qhd->qtd_overlay.xact_err) { chan->result = -EIO; } if (p_qhd->qtd_overlay.buffer_err) { chan->result = -EIO; } /* TODO skip unplugged device */ p_qhd->p_qtd_list_head->used = 0; /* free QTD */ usb_host_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; p_qhd->qtd_overlay.next.terminate = 1; p_qhd->qtd_overlay.alternate.terminate = 1; p_qhd->qtd_overlay.halted = 0; p_setup = usb_host_qtd_control(handle, p_qhd->dev_addr); p_setup->used = 0; } p_qhd->total_xferred_bytes = 0; hpm_ehci_pipe_wakeup(chan); } } static void xfer_error_isr(usb_host_handle_t *handle) { hcd_qhd_t *const async_head = usb_host_qhd_async_head(handle); hcd_qhd_t *p_qhd = async_head; hcd_qhd_t *p_qhd_int; hcd_link_t next_item, *p; /*------------- async list -------------*/ do { qhd_xfer_error_isr(handle, p_qhd); p_qhd = usb_host_qhd_next(p_qhd); p_qhd = (hcd_qhd_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p_qhd); } while (p_qhd != async_head); /* async list traversal, stop if loop around */ /*------------- TODO refractor period list -------------*/ uint32_t const period_1ms_addr = (uint32_t)usb_host_get_period_head(handle, 1); for (uint8_t interval_ms = 1; interval_ms <= USB_HOST_FRAMELIST_SIZE; interval_ms *= 2) { next_item = *usb_host_get_period_head(handle, interval_ms); /* TODO abstract max loop guard for period */ while (!next_item.terminate && !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address))) { switch (next_item.type) { case usb_qtype_qhd: p_qhd_int = (hcd_qhd_t *)tu_align32(next_item.address); qhd_xfer_error_isr(handle, p_qhd_int); break; case usb_qtype_itd: case usb_qtype_sitd: case usb_qtype_fstn: default: break; } p = usb_host_list_next(&next_item); p = (hcd_link_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p); next_item = *p; } } } /*------------- Host Controller Driver's Interrupt Handler -------------*/ void hcd_int_handler(uint8_t rhport) { uint32_t status; usb_host_handle_t *handle = &usb_host_handle; /* Acknowledge handled interrupt */ status = usb_host_status_flags(handle); status &= usb_host_interrupts(handle); usb_host_clear_status_flags(handle, status); if (status == 0) { return; } if (status & hcd_int_mask_framelist_rollover) { handle->hcd_data->uframe_number += (USB_HOST_FRAMELIST_SIZE << 3); } if (status & hcd_int_mask_port_change) { if (usb_host_port_csc(handle)) { port_connect_status_change_isr(handle); } } if (status & hcd_int_mask_error) { xfer_error_isr(handle); } /*------------- some QTD/SITD/ITD with IOC set is completed -------------*/ if (status & hcd_int_mask_async) { async_list_xfer_complete_isr(usb_host_qhd_async_head(handle)); } if (status & hcd_int_mask_periodic) { for (uint8_t i = 1; i <= USB_HOST_FRAMELIST_SIZE; i *= 2) { period_list_xfer_complete_isr(handle, i); } } /*------------- There is some removed async previously -------------*/ if (status & hcd_int_mask_async_advance) { /* need to place after EHCI_INT_MASK_ASYNC */ async_advance_isr(handle); } } void isr_usb0(void) { hcd_int_handler(0); } SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usb0) #ifdef HPM_USB1_BASE void isr_usb1(void) { hcd_int_handler(1); } SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usb1) #endif