diff --git a/port/musb/usb_hc_musb.c b/port/musb/usb_hc_musb.c new file mode 100644 index 00000000..977f3380 --- /dev/null +++ b/port/musb/usb_hc_musb.c @@ -0,0 +1,1051 @@ +#include "usbh_core.h" +#include "usb_musb_reg.h" + +#ifdef USB_MUSB_SUNXI + +#define SUNXI_SRAMC_BASE 0x01c00000 +#define SUNXI_USB0_BASE 0x01c13000 + +#define USBC_REG_o_PHYCTL 0x0404 + +#ifndef USB_BASE +#define USB_BASE (SUNXI_USB0_BASE) +#endif + +#ifndef USBH_IRQHandler +#define USBH_IRQHandler USB_INT_Handler //use actual usb irq name instead +void USBH_IRQHandler(int, void *); +#endif + +#define MUSB_IND_TXMAP_OFFSET 0x80 +#define MUSB_IND_TXCSRL_OFFSET 0x82 +#define MUSB_IND_TXCSRH_OFFSET 0x83 +#define MUSB_IND_RXMAP_OFFSET 0x84 +#define MUSB_IND_RXCSRL_OFFSET 0x86 +#define MUSB_IND_RXCSRH_OFFSET 0x87 +#define MUSB_IND_RXCOUNT_OFFSET 0x88 +#define MUSB_IND_TXTYPE_OFFSET 0x8C +#define MUSB_IND_TXINTERVAL_OFFSET 0x8D +#define MUSB_IND_RXTYPE_OFFSET 0x8E +#define MUSB_IND_RXINTERVAL_OFFSET 0x8F +#define MUSB_FIFO_OFFSET 0x00 +#else +#ifndef USBH_IRQHandler +#define USBH_IRQHandler USB_INT_Handler +#endif + +#ifndef USB_BASE +#define USB_BASE (0x40086400UL) +#endif + +#define MUSB_IND_TXMAP_OFFSET 0x10 +#define MUSB_IND_TXCSRL_OFFSET 0x12 +#define MUSB_IND_TXCSRH_OFFSET 0x13 +#define MUSB_IND_RXMAP_OFFSET 0x14 +#define MUSB_IND_RXCSRL_OFFSET 0x16 +#define MUSB_IND_RXCSRH_OFFSET 0x17 +#define MUSB_IND_RXCOUNT_OFFSET 0x18 +#define MUSB_IND_TXTYPE_OFFSET 0x1A +#define MUSB_IND_TXINTERVAL_OFFSET 0x1B +#define MUSB_IND_RXTYPE_OFFSET 0x1C +#define MUSB_IND_RXINTERVAL_OFFSET 0x1D +#define MUSB_FIFO_OFFSET 0x20 +#endif + +#ifndef CONFIG_USBHOST_CHANNELS +#define CONFIG_USBHOST_CHANNELS 8 +#endif + +#define USB ((USB0_Type *)USB_BASE) + +#define HWREG(x) \ + (*((volatile uint32_t *)(x))) +#define HWREGH(x) \ + (*((volatile uint16_t *)(x))) +#define HWREGB(x) \ + (*((volatile uint8_t *)(x))) + +#define USB_TXMAPx_BASE (USB_BASE + MUSB_IND_TXMAP_OFFSET) +#define USB_RXMAPx_BASE (USB_BASE + MUSB_IND_RXMAP_OFFSET) +#define USB_TXCSRLx_BASE (USB_BASE + MUSB_IND_TXCSRL_OFFSET) +#define USB_RXCSRLx_BASE (USB_BASE + MUSB_IND_RXCSRL_OFFSET) +#define USB_TXCSRHx_BASE (USB_BASE + MUSB_IND_TXCSRH_OFFSET) +#define USB_RXCSRHx_BASE (USB_BASE + MUSB_IND_RXCSRH_OFFSET) +#define USB_RXCOUNTx_BASE (USB_BASE + MUSB_IND_RXCOUNT_OFFSET) +#define USB_FIFO_BASE(ep_idx) (USB_BASE + MUSB_FIFO_OFFSET + 0x4 * ep_idx) +#define USB_TXTYPEx_BASE (USB_BASE + MUSB_IND_TXTYPE_OFFSET) +#define USB_RXTYPEx_BASE (USB_BASE + MUSB_IND_RXTYPE_OFFSET) +#define USB_TXINTERVALx_BASE (USB_BASE + MUSB_IND_TXINTERVAL_OFFSET) +#define USB_RXINTERVALx_BASE (USB_BASE + MUSB_IND_RXINTERVAL_OFFSET) + +#define USB_TXADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx) +#define USB_TXHUBADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 2) +#define USB_TXHUBPORT_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 3) +#define USB_RXADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 4) +#define USB_RXHUBADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 6) +#define USB_RXHUBPORT_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 7) + +typedef enum { + USB_EP0_STATE_SETUP = 0x0, /**< SETUP DATA */ + USB_EP0_STATE_IN_DATA, /**< IN DATA */ + USB_EP0_STATE_IN_STATUS, /**< IN status*/ + USB_EP0_STATE_OUT_DATA, /**< OUT DATA */ + USB_EP0_STATE_OUT_STATUS, /**< OUT status */ + USB_EP0_STATE_IN_DATA_C, /**< IN status*/ + USB_EP0_STATE_IN_STATUS_C, /**< IN DATA */ +} ep0_state_t; + +struct usb_musb_chan { + usb_osal_sem_t waitsem; /* Channel wait semaphore */ + volatile bool waiter; /* True: Thread is waiting for a channel event */ + volatile int result; /* The result of the transfer */ + bool inuse; /* True: This channel is "in use" */ + uint16_t mps; + uint8_t interval; + uint8_t hubaddr; + uint8_t hubport; + uint8_t *buffer; + uint32_t buflen; + bool in; /* True: IN endpoint */ + volatile uint16_t xfrd; /* Bytes transferred (at end of transfer) */ +#ifdef CONFIG_USBHOST_ASYNCH + usbh_asynch_callback_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +#endif +}; + +struct usb_musb_priv { + volatile bool connected; /* Connected to device */ + volatile bool pscwait; /* True: Thread is waiting for a port event */ + usb_osal_sem_t exclsem; /* Support mutually exclusive access */ + volatile uint32_t fifo_size_offset; + struct usb_musb_chan chan[CONFIG_USBHOST_CHANNELS]; +} g_usbhost; + +volatile uint8_t usb_ep0_state = USB_EP0_STATE_SETUP; + +/* get current active ep */ +static uint8_t USBC_GetActiveEp(void) +{ + return USB->EPIDX; +} + +/* set the active ep */ +static void USBC_SelectActiveEp(uint8_t ep_index) +{ + USB->EPIDX = ep_index; +} + +static void usb_musb_fifo_flush(uint8_t ep) +{ + uint8_t ep_idx = ep & 0x7f; + if (ep_idx == 0) { + if ((HWREGB(USB_TXCSRLx_BASE) & (USB_CSRL0_RXRDY | USB_CSRL0_TXRDY)) != 0) + HWREGB(USB_RXCSRLx_BASE) |= USB_CSRH0_FLUSH; + } else { + if (ep & 0x80) { + if (HWREGB(USB_TXCSRLx_BASE) & USB_TXCSRL1_TXRDY) + HWREGB(USB_TXCSRLx_BASE) |= USB_TXCSRL1_FLUSH; + } else { + if (HWREGB(USB_RXCSRLx_BASE) & USB_RXCSRL1_RXRDY) + HWREGB(USB_RXCSRLx_BASE) |= USB_RXCSRL1_FLUSH; + } + } +} + +void usb_musb_ep_status_clear(uint32_t ep_idx, uint32_t flags) +{ + if (ep_idx == USB_EP_0) { + HWREGB(USB_TXCSRLx_BASE) &= ~flags; + } else { + HWREGB(USB_TXCSRLx_BASE) &= ~flags; + HWREGB(USB_RXCSRLx_BASE) &= ~flags; + } +} + +static void usb_musb_write_packet(uint8_t ep_idx, uint8_t *buffer, uint16_t len) +{ + uint32_t *buf32; + uint8_t *buf8; + uint32_t count32; + uint32_t count8; + int i; + + if ((uint32_t)buffer & 0x03) { + buf8 = buffer; + for (i = 0; i < len; i++) { + HWREGB(USB_FIFO_BASE(ep_idx)) = *buf8++; + } + } else { + count32 = len >> 2; + count8 = len & 0x03; + + buf32 = (uint32_t *)buffer; + + while (count32--) { + HWREG(USB_FIFO_BASE(ep_idx)) = *buf32++; + } + + buf8 = (uint8_t *)buf32; + + while (count8--) { + HWREGB(USB_FIFO_BASE(ep_idx)) = *buf8++; + } + } +} + +static void usb_musb_read_packet(uint8_t ep_idx, uint8_t *buffer, uint16_t len) +{ + uint32_t *buf32; + uint8_t *buf8; + uint32_t count32; + uint32_t count8; + int i; + + if ((uint32_t)buffer & 0x03) { + buf8 = buffer; + for (i = 0; i < len; i++) { + *buf8++ = HWREGB(USB_FIFO_BASE(ep_idx)); + } + } else { + count32 = len >> 2; + count8 = len & 0x03; + + buf32 = (uint32_t *)buffer; + + while (count32--) { + *buf32++ = HWREG(USB_FIFO_BASE(ep_idx)); + } + + buf8 = (uint8_t *)buf32; + + while (count8--) { + *buf8++ = HWREGB(USB_FIFO_BASE(ep_idx)); + } + } +} + +static uint32_t usb_musb_get_fifo_size(uint16_t mps, uint16_t *used) +{ + uint32_t size; + + for (uint8_t i = USB_TXFIFOSZ_SIZE_8; i <= USB_TXFIFOSZ_SIZE_2048; i++) { + size = (8 << i); + if (mps <= size) { + *used = size; + return i; + } + } + + *used = 0; + return USB_TXFIFOSZ_SIZE_8; +} + +/**************************************************************************** + * Name: usb_synopsys_chan_alloc + * + * Description: + * Allocate a channel. + * + ****************************************************************************/ + +static int usb_musb_chan_alloc(struct usb_musb_priv *priv) +{ + int chidx; + + /* Search the table of channels */ + + for (chidx = 2; chidx < CONFIG_USBHOST_CHANNELS; chidx++) { + /* Is this channel available? */ + if (!priv->chan[chidx].inuse) { + /* Yes... make it "in use" and return the index */ + + priv->chan[chidx].inuse = true; + return chidx; + } + } + + /* All of the channels are "in-use" */ + + return -EBUSY; +} + +/**************************************************************************** + * Name: usb_synopsys_chan_free + * + * Description: + * Free a previoiusly allocated channel. + * + ****************************************************************************/ + +static void usb_musb_chan_free(struct usb_musb_priv *priv, int chidx) +{ + /* Mark the channel available */ + priv->chan[chidx].inuse = false; +} + +/**************************************************************************** + * Name: usb_synopsys_chan_freeall + * + * Description: + * Free all channels. + * + ****************************************************************************/ + +static inline void usb_musb_chan_freeall(struct usb_musb_priv *priv) +{ + uint8_t chidx; + + /* Free all host channels */ + + for (chidx = 2; chidx < CONFIG_USBHOST_CHANNELS; chidx++) { + usb_musb_chan_free(priv, chidx); + } +} + +/**************************************************************************** + * Name: usb_musb_chan_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 usb_musb_chan_waitsetup(struct usb_musb_priv *priv, + struct usb_musb_chan *chan) +{ + uint32_t flags; + int ret = -ENODEV; + + flags = usb_osal_enter_critical_section(); + + /* Is the device still connected? */ + + if (priv->connected) { + /* 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: usb_musb_chan_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 usb_musb_chan_asynchsetup(struct usb_musb_priv *priv, + struct usb_musb_chan *chan, + usbh_asynch_callback_t callback, void *arg) +{ + uint32_t flags; + int ret = -ENODEV; + + flags = usb_osal_enter_critical_section(); + /* Is the device still connected? */ + + if (priv->connected) { + /* 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: usb_musb_chan_wait + * + * Description: + * Wait for a transfer on a channel to complete. + * + * Assumptions: + * Called from a normal thread context + * + ****************************************************************************/ + +static int usb_musb_chan_wait(struct usb_musb_priv *priv, struct usb_musb_chan *chan) +{ + 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); + if (ret < 0) { + return ret; + } + } + + /* The transfer is complete re-enable interrupts and return the result */ + ret = chan->result; + return ret; +} + +/**************************************************************************** + * Name: usb_musb_chan_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 usb_musb_chan_wakeup(struct usb_musb_priv *priv, struct usb_musb_chan *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) { + /* Handle continuation of IN/OUT pipes */ + if (chan->in) { + 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); + + } else { + 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_init(void) +{ + g_usbhost.connected = 0; + g_usbhost.pscwait = 0; + g_usbhost.fifo_size_offset = 0; + + usb_hc_low_level_init(); + + g_usbhost.exclsem = usb_osal_mutex_create(); + + USB->IE = USB_IE_RESET | USB_IE_CONN | USB_IE_DISCON | + USB_IE_RESUME | USB_IE_SUSPND | + USB_IE_BABBLE | USB_IE_SESREQ | USB_IE_VBUSERR; + + USB->TXIE = USB_TXIE_EP0; + USB->RXIE = 0; + + USB->DEVCTL |= USB_DEVCTL_SESSION; + return 0; +} + +int usbh_reset_port(const uint8_t port) +{ + USB->POWER |= USB_POWER_RESET; + usb_osal_msleep(20); + USB->POWER &= ~(USB_POWER_RESET); + usb_osal_msleep(20); + return 0; +} + +uint8_t usbh_get_port_speed(const uint8_t port) +{ + uint8_t speed; + + if (USB->POWER & USB_POWER_HSMODE) + speed = USB_SPEED_HIGH; + else if (USB->DEVCTL & USB_DEVCTL_FSDEV) + speed = USB_SPEED_FULL; + else if (USB->DEVCTL & USB_DEVCTL_LSDEV) + speed = USB_SPEED_LOW; + + return speed; +} + +int usbh_ep0_reconfigure(usbh_epinfo_t ep, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed) +{ + int ret; + uint8_t ep0 = (uint8_t)ep; + struct usb_musb_chan *chan; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + USBC_SelectActiveEp(ep0); + HWREGB(USB_TXADDR_BASE(ep0)) = dev_addr; + + if (speed == USB_SPEED_HIGH) { + USB->TYPE0 = USB_TYPE0_SPEED_HIGH; + } else if (speed == USB_SPEED_FULL) { + USB->TYPE0 = USB_TYPE0_SPEED_FULL; + } else if (speed == USB_SPEED_LOW) { + USB->TYPE0 = USB_TYPE0_SPEED_LOW; + } + + chan = &g_usbhost.chan[ep0]; + chan->mps = ep_mps; + + usb_osal_mutex_give(g_usbhost.exclsem); + return ret; +} + +int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg) +{ + struct usbh_hubport *hport; + struct usb_musb_chan *chan; + uint32_t chidx; + uint16_t used = 0; + uint8_t fifo_size; + + hport = ep_cfg->hport; + + uint32_t old_index = USBC_GetActiveEp(); + + if (ep_cfg->ep_type == USB_ENDPOINT_TYPE_CONTROL) { + if (hport->parent == NULL) { + chan = &g_usbhost.chan[0]; + memset(chan, 0, sizeof(struct usb_musb_chan)); + + chan->waitsem = usb_osal_sem_create(0); + + USBC_SelectActiveEp(0); + + HWREGB(USB_TXINTERVALx_BASE) = 0; + HWREGB(USB_TXHUBADDR_BASE(0)) = 0; + HWREGB(USB_TXHUBPORT_BASE(0)) = 0; + + *ep = (usbh_epinfo_t)0; + } else { + } + } else { + chidx = usb_musb_chan_alloc(&g_usbhost); + + chan = &g_usbhost.chan[chidx]; + memset(chan, 0, sizeof(struct usb_musb_chan)); + chan->inuse = true; + chan->interval = ep_cfg->ep_interval; + chan->mps = ep_cfg->ep_mps; + + chan->waitsem = usb_osal_sem_create(0); + + USBC_SelectActiveEp(chidx); + + uint32_t temp = ep_cfg->ep_addr & 0x7f; + + if (hport->speed == USB_SPEED_HIGH) { + temp |= USB_TXTYPE1_SPEED_HIGH; + } else if (hport->speed == USB_SPEED_FULL) { + temp |= USB_TXTYPE1_SPEED_FULL; + } else if (hport->speed == USB_SPEED_LOW) { + temp |= USB_TXTYPE1_SPEED_LOW; + } + + switch (ep_cfg->ep_type) { + case 0x00: + temp |= USB_TXTYPE1_PROTO_CTRL; + break; + case 0x01: + temp |= USB_TXTYPE1_PROTO_ISOC; + break; + case 0x02: + temp |= USB_TXTYPE1_PROTO_BULK; + break; + case 0x03: + temp |= USB_TXTYPE1_PROTO_INT; + break; + } + + if (ep_cfg->ep_addr & 0x80) { + chan->in = 1; + + HWREGB(USB_RXADDR_BASE(chidx)) = hport->dev_addr; + HWREGB(USB_RXHUBADDR_BASE(chidx)) = 0; + HWREGB(USB_RXHUBPORT_BASE(chidx)) = 0; + + HWREGB(USB_RXTYPEx_BASE) = temp; + HWREGH(USB_RXMAPx_BASE) = ep_cfg->ep_mps; + HWREGB(USB_RXINTERVALx_BASE) = ep_cfg->ep_interval; + + HWREGB(USB_RXCSRHx_BASE) = 0; + HWREGB(USB_RXCSRLx_BASE) |= USB_RXCSRL1_CLRDT; + + usb_musb_ep_status_clear(chidx, USB_HOST_IN_STATUS); + + fifo_size = usb_musb_get_fifo_size(ep_cfg->ep_mps, &used); + + USB->RXFIFOADD = g_usbhost.fifo_size_offset >> 3; + USB->RXFIFOSZ = fifo_size & 0x0f; + + g_usbhost.fifo_size_offset += used; + + USB->RXIE |= (1 << chidx); + } else { + chan->in = 0; + HWREGB(USB_TXADDR_BASE(chidx)) = hport->dev_addr; + HWREGB(USB_TXHUBADDR_BASE(chidx)) = 0; + HWREGB(USB_TXHUBPORT_BASE(chidx)) = 0; + + HWREGB(USB_TXTYPEx_BASE) = temp; + HWREGH(USB_TXMAPx_BASE) = ep_cfg->ep_mps; + HWREGB(USB_TXINTERVALx_BASE) = ep_cfg->ep_interval; + + HWREGB(USB_TXCSRHx_BASE) = 0; + usb_musb_ep_status_clear(chidx, USB_HOST_OUT_STATUS); + HWREGB(USB_TXCSRLx_BASE) |= USB_TXCSRL1_CLRDT; + + fifo_size = usb_musb_get_fifo_size(ep_cfg->ep_mps, &used); + + USB->TXFIFOADD = g_usbhost.fifo_size_offset >> 3; + USB->TXFIFOSZ = fifo_size & 0x0f; + + g_usbhost.fifo_size_offset += used; + + USB->TXIE |= (1 << chidx); + } + + *ep = (usbh_epinfo_t)chidx; + } + + USBC_SelectActiveEp(old_index); + return 0; +} + +int usbh_ep_free(usbh_epinfo_t ep) +{ + usb_musb_chan_free(&g_usbhost, (int)ep); + usb_osal_sem_delete(g_usbhost.chan[(int)ep].waitsem); + return 0; +} + +int usbh_control_transfer(usbh_epinfo_t ep, struct usb_setup_packet *setup, uint8_t *buffer) +{ + int ret; + struct usb_musb_chan *chan; + uint8_t ep0 = (uint8_t)ep; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + chan = &g_usbhost.chan[0]; + + usb_musb_chan_waitsetup(&g_usbhost, chan); + + usb_musb_write_packet(0, (uint8_t *)setup, 8); + + if (setup->wLength && buffer) { + if (setup->bmRequestType & 0x80) { + usb_ep0_state = USB_EP0_STATE_IN_DATA; + } else { + usb_ep0_state = USB_EP0_STATE_OUT_DATA; + } + chan->buffer = buffer; + chan->buflen = setup->wLength; + } else { + usb_ep0_state = USB_EP0_STATE_IN_STATUS; + } + + USBC_SelectActiveEp(0); + USB->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_SETUP; + + ret = usb_musb_chan_wait(&g_usbhost, chan); + + if (ret < 0) { + goto errout_with_mutex; + } + + usb_osal_mutex_give(g_usbhost.exclsem); + return 0; +errout_with_mutex: + usb_osal_mutex_give(g_usbhost.exclsem); + return ret; +} + +int usbh_ep_bulk_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen) +{ + int ret; + struct usb_musb_chan *chan; + uint8_t chidx = (uint8_t)ep; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + chan = &g_usbhost.chan[chidx]; + usb_musb_chan_waitsetup(&g_usbhost, chan); + + if (chan->in) { + chan->buffer = buffer; + chan->buflen = buflen; + USBC_SelectActiveEp(chidx); + HWREGB(USB_RXCSRLx_BASE) = USB_RXCSRL1_REQPKT; + } else { + chan->buffer = buffer; + chan->buflen = buflen; + if (buflen > chan->mps) { + buflen = chan->mps; + } + + usb_musb_write_packet(chidx, (uint8_t *)buffer, buflen); + USBC_SelectActiveEp(chidx); + HWREGB(USB_TXCSRLx_BASE) = USB_TXCSRL1_TXRDY; + chan->buffer += buflen; + chan->buflen -= buflen; + } + ret = usb_musb_chan_wait(&g_usbhost, chan); + if (ret < 0) { + goto errout_with_mutex; + } + usb_osal_mutex_give(g_usbhost.exclsem); + + return buflen; +errout_with_mutex: + usb_osal_mutex_give(g_usbhost.exclsem); + return ret; +} + +int usbh_ep_intr_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen) +{ + int ret; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + usb_osal_mutex_give(g_usbhost.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) +{ + int ret; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + usb_osal_mutex_give(g_usbhost.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) +{ + int ret; + + ret = usb_osal_mutex_take(g_usbhost.exclsem); + if (ret < 0) { + return ret; + } + + usb_osal_mutex_give(g_usbhost.exclsem); + + return ret; +} + +int usb_ep_cancel(usbh_epinfo_t ep) +{ + return 0; +} + +void handle_ep0(void) +{ + uint8_t ep0_status = USB->CSRL0; + + if (ep0_status & USB_HOST_EP0_ERROR) { + usb_musb_ep_status_clear(0, USB_HOST_EP0_ERROR); + usb_musb_fifo_flush(0); + usb_ep0_state = USB_EP0_STATE_SETUP; + g_usbhost.chan[0].result = -EIO; + goto chan_wait; + } + + if (ep0_status & USB_HOST_EP0_RX_STALL) { + usb_musb_ep_status_clear(0, ep0_status & USB_HOST_IN_STATUS); + usb_ep0_state = USB_EP0_STATE_SETUP; + g_usbhost.chan[0].result = -EPERM; + goto chan_wait; + } + switch (usb_ep0_state) { + case USB_EP0_STATE_SETUP: + break; + case USB_EP0_STATE_IN_DATA: + USB->CSRL0 = USB_RXCSRL1_REQPKT; + usb_ep0_state = USB_EP0_STATE_IN_DATA_C; + + break; + case USB_EP0_STATE_IN_DATA_C: + if (USB->CSRL0 & USB_CSRL0_RXRDY) { + uint32_t size = g_usbhost.chan[0].buflen; + if (size > g_usbhost.chan[0].mps) { + size = g_usbhost.chan[0].mps; + } + + size = MIN(size, USB->COUNT0); + + usb_musb_read_packet(0, g_usbhost.chan[0].buffer, size); + USB->CSRL0 &= ~USB_CSRL0_RXRDY; + + g_usbhost.chan[0].buffer += size; + g_usbhost.chan[0].buflen -= size; + + if ((size < g_usbhost.chan[0].mps) || (g_usbhost.chan[0].buflen == 0)) { + usb_ep0_state = USB_EP0_STATE_IN_STATUS_C; + USB->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_STATUS; + } else { + USB->CSRL0 = USB_RXCSRL1_REQPKT; + } + } + break; + case USB_EP0_STATE_IN_STATUS_C: + if (ep0_status & (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS)) { + usb_musb_ep_status_clear(0, (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS)); + } + + usb_ep0_state = USB_EP0_STATE_SETUP; + g_usbhost.chan[0].result = 0; + goto chan_wait; + + break; + case USB_EP0_STATE_IN_STATUS: + USB->CSRL0 = USB_CSRL0_REQPKT | USB_CSRL0_STATUS; + usb_ep0_state = USB_EP0_STATE_IN_STATUS_C; + break; + + case USB_EP0_STATE_OUT_DATA: { + uint32_t size = g_usbhost.chan[0].buflen; + + if (size > g_usbhost.chan[0].mps) { + size = g_usbhost.chan[0].mps; + } + + usb_musb_write_packet(0, g_usbhost.chan[0].buffer, size); + + g_usbhost.chan[0].buffer += size; + g_usbhost.chan[0].buflen -= size; + + if (size == g_usbhost.chan[0].mps) { + USB->CSRL0 = USB_CSRL0_TXRDY; + } else { + USB->CSRL0 = USB_CSRL0_TXRDY; + usb_ep0_state = USB_EP0_STATE_IN_STATUS; + } + } + + break; + } + return; +chan_wait: + usb_musb_chan_wakeup(&g_usbhost, &g_usbhost.chan[0]); +} +void USBH_IRQHandler(void) +{ + uint32_t is; + uint32_t txis; + uint32_t rxis; + uint32_t ep_status; + struct usb_musb_chan *chan; + uint8_t chidx; + uint8_t old_ep_idx; + + is = USB->IS; + txis = USB->TXIS; + rxis = USB->RXIS; + + old_ep_idx = USBC_GetActiveEp(); + if (is & USB_IS_CONN) { + if (!g_usbhost.connected) { + g_usbhost.connected = true; + extern void usbh_event_notify_handler(uint8_t event, uint8_t rhport); + usbh_event_notify_handler(USBH_EVENT_ATTACHED, 1); + } + } + + if (is & USB_IS_DISCON) { + if (g_usbhost.connected) { + g_usbhost.connected = false; + + for (uint8_t chnum = 0; chnum < CONFIG_USBHOST_CHANNELS; chnum++) { + if (g_usbhost.chan[chnum].waiter) { + /* Wake'em up! */ + g_usbhost.chan[chnum].waiter = false; + usb_osal_sem_give(g_usbhost.chan[chnum].waitsem); + } + } + + extern void usbh_event_notify_handler(uint8_t event, uint8_t rhport); + usbh_event_notify_handler(USBH_EVENT_REMOVED, 1); + } + } + + if (is & USB_IS_SOF) { + } + + txis &= USB->TXIE; + /* Handle EP0 interrupt */ + if (txis & USB_TXIE_EP0) { + txis &= ~USB_TXIE_EP0; + USB->TXIS = USB_TXIE_EP0; // clear isr flag + USBC_SelectActiveEp(0); + handle_ep0(); + } + + while (txis) { + chidx = __builtin_ctz(txis); + txis &= ~(1 << chidx); + USB->TXIS = (1 << chidx); // clear isr flag + + chan = &g_usbhost.chan[chidx]; + + USBC_SelectActiveEp(chidx); + + ep_status = HWREGB(USB_TXCSRLx_BASE); + usb_musb_ep_status_clear(chidx, ep_status | USB_HOST_OUT_STATUS); + + if (ep_status & USB_HOST_OUT_STALL) { + chan->result = -EPERM; + usb_musb_ep_status_clear(chidx, USB_HOST_OUT_STALL); + usb_musb_chan_wakeup(&g_usbhost, chan); + } else if (ep_status & USB_HOST_OUT_ERROR) { + chan->result = -EIO; + usb_musb_ep_status_clear(chidx, USB_HOST_OUT_ERROR); + usb_musb_chan_wakeup(&g_usbhost, chan); + } else { + uint32_t size = chan->buflen; + + if (size) { + if (size > chan->mps) { + size = chan->mps; + } + usb_musb_write_packet(chidx, chan->buffer, size); + HWREGB(USB_TXCSRLx_BASE) = USB_TXCSRL1_TXRDY; + chan->buffer += size; + chan->buflen -= size; + + } else { + chan->result = 0; + usb_musb_chan_wakeup(&g_usbhost, chan); + } + } + } + + rxis &= USB->RXIE; + while (rxis) { + chidx = __builtin_ctz(rxis); + rxis &= ~(1 << chidx); + USB->RXIS = (1 << chidx); // clear isr flag + chan = &g_usbhost.chan[chidx]; + + USBC_SelectActiveEp(chidx); + + ep_status = HWREGB(USB_RXCSRLx_BASE); + + if (ep_status & USB_HOST_IN_STALL) { + usb_musb_ep_status_clear(chidx, USB_HOST_IN_STALL); + usb_musb_chan_wakeup(&g_usbhost, chan); + } else if (ep_status & USB_HOST_IN_ERROR) { + usb_musb_ep_status_clear(chidx, USB_HOST_IN_ERROR); + usb_musb_chan_wakeup(&g_usbhost, chan); + } else if (ep_status & USB_RXCSRL1_RXRDY) { + uint32_t size = chan->buflen; + if (size > chan->mps) { + size = chan->mps; + } + size = MIN(size, HWREG(USB_RXCOUNTx_BASE)); + + usb_musb_read_packet(chidx, chan->buffer, size); + HWREGB(USB_RXCSRLx_BASE) &= ~USB_RXCSRL1_RXRDY; + chan->buffer += size; + chan->buflen -= size; + + if (chan->buflen == 0) { + chan->result = 0; + usb_musb_chan_wakeup(&g_usbhost, chan); + } else { + HWREGB(USB_RXCSRLx_BASE) = USB_RXCSRL1_REQPKT; + } + } + } + USBC_SelectActiveEp(old_ep_idx); +} \ No newline at end of file