From 46e19e85b9bd101ffa51a6118cafc8ab30a3dc9a Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Wed, 8 Nov 2017 10:10:31 +0000 Subject: [PATCH] Experimental. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10972 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- .../ports/STM32/LLD/OTGv1/hal_usb_lld_alt.c | 1397 +++++++++++++++++ .../ports/STM32/LLD/OTGv1/hal_usb_lld_alt.h | 626 ++++++++ 2 files changed, 2023 insertions(+) create mode 100644 os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.c create mode 100644 os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.h diff --git a/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.c b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.c new file mode 100644 index 000000000..bcbee9975 --- /dev/null +++ b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.c @@ -0,0 +1,1397 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file OTGv1/hal_usb_lld.c + * @brief STM32 USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define TRDT_VALUE_FS 5 +#define TRDT_VALUE_HS 9 + +#define EP0_MAX_INSIZE 64 +#define EP0_MAX_OUTSIZE 64 + +#if STM32_OTG_STEPPING == 1 +#if defined(BOARD_OTG_NOVBUSSENS) +#define GCCFG_INIT_VALUE (GCCFG_NOVBUSSENS | GCCFG_VBUSASEN | \ + GCCFG_VBUSBSEN | GCCFG_PWRDWN) +#else +#define GCCFG_INIT_VALUE (GCCFG_VBUSASEN | GCCFG_VBUSBSEN | \ + GCCFG_PWRDWN) +#endif + +#elif STM32_OTG_STEPPING == 2 +#if defined(BOARD_OTG_NOVBUSSENS) +#define GCCFG_INIT_VALUE GCCFG_PWRDWN +#else +#define GCCFG_INIT_VALUE (GCCFG_VBDEN | GCCFG_PWRDWN) +#endif + +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief OTG_FS driver identifier.*/ +#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) +USBDriver USBD1; +#endif + +/** @brief OTG_HS driver identifier.*/ +#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) +USBDriver USBD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief EP0 state. + * @note It is an union because IN and OUT endpoints are never used at the + * same time for EP0. + */ +static union { + /** + * @brief IN EP0 state. + */ + USBInEndpointState in; + /** + * @brief OUT EP0 state. + */ + USBOutEndpointState out; +} ep0_state; + +/** + * @brief Buffer for the EP0 setup packets. + */ +static uint8_t ep0setup_buffer[8]; + +/** + * @brief EP0 initialization structure. + */ +static const USBEndpointConfig ep0config = { + USB_EP_MODE_TYPE_CTRL, + _usb_ep0setup, + _usb_ep0in, + _usb_ep0out, + 0x40, + 0x40, + &ep0_state.in, + &ep0_state.out, + 1, + ep0setup_buffer +}; + +#if STM32_USB_USE_OTG1 +static const stm32_otg_params_t fsparams = { + STM32_USB_OTG1_RX_FIFO_SIZE / 4, + STM32_OTG1_FIFO_MEM_SIZE, + STM32_OTG1_ENDPOINTS +}; +#endif + +#if STM32_USB_USE_OTG2 +static const stm32_otg_params_t hsparams = { + STM32_USB_OTG2_RX_FIFO_SIZE / 4, + STM32_OTG2_FIFO_MEM_SIZE, + STM32_OTG2_ENDPOINTS +}; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void otg_core_reset(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + osalSysPolledDelayX(32); + + /* Core reset and delay of at least 3 PHY cycles.*/ + otgp->GRSTCTL = GRSTCTL_CSRST; + while ((otgp->GRSTCTL & GRSTCTL_CSRST) != 0) + ; + + osalSysPolledDelayX(18); + + /* Wait AHB idle condition.*/ + while ((otgp->GRSTCTL & GRSTCTL_AHBIDL) == 0) + ; +} + +static void otg_disable_ep(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + unsigned i; + + for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { + otgp->ie[i].DIEPCTL = 0; + otgp->ie[i].DIEPTSIZ = 0; + otgp->ie[i].DIEPINT = 0xFFFFFFFF; + + otgp->oe[i].DOEPCTL = 0; + otgp->oe[i].DOEPTSIZ = 0; + otgp->oe[i].DOEPINT = 0xFFFFFFFF; + } + otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); +} + +static void otg_rxfifo_flush(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + otgp->GRSTCTL = GRSTCTL_RXFFLSH; + while ((otgp->GRSTCTL & GRSTCTL_RXFFLSH) != 0) + ; + /* Wait for 3 PHY Clocks.*/ + osalSysPolledDelayX(18); +} + +static void otg_txfifo_flush(USBDriver *usbp, uint32_t fifo) { + stm32_otg_t *otgp = usbp->otg; + + otgp->GRSTCTL = GRSTCTL_TXFNUM(fifo) | GRSTCTL_TXFFLSH; + while ((otgp->GRSTCTL & GRSTCTL_TXFFLSH) != 0) + ; + /* Wait for 3 PHY Clocks.*/ + osalSysPolledDelayX(18); +} + +/** + * @brief Resets the FIFO RAM memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_ram_reset(USBDriver *usbp) { + + usbp->pmnext = usbp->otgparams->rx_fifo_size; +} + +/** + * @brief Allocates a block from the FIFO RAM memory. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] size size of the packet buffer to allocate in words + * + * @notapi + */ +static uint32_t otg_ram_alloc(USBDriver *usbp, size_t size) { + uint32_t next; + + next = usbp->pmnext; + usbp->pmnext += size; + osalDbgAssert(usbp->pmnext <= usbp->otgparams->otg_ram_size, + "OTG FIFO memory overflow"); + return next; +} + +/** + * @brief Writes to a TX FIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[in] buf buffer where to copy the endpoint data + * @param[in] n maximum number of bytes to copy + * + * @notapi + */ +static void otg_fifo_write_from_buffer(volatile uint32_t *fifop, + const uint8_t *buf, + size_t n) { + + osalDbgAssert(n > 0, "is zero"); + + while (true) { + *fifop = *((uint32_t *)buf); + if (n <= 4) { + break; + } + n -= 4; + buf += 4; + } +} + +/** + * @brief Reads a packet from the RXFIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[out] buf buffer where to copy the endpoint data + * @param[in] n number of bytes to pull from the FIFO + * @param[in] max number of bytes to copy into the buffer + * + * @notapi + */ +static void otg_fifo_read_to_buffer(volatile uint32_t *fifop, + uint8_t *buf, + size_t n, + size_t max) { + uint32_t w = 0; + size_t i = 0; + + while (i < n) { + if ((i & 3) == 0){ + w = *fifop; + } + if (i < max) { + *buf++ = (uint8_t)w; + w >>= 8; + } + i++; + } +} + +/** + * @brief Incoming packets handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_rxfifo_handler(USBDriver *usbp) { + uint32_t sts, cnt, ep; + + /* Popping the event word out of the RX FIFO.*/ + sts = usbp->otg->GRXSTSP; + + /* Event details.*/ + cnt = (sts & GRXSTSP_BCNT_MASK) >> GRXSTSP_BCNT_OFF; + ep = (sts & GRXSTSP_EPNUM_MASK) >> GRXSTSP_EPNUM_OFF; + + switch (sts & GRXSTSP_PKTSTS_MASK) { + case GRXSTSP_SETUP_DATA: + otg_fifo_read_to_buffer(usbp->otg->FIFO[0], usbp->epc[ep]->setup_buf, + cnt, 8); + break; + case GRXSTSP_SETUP_COMP: + break; + case GRXSTSP_OUT_DATA: + otg_fifo_read_to_buffer(usbp->otg->FIFO[0], + usbp->epc[ep]->out_state->rxbuf, + cnt, + usbp->epc[ep]->out_state->rxsize - + usbp->epc[ep]->out_state->rxcnt); + usbp->epc[ep]->out_state->rxbuf += cnt; + usbp->epc[ep]->out_state->rxcnt += cnt; + break; + case GRXSTSP_OUT_COMP: + break; + case GRXSTSP_OUT_GLOBAL_NAK: + break; + default: + break; + } +} + +/** + * @brief Outgoing packets handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static bool otg_txfifo_handler(USBDriver *usbp, usbep_t ep) { + + /* The TXFIFO is filled until there is space and data to be transmitted.*/ + while (true) { + uint32_t n; + + /* Transaction end condition.*/ + if (usbp->epc[ep]->in_state->txcnt >= usbp->epc[ep]->in_state->txsize) + return true; + + /* Number of bytes remaining in current transaction.*/ + n = usbp->epc[ep]->in_state->txsize - usbp->epc[ep]->in_state->txcnt; + if (n > usbp->epc[ep]->in_maxsize) + n = usbp->epc[ep]->in_maxsize; + + /* Checks if in the TXFIFO there is enough space to accommodate the + next packet.*/ + if (((usbp->otg->ie[ep].DTXFSTS & DTXFSTS_INEPTFSAV_MASK) * 4) < n) + return false; + +#if STM32_USB_OTGFIFO_FILL_BASEPRI + __set_BASEPRI(CORTEX_PRIO_MASK(STM32_USB_OTGFIFO_FILL_BASEPRI)); +#endif + otg_fifo_write_from_buffer(usbp->otg->FIFO[ep], + usbp->epc[ep]->in_state->txbuf, + n); + usbp->epc[ep]->in_state->txbuf += n; + usbp->epc[ep]->in_state->txcnt += n; +#if STM32_USB_OTGFIFO_FILL_BASEPRI + __set_BASEPRI(0); +#endif + } +} + +/** + * @brief Generic endpoint IN handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static void otg_epin_handler(USBDriver *usbp, usbep_t ep) { + stm32_otg_t *otgp = usbp->otg; + uint32_t epint = otgp->ie[ep].DIEPINT; + + otgp->ie[ep].DIEPINT = epint; + + if (epint & DIEPINT_TOC) { + /* Timeouts not handled yet, not sure how to handle.*/ + } + if ((epint & DIEPINT_XFRC) && (otgp->DIEPMSK & DIEPMSK_XFRCM)) { + /* Transmit transfer complete.*/ + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + if (isp->txsize < isp->totsize) { + /* In case the transaction covered only part of the total transfer + then another transaction is immediately started in order to + cover the remaining.*/ + isp->txsize = isp->totsize - isp->txsize; + isp->txcnt = 0; + osalSysLockFromISR(); + usb_lld_start_in(usbp, ep); + osalSysUnlockFromISR(); + } + else { + /* End on IN transfer.*/ + _usb_isr_invoke_in_cb(usbp, ep); + } + } + if ((epint & DIEPINT_TXFE) && + (otgp->DIEPEMPMSK & DIEPEMPMSK_INEPTXFEM(ep))) { + /* The thread is made ready, it will be scheduled on ISR exit.*/ + osalSysLockFromISR(); + usbp->txpending |= (1 << ep); + otgp->DIEPEMPMSK &= ~(1 << ep); + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } +} + +/** + * @brief Generic endpoint OUT handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static void otg_epout_handler(USBDriver *usbp, usbep_t ep) { + stm32_otg_t *otgp = usbp->otg; + uint32_t epint = otgp->oe[ep].DOEPINT; + + /* Resets all EP IRQ sources.*/ + otgp->oe[ep].DOEPINT = epint; + + if ((epint & DOEPINT_STUP) && (otgp->DOEPMSK & DOEPMSK_STUPM)) { + /* Setup packets handling, setup packets are handled using a + specific callback.*/ + _usb_isr_invoke_setup_cb(usbp, ep); + } + + if ((epint & DOEPINT_XFRC) && (otgp->DOEPMSK & DOEPMSK_XFRCM)) { + USBOutEndpointState *osp; + + /* OUT state structure pointer for this endpoint.*/ + osp = usbp->epc[ep]->out_state; + + /* EP0 requires special handling.*/ + if (ep == 0) { + +#if defined(STM32_OTG_SEQUENCE_WORKAROUND) + /* If an OUT transaction end interrupt is processed while the state + machine is not in an OUT state then it is ignored, this is caused + on some devices (L4) apparently injecting spurious data complete + words in the RX FIFO.*/ + if ((usbp->ep0state & USB_OUT_STATE) == 0) + return; +#endif + + /* In case the transaction covered only part of the total transfer + then another transaction is immediately started in order to + cover the remaining.*/ + if (((osp->rxcnt % usbp->epc[ep]->out_maxsize) == 0) && + (osp->rxsize < osp->totsize)) { + osp->rxsize = osp->totsize - osp->rxsize; + osp->rxcnt = 0; + osalSysLockFromISR(); + usb_lld_start_out(usbp, ep); + osalSysUnlockFromISR(); + return; + } + } + + /* End on OUT transfer.*/ + _usb_isr_invoke_out_cb(usbp, ep); + } +} + +/** + * @brief Isochronous IN transfer failed handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_isoc_in_failed_handler(USBDriver *usbp) { + usbep_t ep; + stm32_otg_t *otgp = usbp->otg; + + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + if (((otgp->ie[ep].DIEPCTL & DIEPCTL_EPTYP_MASK) == DIEPCTL_EPTYP_ISO) && + ((otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) != 0)) { + /* Endpoint enabled -> ISOC IN transfer failed */ + /* Disable endpoint */ + otgp->ie[ep].DIEPCTL |= (DIEPCTL_EPDIS | DIEPCTL_SNAK); + while (otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) + ; + + /* Flush FIFO */ + otg_txfifo_flush(usbp, ep); + + /* Prepare data for next frame */ + _usb_isr_invoke_in_cb(usbp, ep); + + /* Pump out data for next frame */ + osalSysLockFromISR(); + otgp->DIEPEMPMSK &= ~(1 << ep); + usbp->txpending |= (1 << ep); + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } + } +} + +/** + * @brief Isochronous OUT transfer failed handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_isoc_out_failed_handler(USBDriver *usbp) { + usbep_t ep; + stm32_otg_t *otgp = usbp->otg; + + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + if (((otgp->oe[ep].DOEPCTL & DOEPCTL_EPTYP_MASK) == DOEPCTL_EPTYP_ISO) && + ((otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) != 0)) { + /* Endpoint enabled -> ISOC OUT transfer failed */ + /* Disable endpoint */ + /* FIXME: Core stucks here */ + /*otgp->oe[ep].DOEPCTL |= (DOEPCTL_EPDIS | DOEPCTL_SNAK); + while (otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) + ;*/ + /* Prepare transfer for next frame */ + _usb_isr_invoke_out_cb(usbp, ep); + } + } +} + +/** + * @brief OTG shared ISR. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void usb_lld_serve_interrupt(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + uint32_t sts, src; + + sts = otgp->GINTSTS; + sts &= otgp->GINTMSK; + otgp->GINTSTS = sts; + + /* Reset interrupt handling.*/ + if (sts & GINTSTS_USBRST) { + + /* Resetting pending operations.*/ + usbp->txpending = 0; + + /* Default reset action.*/ + _usb_reset(usbp); + + /* Preventing execution of more handlers, the core has been reset.*/ + return; + } + + /* Wake-up handling.*/ + if (sts & GINTSTS_WKUPINT) { + /* If clocks are gated off, turn them back on (may be the case if + coming out of suspend mode).*/ + if (otgp->PCGCCTL & (PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK)) { + /* Set to zero to un-gate the USB core clocks.*/ + otgp->PCGCCTL &= ~(PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK); + } + + /* Clear the Remote Wake-up Signaling.*/ + otgp->DCTL &= ~DCTL_RWUSIG; + + _usb_wakeup(usbp); + } + + /* Suspend handling.*/ + if (sts & GINTSTS_USBSUSP) { + + /* Resetting pending operations.*/ + usbp->txpending = 0; + + /* Default suspend action.*/ + _usb_suspend(usbp); + } + + /* Enumeration done.*/ + if (sts & GINTSTS_ENUMDNE) { + /* Full or High speed timing selection.*/ + if ((otgp->DSTS & DSTS_ENUMSPD_MASK) == DSTS_ENUMSPD_HS_480) { + otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | + GUSBCFG_TRDT(TRDT_VALUE_HS); + } + else { + otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | + GUSBCFG_TRDT(TRDT_VALUE_FS); + } + } + + /* SOF interrupt handling.*/ + if (sts & GINTSTS_SOF) { + _usb_isr_invoke_sof_cb(usbp); + } + + /* Isochronous IN failed handling */ + if (sts & GINTSTS_IISOIXFR) { + otg_isoc_in_failed_handler(usbp); + } + + /* Isochronous OUT failed handling */ + if (sts & GINTSTS_IISOOXFR) { + otg_isoc_out_failed_handler(usbp); + } + + /* RX FIFO not empty handling.*/ +#if 0 + if (sts & GINTSTS_RXFLVL) { + /* The interrupt is masked while the thread has control or it would + be triggered again.*/ + osalSysLockFromISR(); + otgp->GINTMSK &= ~GINTMSK_RXFLVLM; + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } +#else + /* Performing the whole FIFO emptying in the ISR, it is advised to keep + this IRQ at a very low priority level.*/ + if ((sts & GINTSTS_RXFLVL) != 0U) { + otg_rxfifo_handler(usbp); + } +#endif + + /* IN/OUT endpoints event handling.*/ + src = otgp->DAINT; + if (sts & GINTSTS_OEPINT) { + if (src & (1 << 16)) + otg_epout_handler(usbp, 0); + if (src & (1 << 17)) + otg_epout_handler(usbp, 1); + if (src & (1 << 18)) + otg_epout_handler(usbp, 2); + if (src & (1 << 19)) + otg_epout_handler(usbp, 3); +#if USB_MAX_ENDPOINTS >= 4 + if (src & (1 << 20)) + otg_epout_handler(usbp, 4); +#endif +#if USB_MAX_ENDPOINTS >= 5 + if (src & (1 << 21)) + otg_epout_handler(usbp, 5); +#endif +#if USB_MAX_ENDPOINTS >= 6 + if (src & (1 << 22)) + otg_epout_handler(usbp, 6); +#endif +#if USB_MAX_ENDPOINTS >= 7 + if (src & (1 << 23)) + otg_epout_handler(usbp, 7); +#endif +#if USB_MAX_ENDPOINTS >= 8 + if (src & (1 << 24)) + otg_epout_handler(usbp, 8); +#endif + } + if (sts & GINTSTS_IEPINT) { + if (src & (1 << 0)) + otg_epin_handler(usbp, 0); + if (src & (1 << 1)) + otg_epin_handler(usbp, 1); + if (src & (1 << 2)) + otg_epin_handler(usbp, 2); + if (src & (1 << 3)) + otg_epin_handler(usbp, 3); +#if USB_MAX_ENDPOINTS >= 4 + if (src & (1 << 4)) + otg_epin_handler(usbp, 4); +#endif +#if USB_MAX_ENDPOINTS >= 5 + if (src & (1 << 5)) + otg_epin_handler(usbp, 5); +#endif +#if USB_MAX_ENDPOINTS >= 6 + if (src & (1 << 6)) + otg_epin_handler(usbp, 6); +#endif +#if USB_MAX_ENDPOINTS >= 7 + if (src & (1 << 7)) + otg_epin_handler(usbp, 7); +#endif +#if USB_MAX_ENDPOINTS >= 8 + if (src & (1 << 8)) + otg_epin_handler(usbp, 8); +#endif + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) +/** + * @brief OTG1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_OTG1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + usb_lld_serve_interrupt(&USBD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) +/** + * @brief OTG2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_OTG2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + usb_lld_serve_interrupt(&USBD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + + /* Driver initialization.*/ +#if STM32_USB_USE_OTG1 + usbObjectInit(&USBD1); + USBD1.wait = NULL; + USBD1.otg = OTG_FS; + USBD1.otgparams = &fsparams; + +#if defined(_CHIBIOS_RT_) + USBD1.tr = NULL; + /* Filling the thread working area here because the function + @p chThdCreateI() does not do it.*/ +#if CH_DBG_FILL_THREADS + { + void *wsp = USBD1.wa_pump; + _thread_memfill((uint8_t *)wsp, + (uint8_t *)wsp + sizeof (USBD1.wa_pump), + CH_DBG_STACK_FILL_VALUE); + } +#endif /* CH_DBG_FILL_THREADS */ +#endif /* defined(_CHIBIOS_RT_) */ +#endif + +#if STM32_USB_USE_OTG2 + usbObjectInit(&USBD2); + USBD2.wait = NULL; + USBD2.otg = OTG_HS; + USBD2.otgparams = &hsparams; + +#if defined(_CHIBIOS_RT_) + USBD2.tr = NULL; + /* Filling the thread working area here because the function + @p chThdCreateI() does not do it.*/ +#if CH_DBG_FILL_THREADS + { + void *wsp = USBD2.wa_pump; + _thread_memfill((uint8_t *)wsp, + (uint8_t *)wsp + sizeof (USBD2.wa_pump), + CH_DBG_STACK_FILL_VALUE); + } +#endif /* CH_DBG_FILL_THREADS */ +#endif /* defined(_CHIBIOS_RT_) */ +#endif +} + +/** + * @brief Configures and activates the USB peripheral. + * @note Starting the OTG cell can be a slow operation carried out with + * interrupts disabled, perform it before starting time-critical + * operations. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + if (usbp->state == USB_STOP) { + /* Clock activation.*/ + +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + /* OTG FS clock enable and reset.*/ + rccEnableOTG_FS(false); + rccResetOTG_FS(); + + /* Enables IRQ vector.*/ + nvicEnableVector(STM32_OTG1_NUMBER, STM32_USB_OTG1_IRQ_PRIORITY); + + /* - Forced device mode. + - USB turn-around time = TRDT_VALUE_FS. + - Full Speed 1.1 PHY.*/ + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | + GUSBCFG_PHYSEL; + + /* 48MHz 1.1 PHY.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + /* OTG HS clock enable and reset.*/ + rccEnableOTG_HS(false); + rccResetOTG_HS(); + + /* ULPI clock is managed depending on the presence of an external + PHY.*/ +#if defined(BOARD_OTG2_USES_ULPI) + rccEnableOTG_HSULPI(true); +#else + /* Workaround for the problem described here: + http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=1798.*/ + rccDisableOTG_HSULPI(true); +#endif + + /* Enables IRQ vector.*/ + nvicEnableVector(STM32_OTG2_NUMBER, STM32_USB_OTG2_IRQ_PRIORITY); + + /* - Forced device mode. + - USB turn-around time = TRDT_VALUE_HS or TRDT_VALUE_FS.*/ +#if defined(BOARD_OTG2_USES_ULPI) + /* High speed ULPI PHY.*/ + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_HS) | + GUSBCFG_SRPCAP | GUSBCFG_HNPCAP; +#else + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | + GUSBCFG_PHYSEL; +#endif + +#if defined(BOARD_OTG2_USES_ULPI) +#if STM32_USE_USB_OTG2_HS + /* USB 2.0 High Speed PHY in HS mode.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_HS; +#else + /* USB 2.0 High Speed PHY in FS mode.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_HS_FS; +#endif +#else + /* 48MHz 1.1 PHY.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; +#endif + } +#endif + + /* Clearing mask of TXFIFOs to be filled.*/ + usbp->txpending = 0; + + /* PHY enabled.*/ + otgp->PCGCCTL = 0; + + /* VBUS sensing and transceiver enabled.*/ + otgp->GOTGCTL = GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + +#if defined(BOARD_OTG2_USES_ULPI) +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + otgp->GCCFG = GCCFG_INIT_VALUE; + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + otgp->GCCFG = 0; + } +#endif +#else + otgp->GCCFG = GCCFG_INIT_VALUE; +#endif + + /* Soft core reset.*/ + otg_core_reset(usbp); + + /* Interrupts on TXFIFOs half empty.*/ + otgp->GAHBCFG = 0; + + /* Endpoints re-initialization.*/ + otg_disable_ep(usbp); + + /* Clear all pending Device Interrupts, only the USB Reset interrupt + is required initially.*/ + otgp->DIEPMSK = 0; + otgp->DOEPMSK = 0; + otgp->DAINTMSK = 0; + if (usbp->config->sof_cb == NULL) + otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | + GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | + GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM; + else + otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | + GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | + GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM | + GINTMSK_SOFM; + + /* Clears all pending IRQs, if any. */ + otgp->GINTSTS = 0xFFFFFFFF; + +#if defined(_CHIBIOS_RT_) + /* Creates the data pump thread. Note, it is created only once.*/ + if (usbp->tr == NULL) { + thread_descriptor_t usbpump_descriptor = { + "usb_pump", + THD_WORKING_AREA_BASE(usbp->wa_pump), + THD_WORKING_AREA_END(usbp->wa_pump), + STM32_USB_OTG_THREAD_PRIO, + usb_lld_pump, + (void *)usbp + }; + + usbp->tr = chThdCreateI(&usbpump_descriptor); + chSchRescheduleS(); + } +#endif + + /* Global interrupts enable.*/ + otgp->GAHBCFG |= GAHBCFG_GINTMSK; + } +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + /* If in ready state then disables the USB clock.*/ + if (usbp->state != USB_STOP) { + + /* Disabling all endpoints in case the driver has been stopped while + active.*/ + otg_disable_ep(usbp); + + usbp->txpending = 0; + + otgp->DAINTMSK = 0; + otgp->GAHBCFG = 0; + otgp->GCCFG = 0; + +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + nvicDisableVector(STM32_OTG1_NUMBER); + rccDisableOTG_FS(false); + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + nvicDisableVector(STM32_OTG2_NUMBER); + rccDisableOTG_HS(false); +#if defined(BOARD_OTG2_USES_ULPI) + rccDisableOTG_HSULPI(true) +#endif + } +#endif + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + unsigned i; + stm32_otg_t *otgp = usbp->otg; + + /* Flush the Tx FIFO.*/ + otg_txfifo_flush(usbp, 0); + + /* Endpoint interrupts all disabled and cleared.*/ + otgp->DIEPEMPMSK = 0; + otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); + + /* All endpoints in NAK mode, interrupts cleared.*/ + for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { + otgp->ie[i].DIEPCTL = DIEPCTL_SNAK; + otgp->oe[i].DOEPCTL = DOEPCTL_SNAK; + otgp->ie[i].DIEPINT = 0xFFFFFFFF; + otgp->oe[i].DOEPINT = 0xFFFFFFFF; + } + + /* Resets the FIFO memory allocator.*/ + otg_ram_reset(usbp); + + /* Receive FIFO size initialization, the address is always zero.*/ + otgp->GRXFSIZ = usbp->otgparams->rx_fifo_size; + otg_rxfifo_flush(usbp); + + /* Resets the device address to zero.*/ + otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(0); + + /* Enables also EP-related interrupt sources.*/ + otgp->GINTMSK |= GINTMSK_RXFLVLM | GINTMSK_OEPM | GINTMSK_IEPM; + otgp->DIEPMSK = DIEPMSK_TOCM | DIEPMSK_XFRCM; + otgp->DOEPMSK = DOEPMSK_STUPM | DOEPMSK_XFRCM; + + /* EP0 initialization, it is a special case.*/ + usbp->epc[0] = &ep0config; + otgp->oe[0].DOEPTSIZ = DOEPTSIZ_STUPCNT(3); + otgp->oe[0].DOEPCTL = DOEPCTL_SD0PID | DOEPCTL_USBAEP | DOEPCTL_EPTYP_CTRL | + DOEPCTL_MPSIZ(ep0config.out_maxsize); + otgp->ie[0].DIEPTSIZ = 0; + otgp->ie[0].DIEPCTL = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL | + DIEPCTL_TXFNUM(0) | DIEPCTL_MPSIZ(ep0config.in_maxsize); + otgp->DIEPTXF0 = DIEPTXF_INEPTXFD(ep0config.in_maxsize / 4) | + DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, + ep0config.in_maxsize / 4)); +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(usbp->address); +} + +/** + * @brief Enables an endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { + uint32_t ctl, fsize; + stm32_otg_t *otgp = usbp->otg; + + /* IN and OUT common parameters.*/ + switch (usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) { + case USB_EP_MODE_TYPE_CTRL: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL; + break; + case USB_EP_MODE_TYPE_ISOC: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_ISO; + break; + case USB_EP_MODE_TYPE_BULK: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_BULK; + break; + case USB_EP_MODE_TYPE_INTR: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_INTR; + break; + default: + return; + } + + /* OUT endpoint activation or deactivation.*/ + otgp->oe[ep].DOEPTSIZ = 0; + if (usbp->epc[ep]->out_state != NULL) { + otgp->oe[ep].DOEPCTL = ctl | DOEPCTL_MPSIZ(usbp->epc[ep]->out_maxsize); + otgp->DAINTMSK |= DAINTMSK_OEPM(ep); + } + else { + otgp->oe[ep].DOEPCTL &= ~DOEPCTL_USBAEP; + otgp->DAINTMSK &= ~DAINTMSK_OEPM(ep); + } + + /* IN endpoint activation or deactivation.*/ + otgp->ie[ep].DIEPTSIZ = 0; + if (usbp->epc[ep]->in_state != NULL) { + /* FIFO allocation for the IN endpoint.*/ + fsize = usbp->epc[ep]->in_maxsize / 4; + if (usbp->epc[ep]->in_multiplier > 1) + fsize *= usbp->epc[ep]->in_multiplier; + otgp->DIEPTXF[ep - 1] = DIEPTXF_INEPTXFD(fsize) | + DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, fsize)); + otg_txfifo_flush(usbp, ep); + + otgp->ie[ep].DIEPCTL = ctl | + DIEPCTL_TXFNUM(ep) | + DIEPCTL_MPSIZ(usbp->epc[ep]->in_maxsize); + otgp->DAINTMSK |= DAINTMSK_IEPM(ep); + } + else { + otgp->DIEPTXF[ep - 1] = 0x02000400; /* Reset value.*/ + otg_txfifo_flush(usbp, ep); + otgp->ie[ep].DIEPCTL &= ~DIEPCTL_USBAEP; + otgp->DAINTMSK &= ~DAINTMSK_IEPM(ep); + } +} + +/** + * @brief Disables all the active endpoints except the endpoint zero. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_disable_endpoints(USBDriver *usbp) { + + /* Resets the FIFO memory allocator.*/ + otg_ram_reset(usbp); + + /* Disabling all endpoints.*/ + otg_disable_ep(usbp); +} + +/** + * @brief Returns the status of an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { + uint32_t ctl; + + (void)usbp; + + ctl = usbp->otg->oe[ep].DOEPCTL; + if (!(ctl & DOEPCTL_USBAEP)) + return EP_STATUS_DISABLED; + if (ctl & DOEPCTL_STALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +} + +/** + * @brief Returns the status of an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { + uint32_t ctl; + + (void)usbp; + + ctl = usbp->otg->ie[ep].DIEPCTL; + if (!(ctl & DIEPCTL_USBAEP)) + return EP_STATUS_DISABLED; + if (ctl & DIEPCTL_STALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +} + +/** + * @brief Reads a setup packet from the dedicated packet buffer. + * @details This function must be invoked in the context of the @p setup_cb + * callback in order to read the received setup packet. + * @pre In order to use this function the endpoint must have been + * initialized as a control endpoint. + * @post The endpoint is ready to accept another packet. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[out] buf buffer where to copy the packet data + * + * @notapi + */ +void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { + + memcpy(buf, usbp->epc[ep]->setup_buf, 8); +} + +/** + * @brief Starts a receive operation on an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { + uint32_t pcnt, rxsize; + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + /* Transfer initialization.*/ + osp->totsize = osp->rxsize; + if ((ep == 0) && (osp->rxsize > EP0_MAX_OUTSIZE)) + osp->rxsize = EP0_MAX_OUTSIZE; + + /* Transaction size is rounded to a multiple of packet size because the + following requirement in the RM: + "For OUT transfers, the transfer size field in the endpoint's transfer + size register must be a multiple of the maximum packet size of the + endpoint, adjusted to the Word boundary".*/ + pcnt = (osp->rxsize + usbp->epc[ep]->out_maxsize - 1U) / + usbp->epc[ep]->out_maxsize; + rxsize = (pcnt * usbp->epc[ep]->out_maxsize + 3U) & 0xFFFFFFFCU; + + /*Setting up transaction parameters in DOEPTSIZ.*/ + usbp->otg->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | + DOEPTSIZ_XFRSIZ(rxsize); + + /* Special case of isochronous endpoint.*/ + if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { + /* Odd/even bit toggling for isochronous endpoint.*/ + if (usbp->otg->DSTS & DSTS_FNSOF_ODD) + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SEVNFRM; + else + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SODDFRM; + } + + /* Starting operation.*/ + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_EPENA | DOEPCTL_CNAK; +} + +/** + * @brief Starts a transmit operation on an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + /* Transfer initialization.*/ + isp->totsize = isp->txsize; + if (isp->txsize == 0) { + /* Special case, sending zero size packet.*/ + usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(1) | DIEPTSIZ_XFRSIZ(0); + } + else { + if ((ep == 0) && (isp->txsize > EP0_MAX_INSIZE)) + isp->txsize = EP0_MAX_INSIZE; + + /* Normal case.*/ + uint32_t pcnt = (isp->txsize + usbp->epc[ep]->in_maxsize - 1) / + usbp->epc[ep]->in_maxsize; + /* TODO: Support more than one packet per frame for isochronous transfers.*/ + usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_MCNT(1) | DIEPTSIZ_PKTCNT(pcnt) | + DIEPTSIZ_XFRSIZ(isp->txsize); + } + + /* Special case of isochronous endpoint.*/ + if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { + /* Odd/even bit toggling.*/ + if (usbp->otg->DSTS & DSTS_FNSOF_ODD) + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SEVNFRM; + else + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SODDFRM; + } + + /* Starting operation.*/ + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_EPENA | DIEPCTL_CNAK; + usbp->otg->DIEPEMPMSK |= DIEPEMPMSK_INEPTXFEM(ep); +} + +/** + * @brief Brings an OUT endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { + + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_STALL; +} + +/** + * @brief Brings an IN endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { + + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_STALL; +} + +/** + * @brief Brings an OUT endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { + + usbp->otg->oe[ep].DOEPCTL &= ~DOEPCTL_STALL; +} + +/** + * @brief Brings an IN endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { + + usbp->otg->ie[ep].DIEPCTL &= ~DIEPCTL_STALL; +} + +/** + * @brief USB data transfer loop. + * @details This function must be executed by a system thread in order to + * make the USB driver work. + * @note The data copy part of the driver is implemented in this thread + * in order to not perform heavy tasks within interrupt handlers. + * + * @param[in] p pointer to the @p USBDriver object + * + * @special + */ +void usb_lld_pump(void *p) { + USBDriver *usbp = (USBDriver *)p; + stm32_otg_t *otgp = usbp->otg; + + osalSysLock(); + while (true) { + usbep_t ep; + uint32_t epmask; + + /* Nothing to do, going to sleep.*/ + if ((usbp->state == USB_STOP) || + ((usbp->txpending == 0)/* && !(otgp->GINTSTS & GINTSTS_RXFLVL)*/)) { +/* otgp->GINTMSK |= GINTMSK_RXFLVLM;*/ + osalThreadSuspendS(&usbp->wait); + } + osalSysUnlock(); + + /* Checks if there are TXFIFOs to be filled.*/ + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + +#if 0 + /* Empties the RX FIFO.*/ + while (otgp->GINTSTS & GINTSTS_RXFLVL) { + otg_rxfifo_handler(usbp); + } +#endif + + epmask = (1 << ep); + if (usbp->txpending & epmask) { + bool done; + + osalSysLock(); + /* USB interrupts are globally *suspended* because the peripheral + does not allow any interference during the TX FIFO filling + operation. + Synopsys document: DesignWare Cores USB 2.0 Hi-Speed On-The-Go (OTG) + "The application has to finish writing one complete packet before + switching to a different channel/endpoint FIFO. Violating this + rule results in an error.".*/ + otgp->GAHBCFG &= ~GAHBCFG_GINTMSK; + usbp->txpending &= ~epmask; + osalSysUnlock(); + + done = otg_txfifo_handler(usbp, ep); + + osalSysLock(); + otgp->GAHBCFG |= GAHBCFG_GINTMSK; + if (!done) + otgp->DIEPEMPMSK |= epmask; + osalSysUnlock(); + } + } + osalSysLock(); + } +} + +#endif /* HAL_USE_USB */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.h b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.h new file mode 100644 index 000000000..ba5e4fc2f --- /dev/null +++ b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld_alt.h @@ -0,0 +1,626 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file OTGv1/hal_usb_lld.h + * @brief STM32 USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef HAL_USB_LLD_H +#define HAL_USB_LLD_H + +#if HAL_USE_USB || defined(__DOXYGEN__) + +#include "stm32_otg.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Status stage handling method. + */ +#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW + +/** + * @brief The address can be changed immediately upon packet reception. + */ +#define USB_SET_ADDRESS_MODE USB_EARLY_SET_ADDRESS + +/** + * @brief Method for set address acknowledge. + */ +#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief OTG1 driver enable switch. + * @details If set to @p TRUE the support for OTG_FS is included. + * @note The default is @p FALSE + */ +#if !defined(STM32_USB_USE_OTG1) || defined(__DOXYGEN__) +#define STM32_USB_USE_OTG1 FALSE +#endif + +/** + * @brief OTG2 driver enable switch. + * @details If set to @p TRUE the support for OTG_HS is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_USB_USE_OTG2) || defined(__DOXYGEN__) +#define STM32_USB_USE_OTG2 FALSE +#endif + +/** + * @brief OTG1 interrupt priority level setting. + */ +#if !defined(STM32_USB_OTG1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_OTG1_IRQ_PRIORITY 14 +#endif + +/** + * @brief OTG2 interrupt priority level setting. + */ +#if !defined(STM32_USB_OTG2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_OTG2_IRQ_PRIORITY 14 +#endif + +/** + * @brief OTG1 RX shared FIFO size. + * @note Must be a multiple of 4. + */ +#if !defined(STM32_USB_OTG1_RX_FIFO_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG1_RX_FIFO_SIZE 512 +#endif + +/** + * @brief OTG2 RX shared FIFO size. + * @note Must be a multiple of 4. + */ +#if !defined(STM32_USB_OTG2_RX_FIFO_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG2_RX_FIFO_SIZE 1024 +#endif + +/** + * @brief Enables HS mode on OTG2 else FS mode. + * @note The default is @p TRUE. + * @note Has effect only if @p BOARD_OTG2_USES_ULPI is defined. + */ +#if !defined(STM32_USE_USB_OTG2_HS) || defined(__DOXYGEN__) +#define STM32_USE_USB_OTG2_HS TRUE +#endif + +/** + * @brief Dedicated data pump threads priority. + */ +#if !defined(STM32_USB_OTG_THREAD_PRIO) || defined(__DOXYGEN__) +#define STM32_USB_OTG_THREAD_PRIO LOWPRIO +#endif + +/** + * @brief Dedicated data pump threads stack size. + */ +#if !defined(STM32_USB_OTG_THREAD_STACK_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG_THREAD_STACK_SIZE 128 +#endif + +/** + * @brief Exception priority level during TXFIFOs operations. + * @note Because an undocumented silicon behavior the operation of + * copying a packet into a TXFIFO must not be interrupted by + * any other operation on the OTG peripheral. + * This parameter represents the priority mask during copy + * operations. The default value only allows to call USB + * functions from callbacks invoked from USB ISR handlers. + * If you need to invoke USB functions from other handlers + * then raise this priority mast to the same level of the + * handler you need to use. + * @note The value zero means disabled, when disabled calling USB + * functions is only safe from thread level or from USB + * callbacks. + */ +#if !defined(STM32_USB_OTGFIFO_FILL_BASEPRI) || defined(__DOXYGEN__) +#define STM32_USB_OTGFIFO_FILL_BASEPRI 0 +#endif + +/** + * @brief Host wake-up procedure duration. + */ +#if !defined(USB_HOST_WAKEUP_DURATION) || defined(__DOXYGEN__) +#define USB_HOST_WAKEUP_DURATION 2 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/* Registry checks.*/ +#if !defined(STM32_OTG_STEPPING) +#error "STM32_OTG_STEPPING not defined in registry" +#endif + +#if (STM32_OTG_STEPPING < 1) || (STM32_OTG_STEPPING > 2) +#error "unsupported STM32_OTG_STEPPING" +#endif + +#if !defined(STM32_HAS_OTG1) || !defined(STM32_HAS_OTG2) +#error "STM32_HAS_OTGx not defined in registry" +#endif + +#if STM32_HAS_OTG1 && !defined(STM32_OTG1_ENDPOINTS) +#error "STM32_OTG1_ENDPOINTS not defined in registry" +#endif + +#if STM32_HAS_OTG2 && !defined(STM32_OTG2_ENDPOINTS) +#error "STM32_OTG2_ENDPOINTS not defined in registry" +#endif + +#if STM32_HAS_OTG1 && !defined(STM32_OTG1_FIFO_MEM_SIZE) +#error "STM32_OTG1_FIFO_MEM_SIZE not defined in registry" +#endif + +#if STM32_HAS_OTG2 && !defined(STM32_OTG2_FIFO_MEM_SIZE) +#error "STM32_OTG2_FIFO_MEM_SIZE not defined in registry" +#endif + +#if (STM32_USB_USE_OTG1 && !defined(STM32_OTG1_HANDLER)) || \ + (STM32_USB_USE_OTG2 && !defined(STM32_OTG2_HANDLER)) +#error "STM32_OTGx_HANDLER not defined in registry" +#endif + +#if (STM32_USB_USE_OTG1 && !defined(STM32_OTG1_NUMBER)) || \ + (STM32_USB_USE_OTG2 && !defined(STM32_OTG2_NUMBER)) +#error "STM32_OTGx_NUMBER not defined in registry" +#endif + +/** + * @brief Maximum endpoint address. + */ +#if (STM32_HAS_OTG2 && STM32_USB_USE_OTG2) || defined(__DOXYGEN__) +#if (STM32_OTG1_ENDPOINTS < STM32_OTG2_ENDPOINTS) || defined(__DOXYGEN__) +#define USB_MAX_ENDPOINTS STM32_OTG2_ENDPOINTS +#else +#define USB_MAX_ENDPOINTS STM32_OTG1_ENDPOINTS +#endif +#else +#define USB_MAX_ENDPOINTS STM32_OTG1_ENDPOINTS +#endif + +#if STM32_USB_USE_OTG1 && !STM32_HAS_OTG1 +#error "OTG1 not present in the selected device" +#endif + +#if STM32_USB_USE_OTG2 && !STM32_HAS_OTG2 +#error "OTG2 not present in the selected device" +#endif + +#if !STM32_USB_USE_OTG1 && !STM32_USB_USE_OTG2 +#error "USB driver activated but no USB peripheral assigned" +#endif + +#if STM32_USB_USE_OTG1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to OTG1" +#endif + +#if STM32_USB_USE_OTG2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to OTG2" +#endif + +#if (STM32_USB_OTG1_RX_FIFO_SIZE & 3) != 0 +#error "OTG1 RX FIFO size must be a multiple of 4" +#endif + +#if (STM32_USB_OTG2_RX_FIFO_SIZE & 3) != 0 +#error "OTG2 RX FIFO size must be a multiple of 4" +#endif + +#if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) +#define STM32_USBCLK STM32_PLL48CLK +#elif defined(STM32F10X_CL) +#define STM32_USBCLK STM32_OTGFSCLK +#elif defined(STM32L4XX) +#define STM32_USBCLK STM32_48CLK +#else +#error "unsupported STM32 platform for OTG functionality" +#endif + +#if STM32_USBCLK != 48000000 +#error "the USB OTG driver requires a 48MHz clock" +#endif + +#if (USB_HOST_WAKEUP_DURATION < 2) || (USB_HOST_WAKEUP_DURATION > 15) +#error "invalid USB_HOST_WAKEUP_DURATION setting, it must be between 2 and 15" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Peripheral-specific parameters block. + */ +typedef struct { + uint32_t rx_fifo_size; + uint32_t otg_ram_size; + uint32_t num_endpoints; +} stm32_otg_params_t; + +/** + * @brief Type of an IN endpoint state structure. + */ +typedef struct { + /** + * @brief Requested transmit transfer size. + */ + size_t txsize; + /** + * @brief Transmitted bytes so far. + */ + size_t txcnt; + /** + * @brief Pointer to the transmission linear buffer. + */ + const uint8_t *txbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Total transmit transfer size. + */ + size_t totsize; +} USBInEndpointState; + +/** + * @brief Type of an OUT endpoint state structure. + */ +typedef struct { + /** + * @brief Requested receive transfer size. + */ + size_t rxsize; + /** + * @brief Received bytes so far. + */ + size_t rxcnt; + /** + * @brief Pointer to the receive linear buffer. + */ + uint8_t *rxbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Total receive transfer size. + */ + size_t totsize; +} USBOutEndpointState; + +/** + * @brief Type of an USB endpoint configuration structure. + * @note Platform specific restrictions may apply to endpoints. + */ +typedef struct { + /** + * @brief Type and mode of the endpoint. + */ + uint32_t ep_mode; + /** + * @brief Setup packet notification callback. + * @details This callback is invoked when a setup packet has been + * received. + * @post The application must immediately call @p usbReadPacket() in + * order to access the received packet. + * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL + * endpoints, it should be set to @p NULL for other endpoint + * types. + */ + usbepcallback_t setup_cb; + /** + * @brief IN endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t in_cb; + /** + * @brief OUT endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t out_cb; + /** + * @brief IN endpoint maximum packet size. + * @details This field must be set to zero if the IN endpoint is not used. + */ + uint16_t in_maxsize; + /** + * @brief OUT endpoint maximum packet size. + * @details This field must be set to zero if the OUT endpoint is not used. + */ + uint16_t out_maxsize; + /** + * @brief @p USBEndpointState associated to the IN endpoint. + * @details This field must be set to @p NULL if the IN endpoint is not + * used. + */ + USBInEndpointState *in_state; + /** + * @brief @p USBEndpointState associated to the OUT endpoint. + * @details This field must be set to @p NULL if the OUT endpoint is not + * used. + */ + USBOutEndpointState *out_state; + /* End of the mandatory fields.*/ + /** + * @brief Determines the space allocated for the TXFIFO as multiples of + * the packet size (@p in_maxsize). Note that zero is interpreted + * as one for simplicity and robustness. + */ + uint16_t in_multiplier; + /** + * @brief Pointer to a buffer for setup packets. + * @details Setup packets require a dedicated 8-bytes buffer, set this + * field to @p NULL for non-control endpoints. + */ + uint8_t *setup_buf; +} USBEndpointConfig; + +/** + * @brief Type of an USB driver configuration structure. + */ +typedef struct { + /** + * @brief USB events callback. + * @details This callback is invoked when an USB driver event is registered. + */ + usbeventcb_t event_cb; + /** + * @brief Device GET_DESCRIPTOR request callback. + * @note This callback is mandatory and cannot be set to @p NULL. + */ + usbgetdescriptor_t get_descriptor_cb; + /** + * @brief Requests hook callback. + * @details This hook allows to be notified of standard requests or to + * handle non standard requests. + */ + usbreqhandler_t requests_hook_cb; + /** + * @brief Start Of Frame callback. + */ + usbcallback_t sof_cb; + /* End of the mandatory fields.*/ +} USBConfig; + +/** + * @brief Structure representing an USB driver. + */ +struct USBDriver { + /** + * @brief Driver state. + */ + usbstate_t state; + /** + * @brief Current configuration data. + */ + const USBConfig *config; + /** + * @brief Bit map of the transmitting IN endpoints. + */ + uint16_t transmitting; + /** + * @brief Bit map of the receiving OUT endpoints. + */ + uint16_t receiving; + /** + * @brief Active endpoints configurations. + */ + const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an IN endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *in_params[USB_MAX_ENDPOINTS]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an OUT endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *out_params[USB_MAX_ENDPOINTS]; + /** + * @brief Endpoint 0 state. + */ + usbep0state_t ep0state; + /** + * @brief Next position in the buffer to be transferred through endpoint 0. + */ + uint8_t *ep0next; + /** + * @brief Number of bytes yet to be transferred through endpoint 0. + */ + size_t ep0n; + /** + * @brief Endpoint 0 end transaction callback. + */ + usbcallback_t ep0endcb; + /** + * @brief Setup packet buffer. + */ + uint8_t setup[8]; + /** + * @brief Current USB device status. + */ + uint16_t status; + /** + * @brief Assigned USB address. + */ + uint8_t address; + /** + * @brief Current USB device configuration. + */ + uint8_t configuration; + /** + * @brief State of the driver when a suspend happened. + */ + usbstate_t saved_state; +#if defined(USB_DRIVER_EXT_FIELDS) + USB_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the OTG peripheral associated to this driver. + */ + stm32_otg_t *otg; + /** + * @brief Peripheral-specific parameters. + */ + const stm32_otg_params_t *otgparams; + /** + * @brief Pointer to the next address in the packet memory. + */ + uint32_t pmnext; + /** + * @brief Mask of TXFIFOs to be filled by the pump thread. + */ + uint32_t txpending; + /** + * @brief Pointer to the thread when it is sleeping or @p NULL. + */ + thread_reference_t wait; +#if defined(_CHIBIOS_RT_) + /** + * @brief Pointer to the thread. + */ + thread_reference_t tr; + /** + * @brief Working area for the dedicated data pump thread; + */ + THD_WORKING_AREA(wa_pump, STM32_USB_OTG_THREAD_STACK_SIZE); +#endif +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the exact size of a receive transaction. + * @details The received size can be different from the size specified in + * @p usbStartReceiveI() because the last packet could have a size + * different from the expected one. + * @pre The OUT endpoint must have been configured in transaction mode + * in order to use this function. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return Received data size. + * + * @notapi + */ +#define usb_lld_get_transaction_size(usbp, ep) \ + ((usbp)->epc[ep]->out_state->rxcnt) + +/** + * @brief Connects the USB device. + * + * @notapi + */ +#if (STM32_OTG_STEPPING == 1) || defined(__DOXYGEN__) +#define usb_lld_connect_bus(usbp) ((usbp)->otg->GCCFG |= GCCFG_VBUSBSEN) +#else +#define usb_lld_connect_bus(usbp) ((usbp)->otg->DCTL &= ~DCTL_SDIS) +#endif + +/** + * @brief Disconnect the USB device. + * + * @notapi + */ +#if (STM32_OTG_STEPPING == 1) || defined(__DOXYGEN__) +#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->GCCFG &= ~GCCFG_VBUSBSEN) +#else +#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->DCTL |= DCTL_SDIS) +#endif + +/** + * @brief Start of host wake-up procedure. + * + * @notapi + */ +#define usb_lld_wakeup_host(usbp) \ + do{ \ + (usbp)->otg->DCTL |= DCTL_RWUSIG; \ + osalThreadSleepMilliseconds(USB_HOST_WAKEUP_DURATION); \ + (usbp)->otg->DCTL &= ~DCTL_RWUSIG; \ + } while (false) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_USB_USE_OTG1 && !defined(__DOXYGEN__) +extern USBDriver USBD1; +#endif + +#if STM32_USB_USE_OTG2 && !defined(__DOXYGEN__) +extern USBDriver USBD2; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void usb_lld_init(void); + void usb_lld_start(USBDriver *usbp); + void usb_lld_stop(USBDriver *usbp); + void usb_lld_reset(USBDriver *usbp); + void usb_lld_set_address(USBDriver *usbp); + void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); + void usb_lld_disable_endpoints(USBDriver *usbp); + usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); + usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); + void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); + void usb_lld_start_out(USBDriver *usbp, usbep_t ep); + void usb_lld_start_in(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); + void usb_lld_pump(void *p); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB */ + +#endif /* HAL_USB_LLD_H */ + +/** @} */