diff options
Diffstat (limited to 'os/hal/platforms/Rx62n/usb_lld.c')
-rw-r--r-- | os/hal/platforms/Rx62n/usb_lld.c | 983 |
1 files changed, 983 insertions, 0 deletions
diff --git a/os/hal/platforms/Rx62n/usb_lld.c b/os/hal/platforms/Rx62n/usb_lld.c new file mode 100644 index 000000000..f5b7a965f --- /dev/null +++ b/os/hal/platforms/Rx62n/usb_lld.c @@ -0,0 +1,983 @@ +/*
+ 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 RX62N/usb_lld.c
+ * @brief RX62N USB Driver subsystem low level driver source.
+ *
+ * @addtogroup USB
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+
+#if HAL_USE_USB || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief PIPEnCTR register union.
+ */
+typedef union {
+ unsigned short WORD;
+ struct {
+ unsigned short PID:2;
+ unsigned short :3;
+ unsigned short PBUSY:1;
+ unsigned short SQMON:1;
+ unsigned short SQSET:1;
+ unsigned short SQCLR:1;
+ unsigned short ACLRM:1;
+ unsigned short ATREPM:1;
+ unsigned short :3;
+ unsigned short INBUFM:1;
+ unsigned short BSTS:1;
+ } BIT;
+} pipectr_t;;
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief USB1 driver identifier.*/
+#if RX62N_USE_USB0 || defined(__DOXYGEN__)
+USBDriver USBD1;
+#endif
+
+/** @brief USB2 driver identifier.*/
+#if RX62N_USE_USB1 || defined(__DOXYGEN__)
+USBDriver USBD2;
+#error "USBD2 is not supported yet"
+#endif
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/**
+ * @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,
+ USB_EP0_PACKET_SIZE,
+ USB_EP0_PACKET_SIZE,
+ &ep0_state.in,
+ &ep0_state.out,
+ 1,
+ ep0setup_buffer
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+pipectr_t usb_pipectr_read(USBDriver *usbp, usbep_t ep) {
+ volatile pipectr_t *pipe = (pipectr_t *)&usbp->usb->PIPE1CTR;
+
+ return pipe[ep-1];
+}
+
+void usb_pipectr_write(USBDriver *usbp, usbep_t ep, uint16_t p) {
+ volatile pipectr_t *pipe = (pipectr_t *)&usbp->usb->PIPE1CTR;
+
+ pipe[ep-1].WORD = p;
+}
+
+/**
+ * @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(USBDriver *usbp, usbep_t ep,
+ uint8_t *buf, size_t n) {
+ uint32_t i;
+
+ usbp->epc[ep]->in_state->transmitted = n;
+ if (ep == 0) {
+ for (i = 0; i < n; i++)
+ usbp->epc[ep]->out_state->mode.linear.rxbuf[i] = usbp->usb->CFIFO.BYTE.L;
+ }
+ else {
+ for (i = 0; i < n; i++)
+ usbp->epc[ep]->out_state->mode.linear.rxbuf[i] = usbp->usb->D0FIFO.BYTE.L;
+ }
+}
+
+/**
+ * @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(USBDriver *usbp, usbep_t ep, InputQueue *iqp, size_t n) {
+ size_t nhw = n;
+ uint8_t c;
+
+ while (nhw > 0) {
+ if (ep == 0) {
+ c = usbp->usb->CFIFO.BYTE.L;
+ }
+ else {
+ /* ep 1..9 */
+ c = usbp->usb->D0FIFO.BYTE.L;;
+ }
+ *iqp->q_wrptr++ = c;
+ if (iqp->q_wrptr >= iqp->q_top)
+ iqp->q_wrptr = iqp->q_buffer;
+ nhw--;
+ }
+
+ /* 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] 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(USBDriver *usbp, usbep_t ep,
+ const uint8_t *buf, size_t n) {
+
+ usbp->epc[ep]->in_state->transmitted = n;
+ if (ep == 0) {
+ while (n--) {
+ usbp->usb->CFIFO.BYTE.L = *buf++;
+ }
+ }
+ else {
+ /* ep 1..9 */
+ usbp->usb->D1FIFO.BYTE.L = *buf++;
+ }
+}
+
+/**
+ * @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(USBDriver *usbp, usbep_t ep,
+ OutputQueue *oqp, size_t n) {
+ size_t nhw = n;
+ uint8_t c;
+
+ usbp->epc[ep]->in_state->transmitted = n;
+ while (nhw > 0) {
+ c = *oqp->q_rdptr++;;
+ if (ep == 0) {
+ usbp->usb->CFIFO.BYTE.L = c;
+ }
+ else {
+ /* ep 1..9 */
+ usbp->usb->D1FIFO.BYTE.L = c;
+ }
+ c = c;
+ if (oqp->q_rdptr >= oqp->q_top)
+ oqp->q_rdptr = oqp->q_buffer;
+ nhw--;
+ }
+
+ /* 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.*/
+
+ /*
+ TODO: port_lock() doesn't work.*/
+ if (port_enabled()) {
+ dbg_enter_lock();
+ chSysLock();
+
+ oqp->q_counter += n;
+ while (notempty(&oqp->q_waiting))
+ chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK;
+
+ chSysUnlock();
+ dbg_leave_lock();
+ }
+ else {
+ oqp->q_counter += n;
+ while (notempty(&oqp->q_waiting))
+ chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK;
+ }
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if RX62N_USE_USB0 || defined(__DOXYGEN__)
+void usb_lld_serve_interrupt(USBDriver *usbp) {
+ uint16_t sts0, sts0clr;
+ uint16_t ep = 0;
+
+ sts0 = usbp->usb->INTSTS0.WORD;
+ sts0clr = (~(sts0 & usbp->usb->INTENB0.WORD))|
+ INTSTS0_VALID|INTSTS0_DVST|INTSTS0_CTRT;
+
+ if (sts0 & INTSTS0_RESM) {
+/* _usb_isr_invoke_event_cb(usbp, USB_EVENT_RESUME);*/
+ }
+
+ /* USB bus WAKEUP condition handling.*/
+/*
+ if (isr & AT91C_UDP_WAKEUP) {
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_WAKEUP);
+ }
+*/
+ /* SOF handling.*/
+/*
+ if (isr & AT91C_UDP_SOFINT) {
+ _usb_isr_invoke_sof_cb(usbp);
+ }
+*/
+ if (sts0 & INTSTS0_DVST) {
+ usbp->usb->INTSTS0.BIT.DVST = 0;
+ switch (INTSTS0_DVSQ(sts0)) {
+ case DVSQ_POWERED:
+ break;
+ case DVSQ_DEFAULT:
+ if ( !(sts0 & INTSTS0_RESM) && (usbp->last_dvst != DVSQ_ADDRESS) &&
+ (usbp->last_dvst != DVSQ_SUSPENDED_DEF)) {
+ _usb_reset(usbp);
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_RESET);
+ }
+ break;
+ case DVSQ_ADDRESS:
+ break;
+ case DVSQ_CONFIGURED:
+ break;
+ default:
+ /* Default is suspened state */
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_SUSPEND);
+ break;
+ }
+ usbp->last_dvst = INTSTS0_DVSQ(sts0);
+ }
+
+ if (sts0 & INTSTS0_CTRT) {
+ switch (INTSTS0_CTSQ(sts0)) {
+ case CTSQ_IDLE_OR_SETUP:
+ if (usbp->last_ctsq == CTSQ_READ_STATUS) {
+ _usb_isr_invoke_out_cb(usbp, ep);
+ }
+ else if (usbp->last_ctsq == CTSQ_WRITE_STATUS) {
+ _usb_isr_invoke_in_cb(usbp, ep);
+ }
+ else if (usbp->last_ctsq == CTSQ_NODATA_STATUS) {
+ _usb_isr_invoke_in_cb(usbp, ep);
+ }
+ break;
+ default:
+ break;
+ }
+ usbp->last_ctsq = INTSTS0_CTSQ(sts0);
+ usbp->usb->INTSTS0.BIT.CTRT = 0;
+ }
+
+ if (sts0 & INTSTS0_VALID) {
+ usbp->usb->INTSTS0.BIT.VALID = 0;
+ _usb_isr_invoke_setup_cb(usbp, ep);
+ if (INTSTS0_CTSQ(sts0) == CTSQ_NODATA_STATUS) {
+ usbp->usb->DCPCTR.BIT.PID = PID_BUF;
+ usbp->usb->DCPCTR.BIT.CCPL = 1;
+ }
+ }
+
+ if (sts0 & INTSTS0_BRDY) {
+ /* BRDY */
+ uint16_t brdysts = usbp->usb->BRDYSTS.WORD;
+ const USBEndpointConfig *epcp;
+ size_t n;
+ for (ep=0; ep<USB_MAX_ENDPOINTS; ep++) {
+ if (brdysts & (1<<ep)) {
+ epcp = usbp->epc[ep];
+ if (ep == 0) {
+ n = usbp->usb->CFIFOCTR.BIT.DTLN;
+ }
+ else {
+ if (epcp->out_state) {
+ usbp->usb->PIPESEL.BIT.PIPESEL = ep;
+ do {
+ usbp->usb->D0FIFOSEL.WORD = ep;
+ } while (usbp->usb->D0FIFOSEL.WORD != ep);
+ while (!usbp->usb->D0FIFOCTR.BIT.FRDY);
+ n = usbp->usb->D0FIFOCTR.BIT.DTLN;
+ }
+ else {
+ /* in_state */
+ usbp->usb->PIPESEL.BIT.PIPESEL = ep;
+ do {
+ usbp->usb->D1FIFOSEL.WORD = ep;
+ } while (usbp->usb->D1FIFOSEL.WORD != ep);
+ while (!usbp->usb->D1FIFOCTR.BIT.FRDY);
+ n = 0; /* Omit compiler warning */
+ }
+ }
+ if (epcp->out_state) {
+ /* Reads the packet into the defined buffer.*/
+ if (epcp->out_state->rxqueued) {
+ usb_packet_read_to_queue(usbp, ep,
+ epcp->out_state->mode.queue.rxqueue, n);
+ }
+ else {
+ usb_packet_read_to_buffer(usbp, ep,
+ 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;
+ if (epcp->out_state->rxpkts > 0) {
+ /* Transfer not completed, there are more packets to receive.*/
+ }
+ else {
+ /* Transfer completed, invokes the callback.*/
+ /* Set pipe to NAK */
+ usb_pipectr_write(usbp, ep, PID_NAK);
+ _usb_isr_invoke_out_cb(usbp, ep);
+ }
+ }
+ else {
+ epcp->in_state->txcnt += epcp->in_state->transmitted;
+ epcp->in_state->txsize -= epcp->in_state->transmitted;
+ if (epcp->in_state->txsize > 0) {
+ /* Transfer not completed, there are more packets to send.*/
+ if (epcp->in_state->txsize > epcp->in_maxsize)
+ n = epcp->in_maxsize;
+ else
+ n = epcp->in_state->txsize;
+
+ if (epcp->in_state->txqueued) {
+ usb_packet_write_from_queue(usbp, ep,
+ epcp->in_state->mode.queue.txqueue, n);
+ }
+ else {
+ epcp->in_state->mode.linear.txbuf += epcp->in_state->transmitted;
+ usb_packet_write_from_buffer(usbp, 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);
+ }
+ }
+ }
+ }
+ usbp->usb->BRDYSTS.WORD = ~brdysts;
+ }
+ if (sts0 & INTSTS0_BEMP) {
+ /* BEMP */
+ uint16_t bempsts = usbp->usb->BEMPSTS.WORD;
+ const USBEndpointConfig *epcp;
+ size_t n;
+ ep = 0;
+ epcp = usbp->epc[ep];
+ epcp->in_state->txcnt += epcp->in_state->transmitted;
+ epcp->in_state->txsize -= epcp->in_state->transmitted;
+ if (epcp->in_state->txsize > 0) {
+ /* Transfer not completed, there are more packets to send.*/
+ if (epcp->in_state->txsize > epcp->in_maxsize)
+ n = epcp->in_maxsize;
+ else
+ n = epcp->in_state->txsize;
+ if (epcp->in_state->txqueued) {
+ usb_packet_write_from_queue(usbp, ep,
+ epcp->in_state->mode.queue.txqueue, n);
+ }
+ else {
+ epcp->in_state->mode.linear.txbuf += epcp->in_state->transmitted;
+ usb_packet_write_from_buffer(usbp, 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);
+ }
+ usbp->usb->BEMPSTS.WORD = ~bempsts;
+ }
+ usbp->usb->INTSTS0.WORD = sts0clr;
+}
+#endif
+
+/**
+ * @brief USB interrupt handler.
+ *
+ * @isr
+ */
+#if RX62N_USE_USB0 || defined(__DOXYGEN__)
+CH_IRQ_HANDLER(Excep_USB0_USBI0) {
+
+ CH_IRQ_PROLOGUE();
+ usb_lld_serve_interrupt(&USBD1);
+ CH_IRQ_EPILOGUE();
+}
+#endif
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level USB driver initialization.
+ *
+ * @notapi
+ */
+void usb_lld_init(void) {
+
+#if RX62N_USE_USB0 || defined(__DOXYGEN__)
+ usbObjectInit(&USBD1);
+ USBD1.usb = &USB0;
+#endif
+#if RX62N_USE_USB1 || defined(__DOXYGEN__)
+ usbObjectInit(&USBD2);
+ USBD2.usb = &USB1;
+#endif
+}
+
+/**
+ * @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) {
+ /* Enable USB peripheral. */
+ if (usbp == &USBD1) {
+ MSTP(USB0) = 0;
+ /* Enable GPIO Pull-up */
+ PORT1.DDR.BIT.B4 = 1;
+ /* Enable USB0 pins functions */
+ IOPORT.PFKUSB.BYTE = 0x14;
+ }
+ /* USB clock activation. */
+ usbp->usb->SYSCFG.BIT.SCKE = 1;
+ /* Select function. */
+ usbp->usb->SYSCFG.BIT.DCFM = 0;
+ /* Enable module operation. */
+ usbp->usb->SYSCFG.BIT.USBE = 1;
+
+ /* Default as powered state */
+ usbp->last_dvst = 0;
+ /* Default as idle stage */
+ usbp->last_ctsq = 0;
+ /* Current FIFO */
+ usbp->current_fifo = 0;
+
+ /* 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) {
+/* IEN(USB0,USBI0) = 0;*/
+ /* Disable module operation. */
+ usbp->usb->SYSCFG.BIT.USBE = 0;
+ /* Disable USB clock.*/
+ usbp->usb->SYSCFG.BIT.SCKE = 0;
+ /* Disable USB peripheral.*/
+ if (usbp == &USBD1) {
+ MSTP(USB0) = 1;
+ }
+ }
+}
+
+/**
+ * @brief USB low level reset routine.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_reset(USBDriver *usbp) {
+ uint16_t ien = 0;
+
+ if (usbp == &USBD1) {
+ IEN(USB0,USBI0) = 0;
+ }
+ /* Disable interrupts */
+ usbp->usb->INTENB0.WORD = 0;
+ usbp->usb->INTENB1.WORD = 0;
+ usbp->usb->BRDYENB.WORD = 0;
+ usbp->usb->NRDYENB.WORD = 0;
+ usbp->usb->BEMPENB.WORD = 0;
+ /* Clear interrupt status bits */
+ usbp->usb->INTSTS0.WORD = (uint16_t)(~(INTSTS0_VALID|INTSTS0_BRDY|
+ INTSTS0_BEMP|INTSTS0_CTRT|INTSTS0_DVST|INTSTS0_SOFR|INTSTS0_RESM|
+ INTSTS0_VBINT));
+ usbp->usb->INTSTS1.WORD = (uint16_t)(~((1<<4)|(1<<5)|(1<<6)|(1<<11)|(1<<12)|
+ (1<<14)|(1<<15)));
+
+ if (usbp->config->sof_cb != NULL) {
+ ien = INTENB0_SOFE;
+ }
+
+ /* EP0 initialization.*/
+ usbp->epc[0] = &ep0config;
+ usbp->usb->DCPCFG.WORD = 0;
+ usbp->usb->DCPMAXP.WORD = USB_EP0_PACKET_SIZE;
+ usbp->usb->CFIFOSEL.WORD = 0;
+
+ /* Enable the USB interrupts - other interrupts get enabled as the
+ enumeration process progresses. */
+ if (usbp == &USBD1) {
+ IEN(USB0,USBI0) = 1;
+ IPR(USB0,USBI0) = RX62N_USB0_IRQ_PRIORITY;
+ }
+ usbp->usb->INTENB0.WORD =
+ INTENB0_BRDYE|
+ INTENB0_BEMPE|
+ INTENB0_CTRE|
+ INTENB0_DVSE|
+ INTENB0_RSME|
+ ien|
+ 0;
+ usbp->usb->BRDYENB.BIT.PIPE0BRDYE = 1;
+ usbp->usb->BEMPENB.BIT.PIPE0BEMPE = 1;
+}
+
+/**
+ * @brief Sets the USB address.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_set_address(USBDriver *usbp) {
+
+ (void)usbp;
+}
+
+/**
+ * @brief Finalizes the USB configuration process.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_end_configuration(USBDriver *usbp) {
+
+ (void)usbp;
+}
+
+/**
+ * @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 pipecfg;
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+
+ chDbgAssert(ep > 0, "usb_lld_init_endpoint(), #1", "invalid endpoint");
+ if (ep == 0)
+ return;
+
+/* usbp->usb->CFIFOSEL.BIT.CURPIPE = 0;*/
+ usbp->usb->PIPESEL.WORD = ep;
+
+ /* Set PID to NAK */
+ usb_pipectr_write(usbp, ep, 0);
+ while (usb_pipectr_read(usbp, ep).BIT.PBUSY);
+ usb_pipectr_write(usbp, ep, PIPECTR_SQCLR|PIPECTR_ACLRM);
+ usb_pipectr_write(usbp, ep, 0);
+/*
+ if (epcp->out_cb != NULL)
+ epcp->out_state->currentBank = 0;
+*/
+ /* Setting the endpoint type.*/
+ switch (epcp->ep_mode & USB_EP_MODE_TYPE) {
+ case USB_EP_MODE_TYPE_ISOC:
+ pipecfg = PIPECFG_TYPE_ISO;
+ break;
+ case USB_EP_MODE_TYPE_BULK:
+ pipecfg = PIPECFG_TYPE_BULK;
+ break;
+ case USB_EP_MODE_TYPE_INTR:
+ pipecfg = PIPECFG_TYPE_INT;
+ break;
+ default:
+ /* No control type allowed */
+ chDbgAssert((epcp->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_CTRL,
+ "usb_lld_init_endpoint(), #2", "invalid endpoint");
+ return;
+ }
+
+ /* Set direction */
+ if (epcp->in_cb != NULL) {
+ usbp->usb->PIPEMAXP.BIT.MXPS = epcp->in_maxsize;
+ pipecfg |= PIPECFG_DIR_IN;
+ }
+ else {
+ usbp->usb->PIPEMAXP.BIT.MXPS = epcp->out_maxsize;
+ pipecfg |= PIPECFG_DIR_OUT;
+ }
+ pipecfg |= ep;
+ usbp->usb->PIPECFG.WORD = pipecfg;
+
+ /* Clear interrupt status flag */
+ usbp->usb->BRDYSTS.WORD &= ~(1<<ep);
+ /* Enable interrupts */
+ usbp->usb->BRDYENB.WORD |= (1<<ep);
+
+ usbp->usb->PIPESEL.WORD = 0;
+}
+
+/**
+ * @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) {
+ usbep_t i;
+
+ /* Disable endpoints interrupt */
+ usbp->usb->BRDYENB.WORD = 1;
+ usbp->usb->BRDYSTS.WORD = ~1;
+ /* Disable endpoints */
+ for (i=0; i<USB_MAX_ENDPOINTS; i++) {
+ usbp->usb->PIPESEL.WORD = i+1;
+ usbp->usb->PIPECFG.BIT.EPNUM = 0;
+ }
+ usbp->usb->PIPESEL.WORD = 0;
+ /* Clear FIFO */
+}
+
+/**
+ * @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) {
+ usbep_t epPrev = usbp->usb->PIPESEL.BIT.PIPESEL;
+
+ if (ep == 0)
+ return EP_STATUS_ACTIVE;
+ usbp->usb->PIPESEL.BIT.PIPESEL = ep;
+ usbep_t epNum = usbp->usb->PIPECFG.BIT.EPNUM;
+ usbp->usb->PIPESEL.BIT.PIPESEL = epPrev;
+ if (epNum) {
+ if (usb_pipectr_read(usbp, ep).BIT.PID & 2)
+ return EP_STATUS_STALLED;
+ }
+ return EP_STATUS_DISABLED;
+}
+
+/**
+ * @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) {
+
+ return usb_lld_get_status_out(usbp, ep);
+}
+
+/**
+ * @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) {
+
+ buf[0] = usbp->usb->USBREQ.WORD & 0xFF;
+ buf[1] = (usbp->usb->USBREQ.WORD >> 8) & 0xFF;
+ buf[2] = usbp->usb->USBVAL & 0xFF;
+ buf[3] = (usbp->usb->USBVAL >> 8) & 0xFF;
+ buf[4] = usbp->usb->USBINDX & 0xFF;
+ buf[5] = (usbp->usb->USBINDX >> 8) & 0xFF;
+ buf[6] = usbp->usb->USBLENG & 0xFF;
+ buf[7] = (usbp->usb->USBLENG >> 8) & 0xFF;
+}
+
+/**
+ * @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 (ep == 0) {
+ do {
+ usbp->usb->CFIFOSEL.BIT.ISEL = 0;
+ } while (usbp->usb->CFIFOSEL.BIT.ISEL);
+ usbp->usb->DCPCTR.BIT.PID = PID_BUF;
+ while (!usbp->usb->CFIFOCTR.BIT.FRDY);
+ }
+ 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 (ep == 0) {
+ do {
+ usbp->usb->CFIFOSEL.BIT.ISEL = 1;
+ } while (!usbp->usb->CFIFOSEL.BIT.ISEL);
+ usbp->usb->DCPCTR.BIT.PID = PID_BUF;
+ while (!usbp->usb->CFIFOCTR.BIT.FRDY);
+ if (n == 0)
+ usbp->usb->CFIFOCTR.BIT.BCLR = 1;
+ }
+ else {
+ usbp->usb->PIPESEL.BIT.PIPESEL = ep;
+ do {
+ usbp->usb->D1FIFOSEL.WORD = ep;
+ } while (usbp->usb->D1FIFOSEL.WORD != ep);
+ while (!usbp->usb->D1FIFOCTR.BIT.FRDY);
+ do {
+ usb_pipectr_write(usbp, ep, PID_BUF);
+ } while (usb_pipectr_read(usbp, ep).BIT.PID != PID_BUF);
+ }
+ if (isp->txqueued) {
+ usb_packet_write_from_queue(usbp, ep, isp->mode.queue.txqueue, n);
+ }
+ else {
+ usb_packet_write_from_buffer(usbp, 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) {
+
+ if (ep)
+ usb_pipectr_write(usbp, ep, PID_BUF);
+}
+
+/**
+ * @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) {
+
+ if (ep == 0) {
+ if (usbp->epc[ep]->in_state->transmitted < usbp->epc[ep]->in_maxsize)
+ usbp->usb->CFIFOCTR.BIT.BVAL = 1;
+ }
+ else {
+ if (usbp->epc[ep]->in_state->transmitted < usbp->epc[ep]->in_maxsize)
+ usbp->usb->D1FIFOCTR.BIT.BVAL = 1;
+ }
+}
+
+/**
+ * @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) {
+
+ if (ep == 0) {
+ if (usbp->usb->DCPCTR.BIT.PID == PID_NAK)
+ usbp->usb->DCPCTR.BIT.PID = PID_STALL10;
+ else
+ usbp->usb->DCPCTR.BIT.PID = PID_STALL11;
+ }
+ else {
+ if ( usb_pipectr_read(usbp, ep).BIT.PID == PID_NAK)
+ usb_pipectr_write(usbp, ep, PID_STALL10);
+ else
+ usb_pipectr_write(usbp, ep, PID_STALL11);
+ }
+}
+
+/**
+ * @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) {
+
+ usb_lld_stall_out(usbp, ep);
+}
+
+/**
+ * @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) {
+
+ if (ep == 0) {
+ if (usbp->usb->DCPCTR.BIT.PID == PID_STALL11)
+ usbp->usb->DCPCTR.BIT.PID = PID_STALL10;
+ usbp->usb->DCPCTR.BIT.PID = PID_NAK;
+ }
+ else {
+ if ( usb_pipectr_read(usbp, ep).BIT.PID == PID_STALL11)
+ usb_pipectr_write(usbp, ep, PID_STALL10);
+ usb_pipectr_write(usbp, ep, PID_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) {
+
+ usb_lld_clear_out(usbp, ep);
+}
+
+#endif /* HAL_USE_USB */
+
+/** @} */
|