aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal
diff options
context:
space:
mode:
authorRob Lippert <roblip@gmail.com>2016-05-15 12:15:09 +0000
committerFabio Utzig <utzig@utzig.org>2016-05-15 12:15:09 +0000
commit06f9534388d942b2e94ae8b7deda6f60e2914fb1 (patch)
treec4332ff3c9c4ebb29cd1673155a1408c2f3bfdf9 /os/hal
parent5928999fd50a6c2daa12eb7f2c183beffa2c0341 (diff)
downloadChibiOS-06f9534388d942b2e94ae8b7deda6f60e2914fb1.tar.gz
ChibiOS-06f9534388d942b2e94ae8b7deda6f60e2914fb1.tar.bz2
ChibiOS-06f9534388d942b2e94ae8b7deda6f60e2914fb1.zip
Adds support for USB device functionality for AT90USB and
ATU2/U4 series devices. Support tested on a PJRC TEENSY2++ board with AT90USB1286. Signed-off-by: Rob Lippert <roblip@gmail.com> git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9487 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal')
-rw-r--r--os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.c75
-rw-r--r--os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.h86
-rw-r--r--os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.mk5
-rw-r--r--os/hal/ports/AVR/hal_usb_lld.c841
-rw-r--r--os/hal/ports/AVR/hal_usb_lld.h398
-rw-r--r--os/hal/ports/AVR/platform.mk1
6 files changed, 1406 insertions, 0 deletions
diff --git a/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.c b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.c
new file mode 100644
index 000000000..a16f25305
--- /dev/null
+++ b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.c
@@ -0,0 +1,75 @@
+/*
+ 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.
+*/
+
+#include "hal.h"
+
+/**
+ * @brief PAL setup.
+ * @details Digital I/O ports static configuration as defined in @p board.h.
+ * This variable is used by the HAL when initializing the PAL driver.
+ */
+#if HAL_USE_PAL || defined(__DOXYGEN__)
+const PALConfig pal_default_config =
+{
+#if defined(PORTA)
+ {VAL_PORTA, VAL_DDRA},
+#endif
+#if defined(PORTB)
+ {VAL_PORTB, VAL_DDRB},
+#endif
+#if defined(PORTC)
+ {VAL_PORTC, VAL_DDRC},
+#endif
+#if defined(PORTD)
+ {VAL_PORTD, VAL_DDRD},
+#endif
+#if defined(PORTE)
+ {VAL_PORTE, VAL_DDRE},
+#endif
+#if defined(PORTF)
+ {VAL_PORTF, VAL_DDRF},
+#endif
+#if defined(PORTG)
+ {VAL_PORTG, VAL_DDRG},
+#endif
+#if defined(PORTH)
+ {VAL_PORTH, VAL_DDRH},
+#endif
+#if defined(PORTJ)
+ {VAL_PORTJ, VAL_DDRJ},
+#endif
+#if defined(PORTK)
+ {VAL_PORTK, VAL_DDRK},
+#endif
+#if defined(PORTL)
+ {VAL_PORTL, VAL_DDRL},
+#endif
+};
+#endif /* HAL_USE_PAL */
+
+/**
+ * Board-specific initialization code.
+ */
+void boardInit(void) {
+
+ /*
+ * External interrupts setup, all disabled initially.
+ */
+ EICRA = 0x00;
+ EICRB = 0x00;
+ EIMSK = 0x00;
+
+}
diff --git a/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.h b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.h
new file mode 100644
index 000000000..7b20330ee
--- /dev/null
+++ b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.h
@@ -0,0 +1,86 @@
+/*
+ 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.
+*/
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+/*
+ * Setup for the PJRC Teensy2++ board.
+ */
+
+/*
+ * Board identifier.
+ */
+#define BOARD_TEENSY_2PLUSPLUS
+#define BOARD_NAME "PJRC Teensy 2++"
+
+/* All inputs with pull-ups */
+#define VAL_DDRA 0x00
+#define VAL_PORTA 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRB 0x00
+#define VAL_PORTB 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRC 0x00
+#define VAL_PORTC 0xFF
+
+/* All inputs with pull-ups, LED on D6, serial TX1 on D3 */
+#define VAL_DDRD 0x48
+#define VAL_PORTD 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRE 0x00
+#define VAL_PORTE 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRF 0x00
+#define VAL_PORTF 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRG 0x00
+#define VAL_PORTG 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRH 0x00
+#define VAL_PORTH 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRJ 0x00
+#define VAL_PORTJ 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRK 0x00
+#define VAL_PORTK 0xFF
+
+/* All inputs with pull-ups */
+#define VAL_DDRL 0x00
+#define VAL_PORTL 0xFF
+
+#define BOARD_LED1 6
+
+#if !defined(_FROM_ASM_)
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void boardInit(void);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FROM_ASM_ */
+
+#endif /* BOARD_H_ */
diff --git a/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.mk b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.mk
new file mode 100644
index 000000000..244324067
--- /dev/null
+++ b/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.mk
@@ -0,0 +1,5 @@
+# List of all the board related files.
+BOARDSRC = ${CHIBIOS}/os/hal/boards/PJRC_TEENSY_2PLUSPLUS/board.c
+
+# Required include directories
+BOARDINC = ${CHIBIOS}/os/hal/boards/PJRC_TEENSY_2PLUSPLUS
diff --git a/os/hal/ports/AVR/hal_usb_lld.c b/os/hal/ports/AVR/hal_usb_lld.c
new file mode 100644
index 000000000..e6fb42f1e
--- /dev/null
+++ b/os/hal/ports/AVR/hal_usb_lld.c
@@ -0,0 +1,841 @@
+/*
+ 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 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 */
+ USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
+ 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 */
+ UHWCON |= (1 << UIMOD);
+
+ /* 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 */
+
+/** @} */
diff --git a/os/hal/ports/AVR/hal_usb_lld.h b/os/hal/ports/AVR/hal_usb_lld.h
new file mode 100644
index 000000000..e47d38425
--- /dev/null
+++ b/os/hal/ports/AVR/hal_usb_lld.h
@@ -0,0 +1,398 @@
+/*
+ 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 usb_lld.h
+ * @brief AVR USB subsystem low level driver header.
+ *
+ * @addtogroup USB
+ * @{
+ */
+
+#ifndef _USB_LLD_H_
+#define _USB_LLD_H_
+
+#if (HAL_USE_USB == TRUE) || defined(__DOXYGEN__)
+
+#include "hal_usb.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Maximum endpoint address.
+ */
+#define USB_MAX_ENDPOINTS 7
+
+/**
+ * @brief Status stage handling method.
+ */
+#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_HW
+
+/**
+ * @brief The address is changed after IN packet is received.
+ */
+#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS
+
+/**
+ * @brief Method for set address acknowledge.
+ */
+#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name AVR configuration options
+ * @{
+ */
+/**
+ * @brief USB driver enable switch.
+ * @details If set to @p TRUE the support for USB1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(AVR_USB_USE_USB1) || defined(__DOXYGEN__)
+#define AVR_USB_USE_USB1 FALSE
+#endif
+/** @} */
+
+/*
+ * If compiler supports named address spaces
+ * (see https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html)
+ * then mark our TX buf pointer as able to cover flash or SRAM to allow
+ * for storing/transmitting constants like USB descriptors in flash to save
+ * previous RAM space.
+ */
+#if !defined(AVR_USB_USE_NAMED_ADDRESS_SPACES) || defined(__DOXYGEN__)
+#define AVR_USB_USE_NAMED_ADDRESS_SPACES FALSE
+#endif
+
+#if (AVR_USB_USE_NAMED_ADDRESS_SPACES == TRUE) && defined(__MEMX)
+#define AVR_USB_TX_BUF_ADDRESS_SPACE volatile __memx
+#else
+#define AVR_USB_TX_BUF_ADDRESS_SPACE
+#endif
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+typedef const AVR_USB_TX_BUF_ADDRESS_SPACE uint8_t *usbbufptr_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.
+ */
+ usbbufptr_t txbuf;
+#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
+ /**
+ * @brief Waiting thread.
+ */
+ thread_reference_t thread;
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Number of expected bytes in the most recent transmission.
+ */
+ size_t last_tx_size;
+} 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.*/
+ uint8_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.*/
+} 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.
+ */
+ uint8_t transmitting;
+ /**
+ * @brief Bit map of the receiving OUT endpoints.
+ */
+ uint8_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.
+ */
+ const AVR_USB_TX_BUF_ADDRESS_SPACE 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.*/
+};
+
+/*===========================================================================*/
+/* 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) (UDFNUM)
+
+/**
+ * @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.
+ *
+ * @api
+ */
+#define usb_lld_connect_bus(usbp) (UDCON &= ~(1 << DETACH))
+
+/**
+ * @brief Disconnect the USB device.
+ *
+ * @api
+ */
+#define usb_lld_disconnect_bus(usbp) (UDCON |= (1 << DETACH))
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if (AVR_USB_USE_USB1 == TRUE) && !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_enable_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_end_setup(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 == TRUE */
+
+#endif /* _USB_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/AVR/platform.mk b/os/hal/ports/AVR/platform.mk
index 7181924bc..2b20b8a80 100644
--- a/os/hal/ports/AVR/platform.mk
+++ b/os/hal/ports/AVR/platform.mk
@@ -8,6 +8,7 @@ PLATFORMSRC = ${CHIBIOS}/os/hal/ports/AVR/hal_lld.c \
${CHIBIOS}/os/hal/ports/AVR/hal_gpt_lld.c \
${CHIBIOS}/os/hal/ports/AVR/hal_pwm_lld.c \
${CHIBIOS}/os/hal/ports/AVR/hal_icu_lld.c \
+ ${CHIBIOS}/os/hal/ports/AVR/hal_usb_lld.c \
${CHIBIOS}/os/hal/ports/AVR/hal_st_lld.c
# Required include directories