From e619097f77edeceb5e8c38dc7e614c7fccd36cbf Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sun, 4 Aug 2013 13:38:53 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6073 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/halnew/platforms/STM32/USBv1/stm32_usb.h | 243 ++++++++ os/halnew/platforms/STM32/USBv1/usb_lld.c | 830 ++++++++++++++++++++++++++++ os/halnew/platforms/STM32/USBv1/usb_lld.h | 437 +++++++++++++++ 3 files changed, 1510 insertions(+) create mode 100644 os/halnew/platforms/STM32/USBv1/stm32_usb.h create mode 100644 os/halnew/platforms/STM32/USBv1/usb_lld.c create mode 100644 os/halnew/platforms/STM32/USBv1/usb_lld.h (limited to 'os/halnew/platforms/STM32/USBv1') diff --git a/os/halnew/platforms/STM32/USBv1/stm32_usb.h b/os/halnew/platforms/STM32/USBv1/stm32_usb.h new file mode 100644 index 000000000..83f349e42 --- /dev/null +++ b/os/halnew/platforms/STM32/USBv1/stm32_usb.h @@ -0,0 +1,243 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 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 stm32_usb.h + * @brief STM32 USB registers layout header. + * @note This file requires definitions from the ST STM32 header files + * stm32f10x.h or stm32l1xx.h. + * + * @addtogroup USB + * @{ + */ + +#ifndef _STM32_USB_H_ +#define _STM32_USB_H_ + +/** + * @brief Number of the available endpoints. + * @details This value does not include the endpoint 0 which is always present. + */ +#define USB_ENDOPOINTS_NUMBER 7 + +/** + * @brief USB registers block. + */ +typedef struct { + /** + * @brief Endpoint registers. + */ + volatile uint32_t EPR[USB_ENDOPOINTS_NUMBER + 1]; + /* + * @brief Reserved space. + */ + volatile uint32_t _r20[8]; + /* + * @brief Control Register. + */ + volatile uint32_t CNTR; + /* + * @brief Interrupt Status Register. + */ + volatile uint32_t ISTR; + /* + * @brief Frame Number Register. + */ + volatile uint32_t FNR; + /* + * @brief Device Address Register. + */ + volatile uint32_t DADDR; + /* + * @brief Buffer Table Address. + */ + volatile uint32_t BTABLE; +} stm32_usb_t; + +/** + * @brief USB descriptor registers block. + */ +typedef struct { + /** + * @brief TX buffer offset register. + */ + volatile uint32_t TXADDR0; + /** + * @brief TX counter register 0. + */ + volatile uint16_t TXCOUNT0; + /** + * @brief TX counter register 1. + */ + volatile uint16_t TXCOUNT1; + /** + * @brief RX buffer offset register. + */ + volatile uint32_t RXADDR0; + /** + * @brief RX counter register 0. + */ + volatile uint16_t RXCOUNT0; + /** + * @brief RX counter register 1. + */ + volatile uint16_t RXCOUNT1; +} stm32_usb_descriptor_t; + +/** + * @name Register aliases + * @{ + */ +#define RXADDR1 TXADDR0 +#define TXADDR1 RXADDR0 +/** @} */ + +/** + * @brief USB registers block numeric address. + */ +#define STM32_USB_BASE (APB1PERIPH_BASE + 0x5C00) + +/** + * @brief USB RAM numeric address. + */ +#define STM32_USBRAM_BASE (APB1PERIPH_BASE + 0x6000) + +/** + * @brief Pointer to the USB registers block. + */ +#define STM32_USB ((stm32_usb_t *)STM32_USB_BASE) + +/** + * @brief Pointer to the USB RAM. + */ +#define STM32_USBRAM ((uint32_t *)STM32_USBRAM_BASE) + +/** + * @brief Size of the dedicated packet memory. + */ +#define USB_PMA_SIZE 512 + +/** + * @brief Mask of all the toggling bits in the EPR register. + */ +#define EPR_TOGGLE_MASK (EPR_STAT_TX_MASK | EPR_DTOG_TX | \ + EPR_STAT_RX_MASK | EPR_DTOG_RX | \ + EPR_SETUP) + +#define EPR_EA_MASK 0x000F +#define EPR_STAT_TX_MASK 0x0030 +#define EPR_STAT_TX_DIS 0x0000 +#define EPR_STAT_TX_STALL 0x0010 +#define EPR_STAT_TX_NAK 0x0020 +#define EPR_STAT_TX_VALID 0x0030 +#define EPR_DTOG_TX 0x0040 +#define EPR_SWBUF_RX EPR_DTOG_TX +#define EPR_CTR_TX 0x0080 +#define EPR_EP_KIND 0x0100 +#define EPR_EP_DBL_BUF EPR_EP_KIND +#define EPR_EP_STATUS_OUT EPR_EP_KIND +#define EPR_EP_TYPE_MASK 0x0600 +#define EPR_EP_TYPE_BULK 0x0000 +#define EPR_EP_TYPE_CONTROL 0x0200 +#define EPR_EP_TYPE_ISO 0x0400 +#define EPR_EP_TYPE_INTERRUPT 0x0600 +#define EPR_SETUP 0x0800 +#define EPR_STAT_RX_MASK 0x3000 +#define EPR_STAT_RX_DIS 0x0000 +#define EPR_STAT_RX_STALL 0x1000 +#define EPR_STAT_RX_NAK 0x2000 +#define EPR_STAT_RX_VALID 0x3000 +#define EPR_DTOG_RX 0x4000 +#define EPR_SWBUF_TX EPR_DTOG_RX +#define EPR_CTR_RX 0x8000 + +#define CNTR_FRES 0x0001 +#define CNTR_PDWN 0x0002 +#define CNTR_LP_MODE 0x0004 +#define CNTR_FSUSP 0x0008 +#define CNTR_RESUME 0x0010 +#define CNTR_ESOFM 0x0100 +#define CNTR_SOFM 0x0200 +#define CNTR_RESETM 0x0400 +#define CNTR_SUSPM 0x0800 +#define CNTR_WKUPM 0x1000 +#define CNTR_ERRM 0x2000 +#define CNTR_PMAOVRM 0x4000 +#define CNTR_CTRM 0x8000 + +#define ISTR_EP_ID_MASK 0x000F +#define ISTR_DIR 0x0010 +#define ISTR_ESOF 0x0100 +#define ISTR_SOF 0x0200 +#define ISTR_RESET 0x0400 +#define ISTR_SUSP 0x0800 +#define ISTR_WKUP 0x1000 +#define ISTR_ERR 0x2000 +#define ISTR_PMAOVR 0x4000 +#define ISTR_CTR 0x8000 + +#define FNR_FN_MASK 0x07FF +#define FNR_LSOF 0x1800 +#define FNR_LCK 0x2000 +#define FNR_RXDM 0x4000 +#define FNR_RXDP 0x8000 + +#define DADDR_ADD_MASK 0x007F +#define DADDR_EF 0x0080 + +#define RXCOUNT_COUNT_MASK 0x03FF +#define TXCOUNT_COUNT_MASK 0x03FF + +#define EPR_SET(ep, epr) \ + STM32_USB->EPR[ep] = (epr) & ~EPR_TOGGLE_MASK + +#define EPR_TOGGLE(ep, epr) \ + STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] ^ ((epr) & EPR_TOGGLE_MASK)) + +#define EPR_SET_STAT_RX(ep, epr) \ + STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] & \ + ~(EPR_TOGGLE_MASK & ~EPR_STAT_RX_MASK)) ^ \ + (epr) + +#define EPR_SET_STAT_TX(ep, epr) \ + STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] & \ + ~(EPR_TOGGLE_MASK & ~EPR_STAT_TX_MASK)) ^ \ + (epr) + +#define EPR_CLEAR_CTR_RX(ep) \ + STM32_USB->EPR[ep] &= ~EPR_CTR_RX & ~EPR_TOGGLE_MASK + +#define EPR_CLEAR_CTR_TX(ep) \ + STM32_USB->EPR[ep] &= ~EPR_CTR_TX & ~EPR_TOGGLE_MASK + +/** + * @brief Returns an endpoint descriptor pointer. + */ +#define USB_GET_DESCRIPTOR(ep) \ + ((stm32_usb_descriptor_t *)((uint32_t)STM32_USBRAM_BASE + \ + (uint32_t)STM32_USB->BTABLE * 2 + \ + (uint32_t)(ep) * \ + sizeof(stm32_usb_descriptor_t))) + +/** + * @brief Converts from a PMA address to a physical address. + */ +#define USB_ADDR2PTR(addr) \ + ((uint32_t *)((addr) * 2 + STM32_USBRAM_BASE)) + +#endif /* _STM32_USB_H_ */ + +/** @} */ diff --git a/os/halnew/platforms/STM32/USBv1/usb_lld.c b/os/halnew/platforms/STM32/USBv1/usb_lld.c new file mode 100644 index 000000000..22af59866 --- /dev/null +++ b/os/halnew/platforms/STM32/USBv1/usb_lld.c @@ -0,0 +1,830 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 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 STM32/USBv1/usb_lld.c + * @brief STM32 USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define BTABLE_ADDR 0x0000 + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USB1 driver identifier.*/ +#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) +USBDriver USBD1; +#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 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Resets the packet memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + */ +static void usb_pm_reset(USBDriver *usbp) { + + /* The first 64 bytes are reserved for the descriptors table. The effective + available RAM for endpoint buffers is just 448 bytes.*/ + usbp->pmnext = 64; +} + +/** + * @brief Resets the packet memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] size size of the packet buffer to allocate + */ +static uint32_t usb_pm_alloc(USBDriver *usbp, size_t size) { + uint32_t next; + + next = usbp->pmnext; + usbp->pmnext += size; + chDbgAssert(usbp->pmnext <= USB_PMA_SIZE, "usb_pm_alloc(), #1", "PMA overflow"); + return next; +} + +/** + * @brief Reads from a dedicated packet buffer. + * + * @param[in] udp pointer to a @p stm32_usb_descriptor_t + * @param[out] buf buffer where to copy the packet data + * @param[in] n maximum number of bytes to copy. This value must + * not exceed the maximum packet size for this endpoint. + * + * @notapi + */ +static void usb_packet_read_to_buffer(stm32_usb_descriptor_t *udp, + uint8_t *buf, size_t n) { + uint32_t *pmap= USB_ADDR2PTR(udp->RXADDR0); + + n = (n + 1) / 2; + while (n > 0) { + /* Note, this line relies on the Cortex-M3/M4 ability to perform + unaligned word accesses.*/ + *(uint16_t *)buf = (uint16_t)*pmap++; + buf += 2; + n--; + } +} + +/** + * @brief Reads from a dedicated packet buffer. + * + * @param[in] udp pointer to a @p stm32_usb_descriptor_t + * @param[in] iqp pointer to an @p InputQueue object + * @param[in] n maximum number of bytes to copy. This value must + * not exceed the maximum packet size for this endpoint. + * + * @notapi + */ +static void usb_packet_read_to_queue(stm32_usb_descriptor_t *udp, + InputQueue *iqp, size_t n) { + size_t nhw; + uint32_t *pmap= USB_ADDR2PTR(udp->RXADDR0); + + nhw = n / 2; + while (nhw > 0) { + uint32_t w; + + w = *pmap++; + *iqp->q_wrptr++ = (uint8_t)w; + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + *iqp->q_wrptr++ = (uint8_t)(w >> 8); + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + nhw--; + } + /* Last byte for odd numbers.*/ + if ((n & 1) != 0) { + *iqp->q_wrptr++ = (uint8_t)*pmap; + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + } + + /* Updating queue.*/ + chSysLockFromIsr(); + iqp->q_counter += n; + while (notempty(&iqp->q_waiting)) + chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK; + chSysUnlockFromIsr(); +} + +/** + * @brief Writes to a dedicated packet buffer. + * + * @param[in] udp pointer to a @p stm32_usb_descriptor_t + * @param[in] buf buffer where to fetch the packet data + * @param[in] n maximum number of bytes to copy. This value must + * not exceed the maximum packet size for this endpoint. + * + * @notapi + */ +static void usb_packet_write_from_buffer(stm32_usb_descriptor_t *udp, + const uint8_t *buf, + size_t n) { + uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0); + + udp->TXCOUNT0 = (uint16_t)n; + n = (n + 1) / 2; + while (n > 0) { + /* Note, this line relies on the Cortex-M3/M4 ability to perform + unaligned word accesses.*/ + *pmap++ = *(uint16_t *)buf; + buf += 2; + n--; + } +} + +/** + * @brief Writes to a dedicated packet buffer. + * + * @param[in] udp pointer to a @p stm32_usb_descriptor_t + * @param[in] buf buffer where to fetch the packet data + * @param[in] n maximum number of bytes to copy. This value must + * not exceed the maximum packet size for this endpoint. + * + * @notapi + */ +static void usb_packet_write_from_queue(stm32_usb_descriptor_t *udp, + OutputQueue *oqp, size_t n) { + size_t nhw; + uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0); + + udp->TXCOUNT0 = (uint16_t)n; + nhw = n / 2; + while (nhw > 0) { + uint32_t w; + + w = (uint32_t)*oqp->q_rdptr++; + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + w |= (uint32_t)*oqp->q_rdptr++ << 8; + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + *pmap++ = w; + nhw--; + } + + /* Last byte for odd numbers.*/ + if ((n & 1) != 0) { + *pmap = (uint32_t)*oqp->q_rdptr++; + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + } + + /* Updating queue. Note, the lock is done in this unusual way because this + function can be called from both ISR and thread context so the kind + of lock function to be invoked cannot be decided beforehand.*/ + port_lock(); + dbg_enter_lock(); + + oqp->q_counter += n; + while (notempty(&oqp->q_waiting)) + chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; + + dbg_leave_lock(); + port_unlock(); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) +#if !defined(STM32_USB1_HP_HANDLER) +#error "STM32_USB1_HP_HANDLER not defined" +#endif +/** + * @brief USB high priority interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(STM32_USB1_HP_HANDLER) { + + CH_IRQ_PROLOGUE(); + + CH_IRQ_EPILOGUE(); +} + +#if !defined(STM32_USB1_LP_HANDLER) +#error "STM32_USB1_LP_HANDLER not defined" +#endif +/** + * @brief USB low priority interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { + uint32_t istr; + USBDriver *usbp = &USBD1; + + CH_IRQ_PROLOGUE(); + + istr = STM32_USB->ISTR; + + /* USB bus reset condition handling.*/ + if (istr & ISTR_RESET) { + _usb_reset(usbp); + _usb_isr_invoke_event_cb(usbp, USB_EVENT_RESET); + STM32_USB->ISTR = ~ISTR_RESET; + } + + /* USB bus SUSPEND condition handling.*/ + if (istr & ISTR_SUSP) { + STM32_USB->CNTR |= CNTR_FSUSP; + _usb_isr_invoke_event_cb(usbp, USB_EVENT_SUSPEND); +#if STM32_USB_LOW_POWER_ON_SUSPEND + STM32_USB->CNTR |= CNTR_LP_MODE; +#endif + STM32_USB->ISTR = ~ISTR_SUSP; + } + + /* USB bus WAKEUP condition handling.*/ + if (istr & ISTR_WKUP) { + uint32_t fnr = STM32_USB->FNR; + if (!(fnr & FNR_RXDP)) { + STM32_USB->CNTR &= ~CNTR_FSUSP; + _usb_isr_invoke_event_cb(usbp, USB_EVENT_WAKEUP); + } +#if STM32_USB_LOW_POWER_ON_SUSPEND + else { + /* Just noise, going back in SUSPEND mode, reference manual 22.4.5, + table 169.*/ + STM32_USB->CNTR |= CNTR_LP_MODE; + } +#endif + STM32_USB->ISTR = ~ISTR_WKUP; + } + + /* SOF handling.*/ + if (istr & ISTR_SOF) { + _usb_isr_invoke_sof_cb(usbp); + STM32_USB->ISTR = ~ISTR_SOF; + } + + /* Endpoint events handling.*/ + while (istr & ISTR_CTR) { + size_t n; + uint32_t ep; + uint32_t epr = STM32_USB->EPR[ep = istr & ISTR_EP_ID_MASK]; + const USBEndpointConfig *epcp = usbp->epc[ep]; + + if (epr & EPR_CTR_TX) { + size_t transmitted; + /* IN endpoint, transmission.*/ + EPR_CLEAR_CTR_TX(ep); + + transmitted = (size_t)USB_GET_DESCRIPTOR(ep)->TXCOUNT0; + epcp->in_state->txcnt += transmitted; + n = epcp->in_state->txsize - epcp->in_state->txcnt; + if (n > 0) { + /* Transfer not completed, there are more packets to send.*/ + if (n > epcp->in_maxsize) + n = epcp->in_maxsize; + + if (epcp->in_state->txqueued) + usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep), + epcp->in_state->mode.queue.txqueue, + n); + else { + epcp->in_state->mode.linear.txbuf += transmitted; + usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep), + epcp->in_state->mode.linear.txbuf, + n); + } + chSysLockFromIsr(); + usb_lld_start_in(usbp, ep); + chSysUnlockFromIsr(); + } + else { + /* Transfer completed, invokes the callback.*/ + _usb_isr_invoke_in_cb(usbp, ep); + } + } + if (epr & EPR_CTR_RX) { + EPR_CLEAR_CTR_RX(ep); + /* OUT endpoint, receive.*/ + if (epr & EPR_SETUP) { + /* Setup packets handling, setup packets are handled using a + specific callback.*/ + _usb_isr_invoke_setup_cb(usbp, ep); + } + else { + stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); + n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; + + /* Reads the packet into the defined buffer.*/ + if (epcp->out_state->rxqueued) + usb_packet_read_to_queue(udp, + epcp->out_state->mode.queue.rxqueue, + n); + else { + usb_packet_read_to_buffer(udp, + epcp->out_state->mode.linear.rxbuf, + n); + epcp->out_state->mode.linear.rxbuf += n; + } + /* Transaction data updated.*/ + epcp->out_state->rxcnt += n; + epcp->out_state->rxsize -= n; + epcp->out_state->rxpkts -= 1; + + /* The transaction is completed if the specified number of packets + has been received or the current packet is a short packet.*/ + if ((n < epcp->out_maxsize) || (epcp->out_state->rxpkts == 0)) { + /* Transfer complete, invokes the callback.*/ + _usb_isr_invoke_out_cb(usbp, ep); + } + else { + /* Transfer not complete, there are more packets to receive.*/ + EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); + } + } + } + istr = STM32_USB->ISTR; + } + + CH_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + + /* Driver initialization.*/ + usbObjectInit(&USBD1); +} + +/** + * @brief Configures and activates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + + if (usbp->state == USB_STOP) { + /* Clock activation.*/ +#if STM32_USB_USE_USB1 + if (&USBD1 == usbp) { + /* USB clock enabled.*/ + rccEnableUSB(FALSE); + /* Powers up the transceiver while holding the USB in reset state.*/ + STM32_USB->CNTR = CNTR_FRES; + /* Enabling the USB IRQ vectors, this also gives enough time to allow + the transceiver power up (1uS).*/ + nvicEnableVector(STM32_USB1_HP_NUMBER, + CORTEX_PRIORITY_MASK(STM32_USB_USB1_HP_IRQ_PRIORITY)); + nvicEnableVector(STM32_USB1_LP_NUMBER, + CORTEX_PRIORITY_MASK(STM32_USB_USB1_LP_IRQ_PRIORITY)); + /* Releases the USB reset.*/ + STM32_USB->CNTR = 0; + } +#endif + /* Reset procedure enforced on driver start.*/ + _usb_reset(usbp); + } + /* Configuration.*/ +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + + /* If in ready state then disables the USB clock.*/ + if (usbp->state == USB_STOP) { +#if STM32_USB_USE_USB1 + if (&USBD1 == usbp) { + nvicDisableVector(STM32_USB1_HP_NUMBER); + nvicDisableVector(STM32_USB1_LP_NUMBER); + STM32_USB->CNTR = CNTR_PDWN | CNTR_FRES; + rccDisableUSB(FALSE); + } +#endif + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + uint32_t cntr; + + /* Post reset initialization.*/ + STM32_USB->BTABLE = 0; + STM32_USB->ISTR = 0; + STM32_USB->DADDR = DADDR_EF; + cntr = /*CNTR_ESOFM | */ CNTR_RESETM | CNTR_SUSPM | + CNTR_WKUPM | /*CNTR_ERRM | CNTR_PMAOVRM |*/ CNTR_CTRM; + /* The SOF interrupt is only enabled if a callback is defined for + this service because it is an high rate source.*/ + if (usbp->config->sof_cb != NULL) + cntr |= CNTR_SOFM; + STM32_USB->CNTR = cntr; + + /* Resets the packet memory allocator.*/ + usb_pm_reset(usbp); + + /* EP0 initialization.*/ + usbp->epc[0] = &ep0config; + usb_lld_init_endpoint(usbp, 0); +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + + STM32_USB->DADDR = (uint32_t)(usbp->address) | DADDR_EF; +} + +/** + * @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) { + uint16_t nblocks, epr; + stm32_usb_descriptor_t *dp; + const USBEndpointConfig *epcp = usbp->epc[ep]; + + /* Setting the endpoint type.*/ + switch (epcp->ep_mode & USB_EP_MODE_TYPE) { + case USB_EP_MODE_TYPE_ISOC: + epr = EPR_EP_TYPE_ISO; + break; + case USB_EP_MODE_TYPE_BULK: + epr = EPR_EP_TYPE_BULK; + break; + case USB_EP_MODE_TYPE_INTR: + epr = EPR_EP_TYPE_INTERRUPT; + break; + default: + epr = EPR_EP_TYPE_CONTROL; + } + + /* IN endpoint initially in NAK mode.*/ + if (epcp->in_cb != NULL) + epr |= EPR_STAT_TX_NAK; + + /* OUT endpoint initially in NAK mode.*/ + if (epcp->out_cb != NULL) + epr |= EPR_STAT_RX_NAK; + + /* EPxR register setup.*/ + EPR_SET(ep, epr | ep); + EPR_TOGGLE(ep, epr); + + /* Endpoint size and address initialization.*/ + if (epcp->out_maxsize > 62) + nblocks = (((((epcp->out_maxsize - 1) | 0x1f) + 1) / 32) << 10) | + 0x8000; + else + nblocks = ((((epcp->out_maxsize - 1) | 1) + 1) / 2) << 10; + dp = USB_GET_DESCRIPTOR(ep); + dp->TXCOUNT0 = 0; + dp->RXCOUNT0 = nblocks; + dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize); + dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize); +} + +/** + * @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) { + unsigned i; + + /* Resets the packet memory allocator.*/ + usb_pm_reset(usbp); + + /* Disabling all endpoints.*/ + for (i = 1; i <= USB_ENDOPOINTS_NUMBER; i++) { + EPR_TOGGLE(i, 0); + EPR_SET(i, 0); + } +} + +/** + * @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) { + + (void)usbp; + switch (STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) { + case EPR_STAT_RX_DIS: + return EP_STATUS_DISABLED; + case EPR_STAT_RX_STALL: + return EP_STATUS_STALLED; + default: + 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) { + + (void)usbp; + switch (STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) { + case EPR_STAT_TX_DIS: + return EP_STATUS_DISABLED; + case EPR_STAT_TX_STALL: + return EP_STATUS_STALLED; + default: + 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) { + uint32_t *pmap; + stm32_usb_descriptor_t *udp; + uint32_t n; + + (void)usbp; + udp = USB_GET_DESCRIPTOR(ep); + pmap = USB_ADDR2PTR(udp->RXADDR0); + for (n = 0; n < 4; n++) { + *(uint16_t *)buf = (uint16_t)*pmap++; + buf += 2; + } +} + +/** + * @brief Prepares for a receive operation. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_prepare_receive(USBDriver *usbp, usbep_t ep) { + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + /* Transfer initialization.*/ + if (osp->rxsize == 0) /* Special case for zero sized packets.*/ + osp->rxpkts = 1; + else + osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / + usbp->epc[ep]->out_maxsize); +} + +/** + * @brief Prepares for a transmit operation. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep) { + size_t n; + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + /* Transfer initialization.*/ + n = isp->txsize; + if (n > (size_t)usbp->epc[ep]->in_maxsize) + n = (size_t)usbp->epc[ep]->in_maxsize; + + if (isp->txqueued) + usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep), + isp->mode.queue.txqueue, n); + else + usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep), + isp->mode.linear.txbuf, n); +} + +/** + * @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) { + + (void)usbp; + + EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); +} + +/** + * @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) { + + (void)usbp; + + EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); +} + +/** + * @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) { + + (void)usbp; + + EPR_SET_STAT_RX(ep, EPR_STAT_RX_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) { + + (void)usbp; + + EPR_SET_STAT_TX(ep, EPR_STAT_TX_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) { + + (void)usbp; + + /* Makes sure to not put to NAK an endpoint that is already + transferring.*/ + if ((STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) != EPR_STAT_RX_VALID) + EPR_SET_STAT_TX(ep, EPR_STAT_RX_NAK); +} + +/** + * @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) { + + (void)usbp; + + /* Makes sure to not put to NAK an endpoint that is already + transferring.*/ + if ((STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) != EPR_STAT_TX_VALID) + EPR_SET_STAT_TX(ep, EPR_STAT_TX_NAK); +} + +#endif /* HAL_USE_USB */ + +/** @} */ diff --git a/os/halnew/platforms/STM32/USBv1/usb_lld.h b/os/halnew/platforms/STM32/USBv1/usb_lld.h new file mode 100644 index 000000000..a0092a334 --- /dev/null +++ b/os/halnew/platforms/STM32/USBv1/usb_lld.h @@ -0,0 +1,437 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 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 STM32/USBv1/usb_lld.h + * @brief STM32 USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef _USB_LLD_H_ +#define _USB_LLD_H_ + +#if HAL_USE_USB || defined(__DOXYGEN__) + +#include "stm32_usb.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Maximum endpoint address. + */ +#define USB_MAX_ENDPOINTS USB_ENDOPOINTS_NUMBER + +/** + * @brief This device requires the address change after the status packet. + */ +#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief USB1 driver enable switch. + * @details If set to @p TRUE the support for USB1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_USB_USE_USB1) || defined(__DOXYGEN__) +#define STM32_USB_USE_USB1 FALSE +#endif + +/** + * @brief Enables the USB device low power mode on suspend. + */ +#if !defined(STM32_USB_LOW_POWER_ON_SUSPEND) || defined(__DOXYGEN__) +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#endif + +/** + * @brief USB1 interrupt priority level setting. + */ +#if !defined(STM32_USB_USB1_HP_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_USB1_HP_IRQ_PRIORITY 13 +#endif + +/** + * @brief USB1 interrupt priority level setting. + */ +#if !defined(STM32_USB_USB1_LP_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_USB1_LP_IRQ_PRIORITY 14 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 && !STM32_HAS_USB +#error "USB not present in the selected device" +#endif + +#if !STM32_USB_USE_USB1 +#error "USB driver activated but no USB peripheral assigned" +#endif + +#if STM32_USB_USE_USB1 && \ + !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_USB1_HP_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USB HP" +#endif + +#if STM32_USB_USE_USB1 && \ + !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_USB1_LP_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USB LP" +#endif + +#if STM32_USBCLK != 48000000 +#error "the USB driver requires a 48MHz clock" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of an IN endpoint state structure. + */ +typedef struct { + /** + * @brief Buffer mode, queue or linear. + */ + bool_t txqueued; + /** + * @brief Requested transmit transfer size. + */ + size_t txsize; + /** + * @brief Transmitted bytes so far. + */ + size_t txcnt; + union { + struct { + /** + * @brief Pointer to the transmission linear buffer. + */ + const uint8_t *txbuf; + } linear; + struct { + /** + * @brief Pointer to the output queue. + */ + OutputQueue *txqueue; + } queue; + /* End of the mandatory fields.*/ + } mode; +} USBInEndpointState; + +/** + * @brief Type of an OUT endpoint state structure. + */ +typedef struct { + /** + * @brief Buffer mode, queue or linear. + */ + bool_t rxqueued; + /** + * @brief Requested receive transfer size. + */ + size_t rxsize; + /** + * @brief Received bytes so far. + */ + size_t rxcnt; + union { + struct { + /** + * @brief Pointer to the receive linear buffer. + */ + uint8_t *rxbuf; + } linear; + struct { + /** + * @brief Pointer to the input queue. + */ + InputQueue *rxqueue; + } queue; + } mode; + /* End of the mandatory fields.*/ + /** + * @brief Number of packets to receive. + */ + uint16_t rxpkts; +} 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 the IN endpoint is not + * used. + */ + usbepcallback_t in_cb; + /** + * @brief OUT endpoint notification callback. + * @details This field must be set to @p NULL if the OUT endpoint is not + * used. + */ + 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 structure maintains the state of the IN endpoint. + */ + USBInEndpointState *in_state; + /** + * @brief @p USBEndpointState associated to the OUT endpoint. + * @details This structure maintains the state of the OUT endpoint. + */ + USBOutEndpointState *out_state; + /* End of the mandatory fields.*/ + /** + * @brief Reserved field, not currently used. + * @note Initialize this field to 1 in order to be forward compatible. + */ + uint16_t ep_buffers; + /** + * @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; +#if defined(USB_DRIVER_EXT_FIELDS) + USB_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the next address in the packet memory. + */ + uint32_t pmnext; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the current frame number. + * + * @param[in] usbp pointer to the @p USBDriver object + * @return The current frame number. + * + * @notapi + */ +#define usb_lld_get_frame_number(usbp) (STM32_USB->FNR & FNR_FN_MASK) + +/** + * @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 Returns the exact size of a received packet. + * @pre The OUT endpoint must have been configured in packet 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_packet_size(usbp, ep) \ + ((size_t)USB_GET_DESCRIPTOR(ep)->RXCOUNT & RXCOUNT_COUNT_MASK) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 && !defined(__DOXYGEN__) +extern USBDriver USBD1; +#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_prepare_receive(USBDriver *usbp, usbep_t ep); + void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep); + 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); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB */ + +#endif /* _USB_LLD_H_ */ + +/** @} */ -- cgit v1.2.3