aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c
diff options
context:
space:
mode:
authorTheodore Ateba <tf.ateba@gmail.com>2017-08-16 06:38:35 +0000
committerTheodore Ateba <tf.ateba@gmail.com>2017-08-16 06:38:35 +0000
commit81208fd6bec3dd581cd29ce05f7be988dba9b8fb (patch)
tree2a3c043d8a35d49b01131e2235c44b546a6d8707 /os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c
parent2ec713733e3b260987b38e3b7dc6cbb4ba3c5623 (diff)
downloadChibiOS-81208fd6bec3dd581cd29ce05f7be988dba9b8fb.tar.gz
ChibiOS-81208fd6bec3dd581cd29ce05f7be988dba9b8fb.tar.bz2
ChibiOS-81208fd6bec3dd581cd29ce05f7be988dba9b8fb.zip
Add low level driver directory and platfor for AVR MEGA.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10431 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c')
-rw-r--r--os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c b/os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c
new file mode 100644
index 000000000..91f99fb66
--- /dev/null
+++ b/os/hal/ports/AVR/MEGA/LLD/USBv1/hal_usb_lld.c
@@ -0,0 +1,849 @@
+/*
+ Copyright (C) 2015 Robert Lippert
+
+ 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 hal_usb_lld.c
+ * @brief AVR USB subsystem low level driver source.
+ *
+ * @addtogroup USB
+ * @{
+ */
+
+#include "hal.h"
+
+#if (HAL_USE_USB == TRUE) || defined(__DOXYGEN__)
+
+#ifndef F_USB
+#define F_USB F_CPU
+#endif
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief USB1 driver identifier.
+ */
+#if (AVR_USB_USE_USB1 == TRUE) || 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 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
+};
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+#ifdef AVR_USB_PLL_OFF_IN_SUSPEND
+static __attribute__((unused)) void usb_pll_off(void) {
+ PLLCSR = 0;
+}
+#endif
+
+static void usb_pll_on(void) {
+#if (F_USB == 8000000)
+ #if (defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) || \
+ defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || \
+ defined(__AVR_ATmega32U2__))
+ #define PLL_VAL 0
+ #elif (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
+ #define PLL_VAL 0
+ #elif (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
+ #define PLL_VAL ((0 << PLLP2) | (1 << PLLP1) | (1 << PLLP0))
+ #elif (defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1287__))
+ #define PLL_VAL ((0 << PLLP2) | (1 << PLLP1) | (1 << PLLP0))
+ #endif
+#elif (F_USB == 16000000)
+ #if (defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) || \
+ defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || \
+ defined(__AVR_ATmega32U2__))
+ #define PLL_VAL ((0 << PLLP2) | (0 << PLLP1) | (1 << PLLP0))
+ #elif (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
+ #define PLL_VAL (1 << PINDIV)
+ #elif (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__))
+ #define PLL_VAL ((1 << PLLP2) | (1 << PLLP1) | (0 << PLLP0))
+ #elif (defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__))
+ #define PLL_VAL ((1 << PLLP2) | (0 << PLLP1) | (1 << PLLP0))
+ #endif
+#endif
+
+#ifndef PLL_VAL
+#error Could not determine PLL value, unsupported AVR USB model type
+#endif
+
+#ifdef PLLFRQ
+ /* This initializes PLL on supported devices for USB 48MHz *only* */
+ PLLFRQ = (0 << PDIV3) | (1 << PDIV2) | (0 << PDIV1) | (0 << PDIV0);
+#endif
+
+ PLLCSR = PLL_VAL;
+ PLLCSR = PLL_VAL | (1 << PLLE);
+}
+
+static int usb_pll_is_locked(void) {
+ return !!(PLLCSR & (1 << PLOCK));
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers and threads. */
+/*===========================================================================*/
+
+/**
+ * @brief USB general/OTG/device management event interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(USB_GEN_vect) {
+ uint8_t usbint, udint;
+ USBDriver * const usbp = &USBD1;
+
+ OSAL_IRQ_PROLOGUE();
+
+ usbint = USBINT;
+ udint = UDINT;
+
+ if (usbint & (1 << VBUSTI)) {
+ /* Connected. */
+#ifdef AVR_USB_PLL_OFF_IN_SUSPEND
+ usb_pll_on();
+ while (!usb_pll_is_locked()) {}
+#endif /* AVR_USB_PLL_OFF_IN_SUSPEND */
+
+ /* Attach to bus */
+ usb_lld_connect_bus(usbp);
+ USBINT &= ~(1 << VBUSTI);
+ }
+
+ /* USB bus SUSPEND condition handling.*/
+ if (udint & (1 << SUSPI)) {
+ /* Disable suspend interrupt, enable WAKEUP interrupt */
+ UDIEN |= (1 << WAKEUPE);
+ UDINT &= ~(1 << WAKEUPI);
+ UDIEN &= ~(1 << SUSPE);
+
+ /* Freeze the clock to reduce power consumption */
+ USBCON |= (1 << FRZCLK);
+#ifdef AVR_USB_PLL_OFF_IN_SUSPEND
+ usb_pll_off();
+#endif /* AVR_USB_PLL_OFF_IN_SUSPEND */
+
+ /* Clear the interrupt */
+ UDINT &= ~(1 << SUSPI);
+
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_SUSPEND);
+ }
+
+ /* USB bus WAKEUP condition handling.*/
+ if (udint & (1 << WAKEUPI)) {
+#ifdef AVR_USB_PLL_OFF_IN_SUSPEND
+ usb_pll_on();
+ while (!usb_pll_is_locked()) {}
+#endif /* AVR_USB_PLL_OFF_IN_SUSPEND */
+
+ /* Unfreeze the clock */
+ USBCON &= ~(1 << FRZCLK);
+
+ /* Clear & disable wakeup interrupt, enable suspend interrupt */
+ UDINT &= ~(1 << WAKEUPI);
+ UDIEN &= ~(1 << WAKEUPE);
+ UDIEN |= (1 << SUSPE);
+
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_WAKEUP);
+ }
+
+ /* USB bus RESUME condition handling.*/
+ if (udint & (1 << EORSMI)) {
+ UDINT &= ~(1 << EORSMI);
+ UDIEN &= ~(1 << EORSME);
+ }
+
+ /* USB bus reset condition handling.*/
+ if (udint & (1 << EORSTI)) {
+ UDINT &= ~(1 << EORSTI);
+
+ /* Clear & disable suspend interrupt, enable WAKEUP interrupt */
+ UDINT &= ~(1 << SUSPI);
+ UDIEN &= ~(1 << SUSPE);
+ UDIEN |= (1 << WAKEUPE);
+
+ /* Reinitialize EP0. This is not mentioned in the datasheet but
+ * apparently is required. */
+ usb_lld_init_endpoint(usbp, 0);
+
+ _usb_isr_invoke_event_cb(usbp, USB_EVENT_RESET);
+ }
+
+ /* Start-Of-Frame handling, only if enabled */
+ if ((UDIEN & (1 << SOFE)) && (udint & (1 << SOFI))) {
+ _usb_isr_invoke_sof_cb(usbp);
+ UDINT &= ~(1 << SOFI);
+ }
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+static void usb_fifo_write(USBDriver *usbp, usbep_t ep, size_t n) {
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ USBInEndpointState *isp = epcp->in_state;
+ syssts_t sts;
+ if (n == 0) {
+ isp->last_tx_size = 0;
+ return;
+ }
+
+ if (n > epcp->in_maxsize)
+ n = epcp->in_maxsize;
+ /* i is number of bytes remaining to transmit minus 1 (to handle 256b case) */
+ uint8_t i = n - 1;
+
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+ do {
+ UEDATX = *isp->txbuf++;
+ } while (i--);
+ isp->last_tx_size = n;
+ osalSysRestoreStatusX(sts);
+}
+
+static void usb_fifo_read(USBDriver *usbp, usbep_t ep, size_t n) {
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ USBOutEndpointState *osp = epcp->out_state;
+ syssts_t sts;
+ if (n == 0)
+ return;
+ if (n > epcp->out_maxsize)
+ n = epcp->out_maxsize;
+ // i is number of bytes remaining to receive minus 1 (to handle 256b case)
+ uint8_t i = n - 1;
+
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+ do {
+ *osp->rxbuf++ = UEDATX;
+ } while (i--);
+ osalSysRestoreStatusX(sts);
+}
+
+static void ep_isr(USBDriver *usbp, usbep_t ep) {
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ size_t n;
+ UENUM = ep & 0xf;
+
+ /* TODO: if stalling is needed/expected remove this check */
+ osalDbgAssert(!(UEINTX & (1 << STALLEDI)), "Endpoint stalled!");
+
+ if ((UEIENX & (1 << TXINE)) && (UEINTX & (1 << TXINI))) {
+ /* Ready to accept more IN data to transmit to host */
+ /* Update transaction counts to reflect newly transmitted bytes */
+ epcp->in_state->txcnt += epcp->in_state->last_tx_size;
+ n = epcp->in_state->txsize - epcp->in_state->txcnt;
+ if (n > 0) {
+ /* Transfer not completed, there are more packets to send. */
+ usb_fifo_write(usbp, ep, n);
+
+ /* Clear FIFOCON to send the data in the FIFO and switch bank */
+ UEINTX &= ~((1 << TXINI) | (1 << FIFOCON));
+ /* Enable the TX complete interrupt */
+ UEIENX |= (1 << TXINE);
+ } else {
+ /* Disable TXIN interrupt */
+ UEIENX &= ~(1 << TXINE);
+ /* Handshake interrupt status */
+ UEINTX &= ~(1 << TXINI);
+ _usb_isr_invoke_in_cb(usbp, ep);
+ }
+ } else if ((UEIENX & (1 << RXSTPE)) && (UEINTX & (1 << RXSTPI))) {
+ /* Received SETUP data */
+ /* Reset transaction state for endpoint */
+ epcp->in_state->txcnt = 0;
+ epcp->in_state->txsize = 0;
+ epcp->in_state->last_tx_size = 0;
+ /* Setup packets handling, setup packets are handled using a
+ specific callback.*/
+ _usb_isr_invoke_setup_cb(usbp, ep);
+ } else if ((UEIENX & (1 << RXOUTE)) && (UEINTX & (1 << RXOUTI))) {
+ /* Received OUT data from host */
+ if (ep == 0 && usbp->ep0state == USB_EP0_WAITING_STS) {
+ /* SETUP/control transaction complete, invoke the callback. */
+ UEIENX &= ~(1 << RXOUTE);
+ UEINTX &= ~((1 << RXOUTI) | (1 << FIFOCON));
+ _usb_isr_invoke_out_cb(usbp, ep);
+ } else {
+ /* Check the FIFO byte count to see how many bytes were received */
+ n = UEBCX;
+
+ usb_fifo_read(usbp, ep, n);
+
+ /* Transaction state update */
+ epcp->out_state->rxcnt += n;
+ epcp->out_state->rxsize -= n;
+ epcp->out_state->rxpkts -= 1;
+ if (n < epcp->out_maxsize || epcp->out_state->rxpkts == 0) {
+ /* Disable OUT interrupt */
+ UEIENX &= ~(1 << RXOUTE);
+ /* Mark OUT FIFO processed to allow more data to be received */
+ UEINTX &= ~((1 << RXOUTI) | (1 << FIFOCON));
+ /* Transfer complete, invokes the callback.*/
+ _usb_isr_invoke_out_cb(usbp, ep);
+ } else {
+ /* Mark OUT FIFO processed to allow more data to be received */
+ UEINTX &= ~((1 << RXOUTI) | (1 << FIFOCON));
+ }
+ }
+ }
+}
+
+/**
+ * @brief USB communication event interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(USB_COM_vect) {
+ USBDriver *usbp = &USBD1;
+ const uint8_t epnum_orig = UENUM;
+ uint8_t i;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Figure out which endpoint(s) are interrupting */
+ for (i = 0; i < USB_MAX_ENDPOINTS; ++i) {
+ if (UEINT & (1 << i)) {
+ ep_isr(usbp, i);
+ }
+ }
+
+ /* Restore endpoint selector to pre-interrupt state */
+ UENUM = epnum_orig;
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level USB driver initialization.
+ *
+ * @notapi
+ */
+void usb_lld_init(void) {
+#if AVR_USB_USE_USB1 == TRUE
+ /* Driver initialization.*/
+ usbObjectInit(&USBD1);
+
+ /* Start and lock the USB 48MHz PLL (takes ~100ms) */
+ usb_pll_on();
+ while (!usb_pll_is_locked()) {}
+#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) {
+ /* Enables the peripheral.*/
+#if AVR_USB_USE_USB1 == TRUE
+ if (&USBD1 == usbp) {
+ uint8_t i;
+ /*
+ * Workaround: disable pad drivers as first step in case bootloader left
+ * it on. Otherwise VBUS detection interrupt will not trigger later.
+ */
+ USBCON &= ~(1 << OTGPADE);
+
+ /* Enable the internal 3.3V pad regulator */
+ UHWCON |= (1 << UVREGE);
+
+ /* Reset and disable all endpoints */
+ UERST = 0x7f;
+ UERST = 0;
+ for (i = 0; i < USB_MAX_ENDPOINTS; ++i){
+ UENUM = i;
+ UEIENX = 0;
+ UEINTX = 0;
+ UECFG1X = 0;
+ UECONX &= ~(1 << EPEN);
+ }
+ }
+#endif
+ /* Reset procedure enforced on driver start.*/
+ _usb_reset(usbp);
+ }
+}
+
+/**
+ * @brief Deactivates the USB peripheral.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_stop(USBDriver *usbp) {
+ if (usbp->state == USB_READY) {
+ /* Disables the peripheral.*/
+#if AVR_USB_USE_USB1 == TRUE
+ if (&USBD1 == usbp) {
+ /* Disable and clear transition interrupts */
+#if !defined(__AVR_ATmega32U4__)
+ USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
+#else
+ USBCON &= ~(1 << VBUSTE);
+#endif
+
+ USBINT = 0;
+
+ /* Disable and clear device interrupts */
+ UDIEN &= ~((1 << UPRSME) | (1 << EORSME) | (1 << WAKEUPE) | (1 << EORSTE)
+ | (1 << SOFE) | (1 << SUSPE));
+ UDINT = 0;
+
+ /* Freeze clock */
+ USBCON |= (1 << FRZCLK);
+
+ /* Disable USB logic */
+ USBCON &= ~(1 << USBE);
+ }
+#endif
+ }
+}
+
+/**
+ * @brief USB low level reset routine.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_reset(USBDriver *usbp) {
+ /* Post-reset initialization.*/
+ /* Reset and enable via toggling the USB macro logic overall enable bit */
+ USBCON &= ~(1 << USBE);
+ USBCON |= (1 << USBE);
+
+ /* Unfreeze clock */
+ USBCON &= ~(1 << FRZCLK);
+
+ /* Set Device mode */
+ /* TODO: Support HOST/OTG mode if needed */
+
+#if !defined(__AVR_ATmega32U4__)
+ UHWCON |= (1 << UIMOD);
+#endif
+
+ /* Set FULL 12mbps speed */
+ UDCON &= ~(1 << LSM);
+
+ /* Enable device pin interrupt */
+ USBCON |= (1 << VBUSTE);
+
+ /* EP0 initialization.*/
+ UERST |= (1 << 0);
+ UERST &= ~(1 << 0);
+ usbp->epc[0] = &ep0config;
+ usb_lld_init_endpoint(usbp, 0);
+
+ /* Enable device-level event interrupts */
+ UDINT &= ~(1 << SUSPI);
+ UDIEN = (1 << UPRSME) | (1 << EORSME) | (1 << WAKEUPE) | (1 << EORSTE)
+ | (1 << SUSPE);
+ /* The SOF interrupt is only enabled if a callback is defined for
+ this service because it is a high rate source. */
+ if (usbp->config->sof_cb != NULL)
+ UDIEN |= (1 << SOFE);
+
+ /* Set OTG PAD to on which will trigger VBUS transition if plugged in. */
+ USBCON |= (1 << OTGPADE);
+}
+
+/**
+ * @brief Sets the USB address.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_set_address(USBDriver *usbp) {
+ UDADDR = (UDADDR & (1 << ADDEN)) | (usbp->address & 0x7F);
+
+ UDADDR |= (1 << ADDEN);
+}
+
+/**
+ * @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 size = 0;
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+
+ /* Select this endpoint number for subsequent commands */
+ UENUM = ep & 0xf;
+
+ /* Enable endpoint to take out of reset */
+ UECONX |= (1 << EPEN);
+
+ UECFG1X = 0;
+ /* Set the endpoint type.*/
+ switch (epcp->ep_mode & USB_EP_MODE_TYPE) {
+ case USB_EP_MODE_TYPE_ISOC:
+ UECFG0X = (0 << EPTYPE1) | (1 << EPTYPE0);
+ break;
+ case USB_EP_MODE_TYPE_BULK:
+ UECFG0X = (1 << EPTYPE1) | (0 << EPTYPE0);
+ break;
+ case USB_EP_MODE_TYPE_INTR:
+ UECFG0X = (1 << EPTYPE1) | (1 << EPTYPE0);
+ break;
+ default:
+ UECFG0X = (0 << EPTYPE1) | (0 << EPTYPE0);
+ }
+ if ((epcp->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_CTRL) {
+ /* CTRL endpoint */
+ osalDbgCheck(epcp->in_maxsize == epcp->out_maxsize);
+ size = epcp->in_maxsize;
+ } else {
+ osalDbgAssert(!(epcp->in_cb != NULL && epcp->out_cb != NULL),
+ "On AVR each endpoint can be IN or OUT not both");
+
+ /* IN endpoint? */
+ if (epcp->in_cb != NULL) {
+ UECFG0X |= (1 << EPDIR);
+ size = epcp->in_maxsize;
+ }
+
+ /* OUT endpoint? */
+ if (epcp->out_cb != NULL) {
+ UECFG0X &= ~(1 << EPDIR);
+ size = epcp->out_maxsize;
+ }
+ }
+
+ /* Endpoint size and address initialization. */
+ switch (size) {
+ case 8: UECFG1X = (0 << EPSIZE0) | (1 << ALLOC); break;
+ case 16: UECFG1X = (1 << EPSIZE0) | (1 << ALLOC); break;
+ case 32: UECFG1X = (2 << EPSIZE0) | (1 << ALLOC); break;
+ case 64: UECFG1X = (3 << EPSIZE0) | (1 << ALLOC); break;
+ case 128:
+ osalDbgAssert(ep == 1, "Endpoint size of 128 bytes only valid for EP#1");
+ UECFG1X = (4 << EPSIZE0) | (1 << ALLOC); break;
+ case 256:
+ osalDbgAssert(ep == 1, "Endpoint size of 256 bytes only valid for EP#1");
+ UECFG1X = (5 << EPSIZE0) | (1 << ALLOC); break;
+ default:
+ osalDbgAssert(false, "Invalid size for USB endpoint");
+ }
+
+ UEIENX |= (1 << RXSTPE)/* | (1 << RXOUTE)*/ | (1 << STALLEDE) ;
+
+ osalDbgAssert((UESTA0X & (1 << CFGOK)),
+ "Hardware reports endpoint config is INVALID");
+}
+
+/**
+ * @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) {
+ uint8_t i;
+ for (i = 1; i <= USB_MAX_ENDPOINTS; ++i) {
+ UENUM = i;
+ UECFG1X &= ~(1 << ALLOC);
+ UECONX &= ~(1 << EPEN);
+ }
+}
+
+/**
+ * @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) {
+ /* Select this endpoint number for subsequent commands */
+ UENUM = ep & 0xf;
+
+ if (!(UECONX & (1 << EPEN)))
+ return EP_STATUS_DISABLED;
+ if (UECONX & (1 << STALLRQ))
+ 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) {
+ 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) {
+ uint8_t i;
+ /* Select this endpoint number for subsequent commands */
+ UENUM = ep & 0xf;
+
+ for (i = 0; i < 8; ++i) {
+ *buf++ = UEDATX;
+ }
+ /* Clear FIFOCON and RXSTPI to drain the setup packet data from the FIFO */
+ UEINTX &= ~((1 << FIFOCON) | (1 << RXSTPI));
+}
+
+/**
+ * @brief Ends a SETUP transaction
+ * @details This function must be invoked in the context of the @p setup_cb
+ * callback in order to finish an entire 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
+ *
+ * @notapi
+ */
+void usb_lld_end_setup(USBDriver *usbp, usbep_t ep) {
+ /* Select this endpoint number for subsequent commands */
+ UENUM = ep & 0xf;
+
+ if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) {
+ /* Enable interrupt and wait for OUT packet */
+ usbp->epc[ep]->out_state->rxsize = 0;
+ usbp->epc[ep]->out_state->rxpkts = 1;
+
+ UEINTX &= ~((1 << FIFOCON) | (1 << RXOUTI));
+ UEIENX |= (1 << RXOUTE);
+ } else {
+ /* Enable interrupt and wait for IN packet */
+ usbp->epc[ep]->in_state->last_tx_size = 0;
+ usbp->epc[ep]->in_state->txcnt = 0;
+ usbp->epc[ep]->in_state->txsize = 0;
+
+ UEINTX &= ~((1 << FIFOCON) | (1 << TXINI));
+ UEIENX |= (1 << TXINE);
+ }
+}
+
+/**
+ * @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) {
+ USBOutEndpointState *osp = usbp->epc[ep]->out_state;
+ syssts_t sts;
+
+ /* Initialize transfer by recording how many packets we expect to receive. */
+ if (osp->rxsize == 0) /* Special case for zero sized packets.*/
+ osp->rxpkts = 1;
+ else
+ osp->rxpkts = (uint8_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) /
+ usbp->epc[ep]->out_maxsize);
+
+ /* Select this endpoint number for subsequent commands */
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+
+ UEIENX |= (1 << RXOUTE);
+ osalSysRestoreStatusX(sts);
+}
+
+/**
+ * @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;
+ syssts_t sts;
+
+ /* Initialize transfer by filling FIFO with passed data. */
+ usb_fifo_write(usbp, ep, isp->txsize);
+
+ /* Select this endpoint number for subsequent commands */
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+
+ /* Clear FIFOCON to send the data in the FIFO and switch bank */
+ UEINTX &= ~((1 << TXINI) | (1 << FIFOCON));
+
+ /* Enable the TX complete interrupt */
+ UEIENX |= (1 << TXINE);
+
+ osalSysRestoreStatusX(sts);
+}
+
+/**
+ * @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) {
+ syssts_t sts;
+ (void)usbp;
+
+ /* Select this endpoint number for subsequent commands */
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+
+ UECONX |= (1 << STALLRQ);
+ osalSysRestoreStatusX(sts);
+}
+
+/**
+ * @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) {
+ syssts_t sts;
+ (void)usbp;
+
+ /* Select this endpoint number for subsequent commands */
+ /* Must lock for entire operation to ensure nothing changes the ENUM value */
+ sts = osalSysGetStatusAndLockX();
+ UENUM = ep & 0xf;
+
+ UECONX |= (1 << STALLRQC);
+ osalSysRestoreStatusX(sts);
+}
+
+/**
+ * @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 == TRUE */
+
+/** @} */