start with USB CDC echo example

this is the Atmel START USB CDC Echo example project for the
SAM E54 Xplained Pro board using an Atmel ATSAME54P20A
microcontroller.
Atmel START information:
- Version: 1.4.1810 (Dec 18, 2018, 5:52 AM GMT+1)
- Server: 1.4.93
- Content version: 1.0.1340

This will serve as basis for the sysmoOCTSIM project

A jenkins contrib script has also been added to the
osmo-ccid-firmware project to build the sysmoOCTSIM firmware

Change-Id: I356de75e7b730d63fb819248e71d36f785932199
diff --git a/sysmoOCTSIM/hpl/usb/hpl_usb.c b/sysmoOCTSIM/hpl/usb/hpl_usb.c
new file mode 100644
index 0000000..6bf09ab
--- /dev/null
+++ b/sysmoOCTSIM/hpl/usb/hpl_usb.c
@@ -0,0 +1,2078 @@
+/**
+ * \file
+ *
+ * \brief SAM USB HPL
+ *
+ * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+
+#include <compiler.h>
+#include <hal_atomic.h>
+#include <hpl_usb.h>
+#include <hpl_usb_device.h>
+
+#include <hpl_usb_config.h>
+#include <string.h>
+#include <utils_assert.h>
+
+/**
+ * \brief Dummy callback function
+ * \return Always false.
+ */
+static bool _dummy_func_no_return(uint32_t unused0, uint32_t unused1)
+{
+	(void)unused0;
+	(void)unused1;
+	return false;
+}
+
+/**
+ * \brief Load USB calibration value from NVM
+ */
+static void _usb_load_calib(void)
+{
+#define NVM_USB_PAD_TRANSN_POS 32
+#define NVM_USB_PAD_TRANSN_SIZE 5
+#define NVM_USB_PAD_TRANSP_POS 37
+#define NVM_USB_PAD_TRANSP_SIZE 5
+#define NVM_USB_PAD_TRIM_POS 42
+#define NVM_USB_PAD_TRIM_SIZE 3
+	Usb *    hw = USB;
+	uint32_t pad_transn
+	    = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRANSN_POS / 32)) >> (NVM_USB_PAD_TRANSN_POS % 32))
+	      & ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1);
+	uint32_t pad_transp
+	    = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRANSP_POS / 32)) >> (NVM_USB_PAD_TRANSP_POS % 32))
+	      & ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1);
+	uint32_t pad_trim = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRIM_POS / 32)) >> (NVM_USB_PAD_TRIM_POS % 32))
+	                    & ((1 << NVM_USB_PAD_TRIM_SIZE) - 1);
+	if (pad_transn == 0 || pad_transn == 0x1F) {
+		pad_transn = 9;
+	}
+	if (pad_transp == 0 || pad_transp == 0x1F) {
+		pad_transp = 25;
+	}
+	if (pad_trim == 0 || pad_trim == 0x7) {
+		pad_trim = 6;
+	}
+
+	hw->DEVICE.PADCAL.reg = USB_PADCAL_TRANSN(pad_transn) | USB_PADCAL_TRANSP(pad_transp) | USB_PADCAL_TRIM(pad_trim);
+
+	hw->DEVICE.QOSCTRL.bit.CQOS = 3;
+	hw->DEVICE.QOSCTRL.bit.DQOS = 3;
+}
+
+/** \name USB clock source management */
+/*@{*/
+
+/** USB clock is generated by DFLL. */
+#define USB_CLK_SRC_DFLL 0
+
+/** USB clock is generated by DPLL. */
+#define USB_CLK_SRC_DPLL 1
+
+/** Uses DFLL as USB clock source. */
+#define CONF_USB_D_CLK_SRC USB_CLK_SRC_DFLL
+
+/** Retry for USB remote wakeup sending. */
+#define CONF_USB_RMT_WKUP_RETRY 5
+
+/**
+ * \brief Wait DPLL clock to be ready
+ */
+static inline void _usb_d_dev_wait_dpll_rdy(void)
+{
+#define DPLL_READY_FLAG (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK)
+	while (hri_oscctrl_get_DPLLSTATUS_reg(OSCCTRL, 0, DPLL_READY_FLAG) != DPLL_READY_FLAG)
+		;
+}
+
+/**
+ * \brief Wait DFLL clock to be ready
+ */
+static inline void _usb_d_dev_wait_dfll_rdy(void)
+{
+	if (hri_oscctrl_get_DFLLCTRLB_MODE_bit(OSCCTRL)) {
+		while (hri_oscctrl_get_STATUS_reg(OSCCTRL, (OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC))
+		       != (OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC))
+			;
+	} else {
+		while (hri_oscctrl_get_STATUS_reg(OSCCTRL, OSCCTRL_STATUS_DFLLRDY) != OSCCTRL_STATUS_DFLLRDY)
+			;
+	}
+}
+
+/**
+ * \brief Wait USB source clock to be ready
+ * \param[in] clk_src Clock source, could be \ref USB_CLK_SRC_DFLL or
+ *                    \ref USB_CLK_SRC_DPLL.
+ */
+static inline void _usb_d_dev_wait_clk_rdy(const uint8_t clk_src)
+{
+	if (clk_src == USB_CLK_SRC_DFLL) {
+		_usb_d_dev_wait_dfll_rdy();
+	} else if (clk_src == USB_CLK_SRC_DPLL) {
+		_usb_d_dev_wait_dpll_rdy();
+	}
+}
+
+/*@}*/
+
+/** \name USB general settings */
+/*@{*/
+
+/** Increase the value to be aligned. */
+#define _usb_align_up(val) (((val)&0x3) ? (((val) + 4 - ((val)&0x3))) : (val))
+
+/** Check if the buffer is in RAM (can DMA), or cache needed
+ *  \param[in] a Buffer start address.
+ *  \param[in] s Buffer size, in number of bytes.
+ *  \return \c true If the buffer is in RAM.
+ */
+#define _IN_RAM(a, s) ((0x20000000 <= (uint32_t)(a)) && (((uint32_t)(a) + (s)) < (0x20000000 + 0x00042000)))
+
+/** Check if the address should be placed in RAM. */
+#define _usb_is_addr4dma(addr, size) _IN_RAM((addr), (size))
+
+/** Check if the address is 32-bit aligned. */
+#define _usb_is_aligned(val) (((uint32_t)(val)&0x3) == 0)
+/*@}*/
+
+/* Cache static configurations.
+ * By default, all OUT endpoint have 64 bytes cache. */
+#ifndef CONF_USB_EP0_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP0_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP0_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP0_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP1_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP1_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP1_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP1_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP2_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP2_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP2_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP2_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP3_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP3_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP3_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP3_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP4_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP4_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP4_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP4_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP5_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP5_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP5_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP5_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP6_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP6_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP6_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP6_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP7_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP7_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP7_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP7_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP8_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP8_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP8_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP8_I_CACHE 0
+#endif
+
+#ifndef CONF_USB_EP9_CACHE
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#define CONF_USB_EP9_CACHE 64
+#endif
+
+#ifndef CONF_USB_EP9_I_CACHE
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define CONF_USB_EP9_I_CACHE 0
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP0_CACHE
+static uint32_t _usb_ep0_cache[_usb_align_up(CONF_USB_EP0_CACHE) / 4];
+#else
+#define _usb_ep0_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#define _usb_ep0_i_cache NULL
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP1_CACHE && CONF_USB_D_MAX_EP_N >= 1
+static uint32_t _usb_ep1_cache[_usb_align_up(CONF_USB_EP1_CACHE) / 4];
+#else
+#define _usb_ep1_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP1_I_CACHE && CONF_USB_D_MAX_EP_N >= 1
+static uint32_t _usb_ep1_i_cache[_usb_align_up(CONF_USB_EP1_I_CACHE) / 4];
+#else
+#define _usb_ep1_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP2_CACHE && CONF_USB_D_MAX_EP_N >= 2
+static uint32_t _usb_ep2_cache[_usb_align_up(CONF_USB_EP2_CACHE) / 4];
+#else
+#define _usb_ep2_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP2_I_CACHE && CONF_USB_D_MAX_EP_N >= 2
+static uint32_t _usb_ep2_i_cache[_usb_align_up(CONF_USB_EP2_I_CACHE) / 4];
+#else
+#define _usb_ep2_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP3_CACHE && CONF_USB_D_MAX_EP_N >= 3
+static uint32_t _usb_ep3_cache[_usb_align_up(CONF_USB_EP3_CACHE) / 4];
+#else
+#define _usb_ep3_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP3_I_CACHE && CONF_USB_D_MAX_EP_N >= 3
+static uint32_t _usb_ep3_i_cache[_usb_align_up(CONF_USB_EP3_I_CACHE) / 4];
+#else
+#define _usb_ep3_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP4_CACHE && CONF_USB_D_MAX_EP_N >= 4
+static uint32_t _usb_ep4_cache[_usb_align_up(CONF_USB_EP4_CACHE) / 4];
+#else
+#define _usb_ep4_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP4_I_CACHE && CONF_USB_D_MAX_EP_N >= 4
+static uint32_t _usb_ep4_i_cache[_usb_align_up(CONF_USB_EP4_I_CACHE) / 4];
+#else
+#define _usb_ep4_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP5_CACHE && CONF_USB_D_MAX_EP_N >= 5
+static uint32_t _usb_ep5_cache[_usb_align_up(CONF_USB_EP5_CACHE) / 4];
+#else
+#define _usb_ep5_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP5_I_CACHE && CONF_USB_D_MAX_EP_N >= 5
+static uint32_t _usb_ep5_i_cache[_usb_align_up(CONF_USB_EP5_I_CACHE) / 4];
+#else
+#define _usb_ep5_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP6_CACHE && CONF_USB_D_MAX_EP_N >= 6
+static uint32_t _usb_ep6_cache[_usb_align_up(CONF_USB_EP6_CACHE) / 4];
+#else
+#define _usb_ep6_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP6_I_CACHE && CONF_USB_D_MAX_EP_N >= 6
+static uint32_t _usb_ep6_i_cache[_usb_align_up(CONF_USB_EP6_I_CACHE) / 4];
+#else
+#define _usb_ep6_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP7_CACHE && CONF_USB_D_MAX_EP_N >= 7
+static uint32_t _usb_ep7_cache[_usb_align_up(CONF_USB_EP7_CACHE) / 4];
+#else
+#define _usb_ep7_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP7_I_CACHE && CONF_USB_D_MAX_EP_N >= 7
+static uint32_t _usb_ep7_i_cache[_usb_align_up(CONF_USB_EP7_I_CACHE) / 4];
+#else
+#define _usb_ep7_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP8_CACHE && CONF_USB_D_MAX_EP_N >= 8
+static uint32_t _usb_ep8_cache[_usb_align_up(CONF_USB_EP8_CACHE) / 4];
+#else
+#define _usb_ep8_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP8_I_CACHE && CONF_USB_D_MAX_EP_N >= 8
+static uint32_t _usb_ep8_i_cache[_usb_align_up(CONF_USB_EP8_I_CACHE) / 4];
+#else
+#define _usb_ep8_i_cache NULL
+#endif
+
+/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
+ *  transactions (control). */
+#if CONF_USB_EP9_CACHE && CONF_USB_D_MAX_EP_N >= 9
+static uint32_t _usb_ep9_cache[_usb_align_up(CONF_USB_EP9_CACHE) / 4];
+#else
+#define _usb_ep9_cache NULL
+#endif
+
+/** Endpoint cache buffer for IN transactions (none-control). */
+#if CONF_USB_EP9_I_CACHE && CONF_USB_D_MAX_EP_N >= 9
+static uint32_t _usb_ep9_i_cache[_usb_align_up(CONF_USB_EP9_I_CACHE) / 4];
+#else
+#define _usb_ep9_i_cache NULL
+#endif
+
+/** Access endpoint cache buffer for OUT transactions (none-control) or
+ *  SETUP/IN/OUT transactions (control). */
+#define _USB_EP_CACHE(n) ((void *)_usb_ep##n##_cache)
+
+/** Access endpoint cache buffer for IN transactions (none-control). */
+#define _USB_EP_I_CACHE(n) ((void *)_usb_ep##n##_i_cache)
+
+/** The configuration settings for one of the endpoint hardware. */
+struct _usb_ep_cfg_item {
+	/* Endpoint cache buffer for OUT transactions (none-control) or
+	 * SETUP/IN/OUT transactions (control). */
+	void *cache;
+	/* endpoint cache buffer for IN transactions (none-control). */
+	void *i_cache;
+	/* Cache buffer size for OUT transactions (none-control) or
+	 * SETUP/IN/OUT transactions (control). */
+	uint16_t size;
+	/* Cache buffer size for IN transactions (none-control). */
+	uint16_t i_size;
+};
+
+/** Build the endpoint configuration settings for one endpoint. */
+#define _USB_EP_CFG_ITEM(n)                                                                                            \
+	{                                                                                                                  \
+		_USB_EP_CACHE(n), _USB_EP_I_CACHE(n), CONF_USB_EP##n##_CACHE, CONF_USB_EP##n##_I_CACHE,                        \
+	}
+
+/** The configuration settings for all endpoint. */
+static const struct _usb_ep_cfg_item _usb_ep_cfgs[] = {_USB_EP_CFG_ITEM(0)
+#if CONF_USB_D_MAX_EP_N >= 1
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(1)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 2
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(2)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 3
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(3)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 4
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(4)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 5
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(5)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 6
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(6)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 7
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(7)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 8
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(8)
+#endif
+#if CONF_USB_D_MAX_EP_N >= 9
+                                                           ,
+                                                       _USB_EP_CFG_ITEM(9)
+#endif
+};
+
+/** \name HW specific settings and implements */
+/*@{*/
+
+/** Number of endpoints supported. */
+#define USB_D_N_EP (1 + CONF_USB_D_NUM_EP_SP * 2)
+
+/** HPL USB device endpoint struct. */
+struct _usb_d_dev_ep {
+	/** Pointer to transaction buffer. */
+	uint8_t *trans_buf;
+	/** Transaction size. */
+	uint32_t trans_size;
+	/** Transaction transferred count. */
+	uint32_t trans_count;
+
+	/** Pointer to cache buffer, must be aligned. */
+	uint8_t *cache;
+
+	/** Endpoint size. */
+	uint16_t size;
+	/** Endpoint address. */
+	uint8_t ep;
+	/** Feature flags. */
+	union {
+		/** Interpreted by bit fields. */
+		struct {
+			/** EPCFG.ETYPE. */
+			uint8_t eptype : 3;
+			/** Stall status. */
+			uint8_t is_stalled : 1;
+			/** Transaction auto ZLP. */
+			uint8_t need_zlp : 1;
+			/** Transaction with cache */
+			uint8_t use_cache : 1;
+			/** Endpoint is busy. */
+			uint8_t is_busy : 1;
+			/** Transaction direction. */
+			uint8_t dir : 1;
+		} bits;
+		uint8_t u8;
+	} flags;
+};
+
+/** Check if the endpoint is used. */
+#define _usb_d_dev_ep_is_used(ept) ((ept)->ep != 0xFF)
+
+/** Check if the endpoint is busy doing transactions. */
+#define _usb_d_dev_ep_is_busy(ept) ((ept)->flags.bits.is_busy)
+
+/** Check if the endpoint is control endpoint. */
+#define _usb_d_dev_ep_is_ctrl(ept) ((ept)->flags.bits.eptype == USB_D_EPTYPE_CTRL)
+
+/** Check if the endpoint transactions are IN. */
+#define _usb_d_dev_ep_is_in(ept) ((ept)->flags.bits.dir)
+
+/** Interrupt flags for SETUP transaction. */
+#define USB_D_SETUP_INT_FLAGS (USB_DEVICE_EPINTFLAG_RXSTP)
+
+/** Interrupt flags for BANK1 transactions. */
+#define USB_D_BANK1_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1 | USB_DEVICE_EPINTFLAG_STALL1)
+
+/** Interrupt flags for BANK0 transactions. */
+#define USB_D_BANK0_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT0 | USB_DEVICE_EPINTFLAG_TRFAIL0 | USB_DEVICE_EPINTFLAG_STALL0)
+
+/** Interrupt flags for SETUP/IN/OUT transactions. */
+#define USB_D_ALL_INT_FLAGS (0x7F)
+
+/** Interrupt flags for WAKEUP event. */
+#define USB_D_WAKEUP_INT_FLAGS (USB_DEVICE_INTFLAG_UPRSM | USB_DEVICE_INTFLAG_EORSM | USB_DEVICE_INTFLAG_WAKEUP)
+
+/** Interrupt flags for SUSPEND event. */
+#define USB_D_SUSPEND_INT_FLAGS (USB_DEVICE_INTFLAG_LPMSUSP | USB_DEVICE_INTFLAG_SUSPEND)
+
+/** Max data bytes for a single DMA transfer. */
+#define USB_D_DEV_TRANS_MAX 8192 /* 14-bits, uses 13-bits. */
+
+/** Endpoint type setting to disable. */
+#define USB_D_EPTYPE_DISABLE 0
+
+/** Endpoint type setting to work as control endpoint. */
+#define USB_D_EPTYPE_CTRL 1
+
+/** Endpoint type setting to work as isochronous endpoint. */
+#define USB_D_EPTYPE_ISOCH 2
+
+/** Endpoint type setting to work as interrupt endpoint. */
+#define USB_D_EPTYPE_INT 3
+
+/** Endpoint type setting to work as bulk endpoint. */
+#define USB_D_EPTYPE_BULK 4
+
+/** Endpoint type setting for dual bank endpoint. */
+#define USB_D_EPTYPE_DUAL 5
+
+/** EPCFG register value for control endpoints. */
+#define USB_D_EPCFG_CTRL 0x11
+
+/** HPL USB device struct. */
+struct _usb_d_dev {
+	/** Callbacks of USB device. */
+	struct _usb_d_dev_callbacks callbacks;
+	/** Endpoint transaction callbacks. */
+	struct _usb_d_dev_ep_callbacks ep_callbacks;
+	/** Endpoints (ep0 + others). */
+	struct _usb_d_dev_ep ep[USB_D_N_EP];
+};
+
+/** Private data for SAM0 USB peripheral.
+ */
+typedef struct _usb_d_dev_prvt {
+	/** USB device descriptor table for peripheral to work. */
+	UsbDeviceDescriptor desc_table[CONF_USB_D_MAX_EP_N + 1];
+} usb_d_dev_prvt_t;
+
+/*@}*/
+
+/** USB device driver instance. */
+static struct _usb_d_dev dev_inst;
+
+/** USB device driver private data instance. */
+static struct _usb_d_dev_prvt prvt_inst;
+
+static void _usb_d_dev_reset_epts(void);
+
+static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t status);
+static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code);
+
+static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr);
+static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr);
+
+static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept);
+
+/** \brief ACK the endpoint interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] flags Interrupt flags.
+ */
+static inline void _usbd_ep_int_ack(uint8_t epn, uint32_t flags)
+{
+	hri_usbendpoint_clear_EPINTFLAG_reg(USB, epn, flags);
+}
+
+/** \brief Enable the endpoint interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] flags Interrupt flags.
+ */
+static inline void _usbd_ep_int_en(uint8_t epn, uint32_t flags)
+{
+	hri_usbendpoint_set_EPINTEN_reg(USB, epn, flags);
+}
+
+/** \brief Disable the endpoint interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] flags Interrupt flags.
+ */
+static inline void _usbd_ep_int_dis(uint8_t epn, uint32_t flags)
+{
+	hri_usbendpoint_clear_EPINTEN_reg(USB, epn, flags);
+}
+
+/** \brief Check if endpoint is control endpoint
+ * \param[in] epn Endpoint number.
+ */
+static inline bool _usbd_ep_is_ctrl(uint8_t epn)
+{
+	return (hri_usbendpoint_read_EPCFG_reg(USB, epn) == USB_D_EPCFG_CTRL);
+}
+
+/** \brief Set endpoint stall
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] st Stall status.
+ */
+static inline void _usbd_ep_set_stall(uint8_t epn, uint8_t bank_n, bool st)
+{
+	if (st) {
+		hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
+	} else {
+		hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
+	}
+}
+
+/** \brief Check if the endpoint is stalled
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \return \c true if it's stalled.
+ */
+static inline bool _usbd_ep_is_stalled(uint8_t epn, uint8_t bank_n)
+{
+	Usb *hw = USB;
+	return (hri_usbendpoint_read_EPSTATUS_reg(hw, epn) & (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
+}
+
+/** \brief Check if stall has been sent from the endpoint
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \return \c true if it's sent.
+ */
+static inline bool _usbd_ep_is_stall_sent(uint8_t epn, uint8_t bank_n)
+{
+	Usb *hw = USB;
+	return (hri_usbendpoint_read_EPINTFLAG_reg(hw, epn) & (USB_DEVICE_EPINTFLAG_STALL0 << bank_n));
+}
+
+/** \brief ACK endpoint STALL interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ */
+static inline void _usbd_ep_ack_stall(uint8_t epn, uint8_t bank_n)
+{
+	_usbd_ep_int_ack(epn, (USB_DEVICE_EPINTFLAG_STALL0 << bank_n));
+}
+
+/** \brief Enable/disable endpoint STALL interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] en \c true to enable, \c false to disable.
+ */
+static inline void _usbd_ep_int_stall_en(uint8_t epn, uint8_t bank_n, const bool en)
+{
+	if (en) {
+		_usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n);
+	} else {
+		_usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n);
+	}
+}
+
+/** \brief Stop SETUP transactions
+ * \param[in] epn Endpoint number.
+ */
+static inline void _usbd_ep_stop_setup(uint8_t epn)
+{
+	hri_usbendpoint_clear_EPINTEN_RXSTP_bit(USB, epn);
+}
+
+/** \brief Check if SETUP packet is ready in cache
+ * \param[in] epn Endpoint number.
+ */
+static inline bool _usbd_ep_is_setup(uint8_t epn)
+{
+	return hri_usbendpoint_get_EPINTFLAG_reg(USB, epn, USB_DEVICE_EPINTFLAG_RXSTP);
+}
+
+/** \brief ACK endpoint SETUP interrupt
+ * \param[in] epn Endpoint number.
+ */
+static inline void _usbd_ep_ack_setup(uint8_t epn)
+{
+	_usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_RXSTP);
+}
+
+/** \brief Set endpoint toggle value
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] tgl Toggle value.
+ */
+static inline void _usbd_ep_set_toggle(uint8_t epn, uint8_t bank_n, uint8_t tgl)
+{
+	if (tgl) {
+		hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n));
+	} else {
+		hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n));
+	}
+}
+
+/** \brief ACK IN/OUT complete interrupt
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ */
+static inline void _usbd_ep_ack_io_cpt(uint8_t epn, uint8_t bank_n)
+{
+	_usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_TRCPT0 << bank_n);
+}
+
+/** \brief Set DMA buffer used for bank data
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] addr DMA buffer address to set.
+ */
+static inline void _usbd_ep_set_buf(uint8_t epn, uint8_t bank_n, uint32_t addr)
+{
+	UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->ADDR.reg          = addr;
+}
+
+/** \brief Set bank count for IN transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] count Data count for IN.
+ */
+static inline void _usbd_ep_set_in_count(uint8_t epn, uint8_t bank_n, uint16_t count)
+{
+	UsbDeviceDescBank *bank             = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->PCKSIZE.bit.MULTI_PACKET_SIZE = count;
+}
+
+/** \brief Set bank size for IN transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] size Data size for IN.
+ */
+static inline void _usbd_ep_set_in_size(uint8_t epn, uint8_t bank_n, uint16_t size)
+{
+	UsbDeviceDescBank *bank      = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->PCKSIZE.bit.BYTE_COUNT = size;
+}
+
+/** \brief Set bank count for OUT transaction
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] count Data count for OUT.
+ */
+static inline void _usbd_ep_set_out_count(uint8_t epn, uint8_t bank_n, uint16_t count)
+{
+	UsbDeviceDescBank *bank      = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->PCKSIZE.bit.BYTE_COUNT = count;
+}
+
+/** \brief Set bank size for OUT transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] size Data size for OUT.
+ */
+static inline void _usbd_ep_set_out_size(uint8_t epn, uint8_t bank_n, uint16_t size)
+{
+	UsbDeviceDescBank *bank             = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->PCKSIZE.bit.MULTI_PACKET_SIZE = size;
+}
+
+/** Set bank size and count for IN transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] size Data size.
+ * \param[in] count Initial data count.
+ */
+static inline void _usbd_ep_set_in_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count)
+{
+	_usbd_ep_set_in_size(epn, bank_n, size);
+	_usbd_ep_set_in_count(epn, bank_n, count);
+}
+
+/** \brief Set bank size and count for OUT transaction
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] size Data size.
+ * \param[in] count Initial data count.
+ */
+static inline void _usbd_ep_set_out_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count)
+{
+	_usbd_ep_set_out_size(epn, bank_n, size);
+	_usbd_ep_set_out_count(epn, bank_n, count);
+}
+
+/** \brief Clear bank status
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ */
+static inline void _usbd_ep_clear_bank_status(uint8_t epn, uint8_t bank_n)
+{
+	UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
+	bank->STATUS_BK.reg     = 0;
+}
+
+/** Set IN ready for IN transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] rdy Set to \c true to indicate IN packet ready to TX.
+ */
+static inline void _usbd_ep_set_in_rdy(uint8_t epn, uint8_t bank_n, const bool rdy)
+{
+	if (rdy) {
+		hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
+	} else {
+		hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
+	}
+}
+
+/** \brief Set bank ready for OUT transactions
+ * \param[in] epn Endpoint number.
+ * \param[in] bank_n Endpoint bank number.
+ * \param[in] rdy Set to \c true to indicate OUT bank ready to RX.
+ */
+static inline void _usbd_ep_set_out_rdy(uint8_t epn, uint8_t bank_n, const bool rdy)
+{
+	if (rdy) {
+		hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
+	} else {
+		hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
+	}
+}
+
+/**
+ *  \brief Convert USB endpoint size to HW PCKSIZE.SIZE
+ * \param[in] n Number of bytes of endpoint size.
+ */
+static inline uint8_t _usbd_ep_pcksize_size(uint16_t n)
+{
+	return (
+	    (n > 512)
+	        ? 7
+	        : ((n > 256) ? 6 : ((n > 128) ? 5 : ((n > 64) ? 4 : ((n > 32) ? 3 : ((n > 16) ? 2 : ((n > 8) ? 1 : 0)))))));
+}
+
+/**
+ *  \brief Obtain endpoint descriptor pointer
+ * \param[in] epn Endpoint number.
+ * \param[in] dir Endpoint direction.
+ */
+static inline struct _usb_d_dev_ep *_usb_d_dev_ept(uint8_t epn, bool dir)
+{
+	uint8_t ep_index = (epn == 0) ? 0 : (dir ? (epn + CONF_USB_D_MAX_EP_N) : epn);
+	return &dev_inst.ep[ep_index];
+}
+
+/**
+ * \brief Handles USB SOF interrupt
+ */
+static inline void _usb_d_dev_sof(void)
+{
+	/* ACK SOF interrupt. */
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_SOF);
+	dev_inst.callbacks.sof();
+}
+
+/**
+ * \brief Handles USB LPM Suspend interrupt
+ */
+static inline void _usb_d_dev_lpmsusp(void)
+{
+	uint8_t  i;
+	uint32_t lpm_variable = 0;
+
+	/* ACK LPMSUSP interrupt. */
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+	/* Change interrupt masks */
+	hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+	hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
+
+	/* Find LPM data */
+	for (i = 0; i < CONF_USB_D_MAX_EP_N; i++) {
+		UsbDeviceDescBank *bank = &prvt_inst.desc_table[i].DeviceDescBank[0];
+		if (bank->EXTREG.bit.SUBPID == 0x3) {
+			/* Save LPM variable */
+			lpm_variable = bank->EXTREG.bit.VARIABLE;
+			/* Clear */
+			bank->EXTREG.reg = 0;
+			break;
+		}
+	}
+	dev_inst.callbacks.event(USB_EV_LPM_SUSPEND, lpm_variable);
+}
+
+/**
+ * \brief Handles USB RAM Error interrupt
+ */
+static inline void _usb_d_dev_ramerr(void)
+{
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_RAMACER);
+	dev_inst.callbacks.event(USB_EV_ERROR, 0);
+}
+
+/**
+ * \brief Handles USB resume/wakeup interrupts
+ */
+static inline void _usb_d_dev_wakeup(void)
+{
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_WAKEUP_INT_FLAGS);
+	hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
+	hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+
+	_usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC);
+	dev_inst.callbacks.event(USB_EV_WAKEUP, 0);
+}
+
+/**
+ * \brief Handles USB signal reset interrupt
+ */
+static inline void _usb_d_dev_reset(void)
+{
+	/* EP0 will not be reseted by USB RESET, disable manually. */
+	hri_usbendpoint_write_EPCFG_reg(USB, 0, 0);
+
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_EORST);
+	hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
+	hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+
+	_usb_d_dev_reset_epts();
+	dev_inst.callbacks.event(USB_EV_RESET, 0);
+}
+
+static inline void _usb_d_dev_suspend(void)
+{
+	hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+	hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
+	hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
+
+	dev_inst.callbacks.event(USB_EV_SUSPEND, 0);
+}
+
+/**
+ * \brief Handles USB non-endpoint interrupt
+ */
+static inline bool _usb_d_dev_handle_nep(void)
+{
+	bool     rc    = true;
+	uint16_t flags = hri_usbdevice_read_INTFLAG_reg(USB);
+	flags &= hri_usbdevice_read_INTEN_reg(USB);
+
+	if (flags & USB_DEVICE_INTFLAG_SOF) {
+		_usb_d_dev_sof();
+		return true;
+	}
+	if (flags & USB_DEVICE_INTFLAG_LPMSUSP) {
+		_usb_d_dev_lpmsusp();
+	} else if (flags & USB_DEVICE_INTFLAG_RAMACER) {
+		_usb_d_dev_ramerr();
+	} else if (flags & USB_D_WAKEUP_INT_FLAGS) {
+		_usb_d_dev_wakeup();
+	} else if (flags & USB_DEVICE_INTFLAG_EORST) {
+		_usb_d_dev_reset();
+	} else if (flags & USB_DEVICE_INTFLAG_SUSPEND) {
+		_usb_d_dev_suspend();
+	} else {
+		rc = false;
+	}
+	return rc;
+}
+
+/**
+ * \brief Prepare next IN transactions
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] isr Invoked from ISR.
+ */
+static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr)
+{
+	Usb *              hw          = USB;
+	uint8_t            epn         = USB_EP_GET_N(ept->ep);
+	UsbDeviceDescBank *bank        = &prvt_inst.desc_table[epn].DeviceDescBank[0];
+	uint16_t           trans_count = isr ? bank[1].PCKSIZE.bit.BYTE_COUNT : 0;
+	uint16_t           trans_next;
+	uint16_t           last_pkt = trans_count & ((ept->size == 1023) ? ept->size : (ept->size - 1));
+	uint8_t            inten    = 0;
+	bool               is_ctrl  = _usb_d_dev_ep_is_ctrl(ept);
+
+	if (isr) {
+		_usbd_ep_ack_io_cpt(epn, 1);
+	}
+
+	ept->trans_count += trans_count;
+	/* Send more data. */
+	if (ept->trans_count < ept->trans_size) {
+		trans_next = ept->trans_size - ept->trans_count;
+		if (ept->flags.bits.use_cache) {
+			if (trans_next > ept->size) {
+				trans_next = ept->size;
+			}
+			memcpy(ept->cache, &ept->trans_buf[ept->trans_count], trans_next);
+			_usbd_ep_set_buf(epn, 1, (uint32_t)ept->cache);
+		} else {
+			if (trans_next > USB_D_DEV_TRANS_MAX) {
+				trans_next = USB_D_DEV_TRANS_MAX;
+			}
+			_usbd_ep_set_buf(epn, 1, (uint32_t)&ept->trans_buf[ept->trans_count]);
+		}
+		_usbd_ep_set_in_trans(epn, 1, trans_next, 0);
+		goto _in_tx_exec;
+	} else if (ept->flags.bits.need_zlp) {
+		ept->flags.bits.need_zlp = 0;
+		_usbd_ep_set_in_trans(epn, 1, 0, 0);
+		goto _in_tx_exec;
+	}
+	/* Complete. */
+	if (is_ctrl) {
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRCPT0);
+	} else {
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS);
+	}
+
+	/* No ping-pong, so ask more data without background transfer. */
+	if (last_pkt == ept->size) {
+		ept->flags.bits.is_busy = 0;
+		if (dev_inst.ep_callbacks.more(ept->ep, ept->trans_count)) {
+			/* More data added. */
+			return;
+		}
+		ept->flags.bits.is_busy = 1;
+	}
+	/* Finish normally. */
+	_usb_d_dev_trans_done(ept, USB_TRANS_DONE);
+	return;
+
+_in_tx_exec:
+	if (!isr) {
+		if (is_ctrl) {
+			/* Control endpoint: SETUP or OUT will abort IN transaction.
+			 * SETUP: terminate the IN without any notification. Trigger
+			 *        SETUP callback.
+			 * OUT NAK: terminate IN.
+			 */
+			inten = USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL0;
+		} else {
+			/* Initialize normal IN transaction. */
+			inten = USB_D_BANK1_INT_FLAGS;
+		}
+		hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten);
+	}
+	_usbd_ep_set_in_rdy(epn, 1, true);
+}
+
+/**
+ * \brief Prepare next OUT transactions
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] isr Invoked from ISR.
+ */
+static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr)
+{
+	Usb *              hw         = USB;
+	uint8_t            epn        = USB_EP_GET_N(ept->ep);
+	UsbDeviceDescBank *bank       = &prvt_inst.desc_table[epn].DeviceDescBank[0];
+	uint16_t           trans_size = isr ? bank->PCKSIZE.bit.MULTI_PACKET_SIZE : 0;
+	uint16_t           last_trans = isr ? bank->PCKSIZE.bit.BYTE_COUNT : 0;
+	uint16_t           size_mask  = (ept->size == 1023) ? 1023 : (ept->size - 1);
+	uint16_t           last_pkt   = last_trans & size_mask;
+	uint16_t           trans_next;
+	uint8_t            inten;
+	bool               is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
+
+	if (isr) {
+		_usbd_ep_ack_io_cpt(epn, 0);
+	}
+
+	/* If cache is used, copy data to buffer. */
+	if (ept->flags.bits.use_cache && ept->trans_size) {
+		uint16_t buf_remain = ept->trans_size - ept->trans_count;
+		memcpy(&ept->trans_buf[ept->trans_count], ept->cache, (buf_remain > last_pkt) ? last_pkt : buf_remain);
+	}
+
+	/* Force wait ZLP */
+	if (ept->trans_size == 0 && ept->flags.bits.need_zlp) {
+		ept->flags.bits.need_zlp  = 0;
+		ept->flags.bits.use_cache = 1;
+		_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
+		_usbd_ep_set_out_trans(epn, 0, ept->size, 0);
+		goto _out_rx_exec;
+	} else if (isr && last_pkt < ept->size) {
+		/* Short packet. */
+		ept->flags.bits.need_zlp = 0;
+		ept->trans_count += last_trans;
+	} else {
+		/* Full packets. */
+		ept->trans_count += trans_size;
+
+		/* Wait more data */
+		if (ept->trans_count < ept->trans_size) {
+			/* Continue OUT */
+			trans_next = ept->trans_size - ept->trans_count;
+			if (ept->flags.bits.use_cache) {
+				/* Expect single packet each time. */
+				if (trans_next > ept->size) {
+					trans_next = ept->size;
+				}
+				_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
+			} else {
+				/* Multiple packets each time. */
+				if (trans_next > ept->size) {
+					if (trans_next > USB_D_DEV_TRANS_MAX) {
+						trans_next = USB_D_DEV_TRANS_MAX;
+					} else {
+						/* Must expect multiple of ep size. */
+						trans_next -= trans_next & size_mask;
+					}
+				} else if (trans_next < ept->size) {
+					/* Last un-aligned packet should be cached. */
+					ept->flags.bits.use_cache = 1;
+				}
+				_usbd_ep_set_buf(epn, 0, (uint32_t)&ept->trans_buf[ept->trans_count]);
+			}
+			_usbd_ep_set_out_trans(epn, 0, trans_next, 0);
+			goto _out_rx_exec;
+		}
+	}
+	/* Finish normally. */
+	if (is_ctrl) {
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1);
+	} else {
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS);
+	}
+	/* Use ep0 out cache for next setup packets */
+	if (0 == epn) {
+		_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
+	}
+	_usb_d_dev_trans_done(ept, USB_TRANS_DONE);
+	return;
+
+_out_rx_exec:
+	if (!isr) {
+		if (is_ctrl) {
+			/* Initialize control OUT transaction. */
+
+			/* Control transfer: SETUP or IN request will abort the
+			 *                   OUT transactions.
+			 * SETUP: terminate OUT without any notification.
+			 *        Trigger SETUP notification.
+			 * IN NAK: finish OUT normally. Notify data done.
+			 */
+			_usbd_ep_clear_bank_status(epn, 1);
+			/* Detect OUT, SETUP, NAK IN */
+			inten = USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1;
+		} else {
+			/* Initialize normal OUT transaction. */
+			inten = USB_D_BANK0_INT_FLAGS;
+		}
+		hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten);
+	}
+	_usbd_ep_set_out_rdy(epn, 0, true);
+}
+
+/**
+ * \brief Handles setup received interrupt
+ * \param[in] ept Pointer to endpoint information.
+ */
+static void _usb_d_dev_handle_setup(struct _usb_d_dev_ep *ept)
+{
+	uint8_t epn     = USB_EP_GET_N(ept->ep);
+	bool    is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
+
+	if (!is_ctrl) {
+		/* Should never be here! */
+		_usbd_ep_ack_setup(epn);
+		_usbd_ep_stop_setup(epn);
+		return;
+	}
+	/* Control transfer:
+	 * SETUP transaction will terminate IN/OUT transaction,
+	 * and start new transaction with received SETUP packet.
+	 */
+	if (_usb_d_dev_ep_is_busy(ept)) {
+		ept->flags.bits.is_busy = 0;
+
+		/* Stop transfer on either direction. */
+		_usbd_ep_set_in_rdy(epn, 1, false);
+		_usbd_ep_set_out_rdy(epn, 0, false);
+	}
+	ept->flags.bits.is_stalled = 0;
+
+	/* Clear status and notify SETUP */
+	_usbd_ep_clear_bank_status(epn, 0);
+	_usbd_ep_clear_bank_status(epn, 1);
+	_usbd_ep_int_ack(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS);
+	_usbd_ep_int_dis(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS);
+	/* Invoke callback. */
+	dev_inst.ep_callbacks.setup(ept->ep);
+}
+
+/**
+ * \brief Handles stall sent interrupt
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] bank_n Bank number.
+ */
+static void _usb_d_dev_handle_stall(struct _usb_d_dev_ep *ept, const uint8_t bank_n)
+{
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+	/* Clear interrupt enable. Leave status there for status check. */
+	_usbd_ep_int_stall_en(epn, bank_n, false);
+	dev_inst.ep_callbacks.done(ept->ep, USB_TRANS_STALL, ept->trans_count);
+}
+
+/**
+ * \brief Handles transaction fail interrupt
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] bank_n Bank number.
+ */
+static void _usb_d_dev_handle_trfail(struct _usb_d_dev_ep *ept, const uint8_t bank_n)
+{
+	Usb *              hw      = USB;
+	uint8_t            epn     = USB_EP_GET_N(ept->ep);
+	const uint8_t      fail[2] = {USB_DEVICE_EPINTFLAG_TRFAIL0, USB_DEVICE_EPINTFLAG_TRFAIL1};
+	UsbDeviceDescBank *bank    = prvt_inst.desc_table[epn].DeviceDescBank;
+	uint8_t            eptype
+	    = bank_n ? hri_usbendpoint_read_EPCFG_EPTYPE1_bf(hw, epn) : hri_usbendpoint_read_EPCFG_EPTYPE0_bf(hw, epn);
+	bool                      is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
+	USB_DEVICE_STATUS_BK_Type st;
+	st.reg = bank[bank_n].STATUS_BK.reg;
+
+	if ((eptype == USB_D_EPTYPE_ISOCH) && st.bit.CRCERR) {
+		bank[bank_n].STATUS_BK.bit.CRCERR = 0;
+		hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
+		_usb_d_dev_trans_stop(ept, bank_n, USB_TRANS_ERROR);
+	} else if (st.bit.ERRORFLOW) {
+		bank[bank_n].STATUS_BK.bit.ERRORFLOW = 0;
+		hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
+		/* Abort control transfer. */
+		if (is_ctrl && _usb_d_dev_ep_is_busy(ept)) {
+			if (bank_n != _usb_d_dev_ep_is_in(ept)) {
+				_usb_d_dev_trans_stop(ept, _usb_d_dev_ep_is_in(ept), USB_TRANS_DONE);
+			}
+		}
+	} else {
+		_usbd_ep_clear_bank_status(epn, bank_n);
+		hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
+	}
+}
+
+/**
+ * \brief Analyze flags for setup transaction
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] flags Endpoint interrupt flags.
+ */
+static inline void _usb_d_dev_trans_setup_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
+{
+	/*
+	 * SETPU is automatically ACKed by hardware
+	 * OUT & IN should be set to NAK when checking SETUP
+	 * No need to check OUT & IN status.
+	 */
+	if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
+		_usb_d_dev_handle_setup(ept);
+	} else if (flags & USB_DEVICE_EPINTFLAG_STALL1) {
+		_usb_d_dev_handle_stall(ept, 1);
+	} else if (flags & USB_DEVICE_EPINTFLAG_STALL0) {
+		_usb_d_dev_handle_stall(ept, 0);
+	}
+}
+
+/**
+ * \brief Analyze flags for IN transactions
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] flags Endpoint interrupt flags.
+ */
+static inline void _usb_d_dev_trans_in_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
+{
+	/*
+	 * Check IN flags
+	 * If control endpoint, SETUP & OUT is checked to see if abort
+	 */
+	if (flags & USB_DEVICE_EPINTFLAG_STALL1) {
+		_usb_d_dev_handle_stall(ept, 1);
+	} else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) {
+		_usb_d_dev_handle_trfail(ept, 1);
+	} else if (flags & USB_DEVICE_EPINTFLAG_TRCPT1) {
+		_usb_d_dev_in_next(ept, true);
+	} else if (_usb_d_dev_ep_is_ctrl(ept)) {
+		/* Check OUT NAK
+		 * Check SETUP
+		 */
+		if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) {
+			_usb_d_dev_handle_trfail(ept, 0);
+		} else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
+			_usb_d_dev_handle_setup(ept);
+		}
+	}
+}
+
+/**
+ * \brief Analyze flags for OUT transactions
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] flags Endpoint interrupt flags.
+ */
+static inline void _usb_d_dev_trans_out_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
+{
+	/*
+	 * Check OUT flags.
+	 * If control endpoint, SETUP & IN NAK is checked to see if abort
+	 */
+	if (flags & USB_DEVICE_EPINTFLAG_STALL0) {
+		_usb_d_dev_handle_stall(ept, 0);
+	} else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) {
+		_usb_d_dev_handle_trfail(ept, 0);
+	} else if (flags & USB_DEVICE_EPINTFLAG_TRCPT0) {
+		_usb_d_dev_out_next(ept, true);
+	} else if (_usb_d_dev_ep_is_ctrl(ept)) {
+		/* Check IN NAK
+		 * Check SETUP
+		 */
+		if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) {
+			_usb_d_dev_handle_trfail(ept, 1);
+		} else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
+			_usb_d_dev_handle_setup(ept);
+		}
+	}
+}
+
+/**
+ * \brief Handles the endpoint interrupts.
+ * \param[in] epint Endpoint interrupt summary (by bits).
+ * \param[in] ept Pointer to endpoint information.
+ */
+static inline void _usb_d_dev_handle_eps(uint32_t epint, struct _usb_d_dev_ep *ept)
+{
+	Usb *hw = USB;
+
+	uint8_t flags, mask;
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+
+	if (!(epint & (1u << epn))) {
+		return;
+	}
+	flags = hw->DEVICE.DeviceEndpoint[epn].EPINTFLAG.reg;
+	mask  = hw->DEVICE.DeviceEndpoint[epn].EPINTENSET.reg;
+	flags &= mask;
+	if (flags) {
+		if ((ept->flags.bits.eptype == 0x1) && !_usb_d_dev_ep_is_busy(ept)) {
+			_usb_d_dev_trans_setup_isr(ept, flags);
+		} else if (_usb_d_dev_ep_is_in(ept)) {
+			_usb_d_dev_trans_in_isr(ept, flags);
+		} else {
+			_usb_d_dev_trans_out_isr(ept, flags);
+		}
+	}
+}
+
+/**
+ * \brief USB device interrupt handler
+ * \param[in] unused The parameter is not used
+ */
+static void _usb_d_dev_handler(void)
+{
+	Usb *   hw = USB;
+	uint8_t i;
+
+	uint16_t epint = hw->DEVICE.EPINTSMRY.reg;
+	if (0 == epint) {
+		if (_usb_d_dev_handle_nep()) {
+			return;
+		}
+	}
+	/* Handle endpoints */
+	for (i = 0; i < USB_D_N_EP; i++) {
+		struct _usb_d_dev_ep *ept = &dev_inst.ep[i];
+		if (ept->ep == 0xFF) {
+			continue;
+		}
+		_usb_d_dev_handle_eps(epint, ept);
+	}
+}
+
+/**
+ * \brief Reset all endpoint software instances
+ */
+static void _usb_d_dev_reset_epts(void)
+{
+	uint8_t i;
+	for (i = 0; i < USB_D_N_EP; i++) {
+		_usb_d_dev_trans_done(&dev_inst.ep[i], USB_TRANS_RESET);
+		dev_inst.ep[i].ep       = 0xFF;
+		dev_inst.ep[i].flags.u8 = 0;
+	}
+	memset(prvt_inst.desc_table, 0, sizeof(UsbDeviceDescriptor) * (CONF_USB_D_MAX_EP_N + 1));
+}
+
+int32_t _usb_d_dev_init(void)
+{
+	Usb *         hw         = USB;
+	uint8_t       speed      = CONF_USB_D_SPEED;
+	const uint8_t spdconf[4] = {
+	    USB_DEVICE_CTRLB_SPDCONF(1), /* LS */
+	    USB_DEVICE_CTRLB_SPDCONF(0), /* FS */
+	    0,
+	    0 /* Reserved */
+	};
+
+	if (!hri_usbdevice_is_syncing(hw, USB_SYNCBUSY_SWRST)) {
+		if (hri_usbdevice_get_CTRLA_reg(hw, USB_CTRLA_ENABLE)) {
+			hri_usbdevice_clear_CTRLA_ENABLE_bit(hw);
+			hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_ENABLE);
+		}
+		hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_SWRST);
+	}
+	hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_SWRST);
+
+	dev_inst.callbacks.sof   = (_usb_d_dev_sof_cb_t)_dummy_func_no_return;
+	dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)_dummy_func_no_return;
+
+	dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)_dummy_func_no_return;
+	dev_inst.ep_callbacks.more  = (_usb_d_dev_ep_cb_more_t)_dummy_func_no_return;
+	dev_inst.ep_callbacks.done  = (_usb_d_dev_ep_cb_done_t)_dummy_func_no_return;
+
+	_usb_d_dev_reset_epts();
+
+	_usb_load_calib();
+
+	hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_RUNSTDBY);
+	hri_usbdevice_write_DESCADD_reg(hw, (uint32_t)prvt_inst.desc_table);
+	hri_usbdevice_write_CTRLB_reg(hw, spdconf[speed] | USB_DEVICE_CTRLB_DETACH);
+
+	return ERR_NONE;
+}
+
+void _usb_d_dev_deinit(void)
+{
+	Usb *hw = USB;
+
+	while (_usb_d_dev_disable() < 0)
+		;
+
+	hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_SWRST);
+
+	NVIC_DisableIRQ(USB_0_IRQn);
+	NVIC_ClearPendingIRQ(USB_0_IRQn);
+	NVIC_DisableIRQ(USB_1_IRQn);
+	NVIC_ClearPendingIRQ(USB_1_IRQn);
+	NVIC_DisableIRQ(USB_2_IRQn);
+	NVIC_ClearPendingIRQ(USB_2_IRQn);
+	NVIC_DisableIRQ(USB_3_IRQn);
+	NVIC_ClearPendingIRQ(USB_3_IRQn);
+}
+
+int32_t _usb_d_dev_enable(void)
+{
+	Usb *   hw = USB;
+	uint8_t ctrla;
+
+	if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) {
+		return -USB_ERR_DENIED;
+	}
+	ctrla = hri_usbdevice_read_CTRLA_reg(hw);
+	if ((ctrla & USB_CTRLA_ENABLE) == 0) {
+		hri_usbdevice_write_CTRLA_reg(hw, ctrla | USB_CTRLA_ENABLE);
+	}
+
+	NVIC_EnableIRQ(USB_0_IRQn);
+	NVIC_EnableIRQ(USB_1_IRQn);
+	NVIC_EnableIRQ(USB_2_IRQn);
+	NVIC_EnableIRQ(USB_3_IRQn);
+
+	hri_usbdevice_set_INTEN_reg(hw,
+	                            USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER
+	                                | USB_D_SUSPEND_INT_FLAGS);
+
+	return ERR_NONE;
+}
+
+int32_t _usb_d_dev_disable(void)
+{
+	Usb *   hw = USB;
+	uint8_t ctrla;
+
+	if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) {
+		return -USB_ERR_DENIED;
+	}
+
+	ctrla = hri_usbdevice_read_CTRLA_reg(hw);
+	if (ctrla & USB_CTRLA_ENABLE) {
+		hri_usbdevice_write_CTRLA_reg(hw, ctrla & ~USB_CTRLA_ENABLE);
+	}
+
+	NVIC_DisableIRQ(USB_0_IRQn);
+	NVIC_DisableIRQ(USB_1_IRQn);
+	NVIC_DisableIRQ(USB_2_IRQn);
+	NVIC_DisableIRQ(USB_3_IRQn);
+
+	hri_usbdevice_clear_INTEN_reg(hw,
+	                              USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER
+	                                  | USB_D_SUSPEND_INT_FLAGS | USB_D_WAKEUP_INT_FLAGS);
+
+	return ERR_NONE;
+}
+
+void _usb_d_dev_attach(void)
+{
+	hri_usbdevice_clear_CTRLB_DETACH_bit(USB);
+}
+
+void _usb_d_dev_detach(void)
+{
+	hri_usbdevice_set_CTRLB_DETACH_bit(USB);
+}
+
+#ifndef USB_FSMSTATUS_FSMSTATE_ON
+#define USB_FSMSTATUS_FSMSTATE_ON USB_FSMSTATUS_FSMSTATE(2ul)
+#endif
+void _usb_d_dev_send_remotewakeup(void)
+{
+	uint32_t retry = CONF_USB_RMT_WKUP_RETRY;
+	_usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC);
+	while ((USB_FSMSTATUS_FSMSTATE_ON != hri_usbdevice_read_FSMSTATUS_FSMSTATE_bf(USB)) && (retry--)) {
+		USB->DEVICE.CTRLB.bit.UPRSM = 1;
+	}
+}
+
+enum usb_speed _usb_d_dev_get_speed(void)
+{
+	uint8_t              sp       = (enum usb_speed)hri_usbdevice_read_STATUS_SPEED_bf(USB);
+	const enum usb_speed speed[2] = {USB_SPEED_FS, USB_SPEED_LS};
+
+	return speed[sp];
+}
+
+void _usb_d_dev_set_address(uint8_t addr)
+{
+	hri_usbdevice_write_DADD_reg(USB, USB_DEVICE_DADD_ADDEN | USB_DEVICE_DADD_DADD(addr));
+}
+
+uint8_t _usb_d_dev_get_address(void)
+{
+	uint8_t addr = hri_usbdevice_read_DADD_DADD_bf(USB);
+	return addr;
+}
+
+uint16_t _usb_d_dev_get_frame_n(void)
+{
+	uint16_t fn = hri_usbdevice_read_FNUM_FNUM_bf(USB);
+	return fn;
+}
+
+uint8_t _usb_d_dev_get_uframe_n(void)
+{
+	uint8_t ufn = hri_usbdevice_read_FNUM_MFNUM_bf(USB);
+	return ufn;
+}
+
+/**
+ *  \brief Start a setup transaction
+ *  \param[in] ept Endpoint information.
+ */
+static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept)
+{
+	Usb *   hw  = USB;
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+
+	_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
+	_usbd_ep_set_out_trans(epn, 0, ept->size, 0);
+
+	hri_usbendpoint_clear_EPSTATUS_reg(hw, epn, USB_DEVICE_EPSTATUS_STALLRQ(0x3) | USB_DEVICE_EPSTATUS_BK1RDY);
+	_usbd_ep_set_out_rdy(epn, 0, false);
+
+	hri_usbendpoint_set_EPINTEN_reg(hw, epn, USB_D_SETUP_INT_FLAGS);
+}
+
+int32_t _usb_d_dev_ep0_init(const uint8_t max_pkt_siz)
+{
+	return _usb_d_dev_ep_init(0, USB_EP_XTYPE_CTRL, max_pkt_siz);
+}
+
+int32_t _usb_d_dev_ep_init(const uint8_t ep, const uint8_t attr, const uint16_t max_pkt_siz)
+{
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+
+	uint8_t                        ep_type = attr & USB_EP_XTYPE_MASK;
+	const struct _usb_ep_cfg_item *pcfg    = &_usb_ep_cfgs[epn];
+
+	if (epn > CONF_USB_D_MAX_EP_N) {
+		return -USB_ERR_PARAM;
+	}
+	if (ept->ep != 0xFF) {
+		return -USB_ERR_REDO;
+	}
+	if (ep_type == USB_EP_XTYPE_CTRL) {
+		struct _usb_d_dev_ep *ept_in = _usb_d_dev_ept(epn, !dir);
+		if (ept_in->ep != 0xFF) {
+			return -USB_ERR_REDO;
+		}
+		if (pcfg->cache == NULL) {
+			return -USB_ERR_FUNC;
+		}
+	}
+	if ((dir ? pcfg->i_cache : pcfg->cache) && ((dir ? pcfg->i_size : pcfg->size) < max_pkt_siz)) {
+		return -USB_ERR_FUNC;
+	}
+
+	/* Initialize EP n settings */
+	ept->cache    = (uint8_t *)(dir ? pcfg->i_cache : pcfg->cache);
+	ept->size     = max_pkt_siz;
+	ept->flags.u8 = (ep_type + 1);
+	ept->ep       = ep;
+
+	return USB_OK;
+}
+
+void _usb_d_dev_ep_deinit(uint8_t ep)
+{
+	Usb *                 hw  = USB;
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+
+	if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) {
+		return;
+	}
+
+	/* Finish pending transactions. */
+	_usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET);
+
+	/* Disable the endpoint. */
+	if (_usb_d_dev_ep_is_ctrl(ept)) {
+		hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg = 0;
+	} else if (USB_EP_GET_DIR(ep)) {
+		hw->DEVICE.DeviceEndpoint[USB_EP_GET_N(ep)].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE1_Msk;
+	} else {
+		hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE0_Msk;
+	}
+	ept->flags.u8 = 0;
+	ept->ep       = 0xFF;
+}
+
+int32_t _usb_d_dev_ep_enable(const uint8_t ep)
+{
+	Usb *                 hw    = USB;
+	uint8_t               epn   = USB_EP_GET_N(ep);
+	bool                  dir   = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept   = _usb_d_dev_ept(epn, dir);
+	uint8_t               epcfg = hri_usbendpoint_read_EPCFG_reg(hw, epn);
+	UsbDeviceDescBank *   bank;
+
+	if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) {
+		return -USB_ERR_PARAM;
+	}
+
+	bank = prvt_inst.desc_table[epn].DeviceDescBank;
+	if (ept->flags.bits.eptype == USB_D_EPTYPE_CTRL) {
+		if (epcfg & (USB_DEVICE_EPCFG_EPTYPE1_Msk | USB_DEVICE_EPCFG_EPTYPE0_Msk)) {
+			return -USB_ERR_REDO;
+		}
+		hri_usbendpoint_write_EPCFG_reg(hw, epn, USB_D_EPCFG_CTRL);
+		bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size)
+		                      | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
+		bank[1].PCKSIZE.reg
+		    = USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
+		/* By default, control endpoint accept SETUP and NAK all other token. */
+		_usbd_ep_set_out_rdy(epn, 0, false);
+		_usbd_ep_set_in_rdy(epn, 1, false);
+
+		_usbd_ep_clear_bank_status(epn, 0);
+		_usbd_ep_clear_bank_status(epn, 1);
+
+		/* Enable SETUP reception for control endpoint. */
+		_usb_d_dev_trans_setup(ept);
+
+	} else if (dir) {
+		if (epcfg & USB_DEVICE_EPCFG_EPTYPE1_Msk) {
+			return -USB_ERR_REDO;
+		}
+		epcfg |= USB_DEVICE_EPCFG_EPTYPE1(ept->flags.bits.eptype);
+		hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);
+
+		bank[1].PCKSIZE.reg
+		    = USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
+
+		/* By default, IN endpoint will NAK all token. */
+		_usbd_ep_set_in_rdy(epn, 1, false);
+		_usbd_ep_clear_bank_status(epn, 1);
+
+	} else {
+
+		if (epcfg & USB_DEVICE_EPCFG_EPTYPE0_Msk) {
+			return -USB_ERR_REDO;
+		}
+		epcfg |= USB_DEVICE_EPCFG_EPTYPE0(ept->flags.bits.eptype);
+		hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);
+
+		bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size)
+		                      | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
+
+		/* By default, OUT endpoint will NAK all token. */
+		_usbd_ep_set_out_rdy(epn, 0, false);
+		_usbd_ep_clear_bank_status(epn, 0);
+	}
+
+	return USB_OK;
+}
+
+void _usb_d_dev_ep_disable(const uint8_t ep)
+{
+	Usb *                 hw  = USB;
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+
+	_usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET);
+	if (_usb_d_dev_ep_is_ctrl(ept)) {
+		hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_ALL_INT_FLAGS);
+	}
+}
+
+/**
+ * \brief Get endpoint stall status
+ * \param[in] ept Pointer to endpoint information.
+ * \param[in] dir Endpoint direction.
+ * \return Stall status.
+ * \retval \c true Endpoint is stalled.
+ * \retval \c false Endpoint is not stalled.
+ */
+static inline int32_t _usb_d_dev_ep_stall_get(struct _usb_d_dev_ep *ept, bool dir)
+{
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+	return _usbd_ep_is_stalled(epn, dir);
+}
+
+/**
+ * \brief Set endpoint stall
+ * \param[in, out] ept Pointer to endpoint information.
+ * \param[in] dir Endpoint direction.
+ * \return Always 0, success.
+ */
+static inline int32_t _usb_d_dev_ep_stall_set(struct _usb_d_dev_ep *ept, bool dir)
+{
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+	_usbd_ep_set_stall(epn, dir, true);
+	_usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir);
+	ept->flags.bits.is_stalled = 1;
+	/* In stall interrupt abort the transfer. */
+	return ERR_NONE;
+}
+
+/**
+ * \brief Clear endpoint stall
+ * \param[in, out] ept Pointer to endpoint information.
+ * \param[in] dir Endpoint direction.
+ * \return Always 0, success.
+ */
+static inline int32_t _usb_d_dev_ep_stall_clr(struct _usb_d_dev_ep *ept, bool dir)
+{
+	uint8_t epn        = USB_EP_GET_N(ept->ep);
+	bool    is_stalled = _usbd_ep_is_stalled(epn, dir);
+	if (!is_stalled) {
+		return ERR_NONE;
+	}
+	_usbd_ep_set_stall(epn, dir, false);
+	_usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir);
+	if (_usbd_ep_is_stall_sent(epn, dir)) {
+		_usbd_ep_ack_stall(epn, dir);
+		_usbd_ep_set_toggle(epn, dir, 0);
+	}
+	if (_usb_d_dev_ep_is_ctrl(ept)) {
+		if ((hri_usbendpoint_read_EPSTATUS_reg(USB, epn) & USB_DEVICE_EPSTATUS_STALLRQ_Msk) == 0) {
+			ept->flags.bits.is_stalled = 0;
+		}
+	} else {
+		ept->flags.bits.is_stalled = 0;
+	}
+	return ERR_NONE;
+}
+
+int32_t _usb_d_dev_ep_stall(const uint8_t ep, const enum usb_ep_stall_ctrl ctrl)
+{
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+	int32_t               rc;
+
+	if (epn > CONF_USB_D_MAX_EP_N) {
+		return -USB_ERR_PARAM;
+	}
+
+	if (USB_EP_STALL_SET == ctrl) {
+		rc = _usb_d_dev_ep_stall_set(ept, dir);
+	} else if (USB_EP_STALL_CLR == ctrl) {
+		rc = _usb_d_dev_ep_stall_clr(ept, dir);
+	} else {
+		rc = _usb_d_dev_ep_stall_get(ept, dir);
+	}
+	return rc;
+}
+
+/**
+ *  \brief Finish the transaction and invoke callback
+ * \param[in, out] ept Pointer to endpoint information.
+ * \param[in] code Information code passed.
+ */
+static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t code)
+{
+	if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) {
+		return;
+	}
+	ept->flags.bits.is_busy = 0;
+	dev_inst.ep_callbacks.done(ept->ep, code, ept->trans_count);
+}
+
+/**
+ *  \brief Terminate the transaction with specific status code
+ * \param[in, out] ept Pointer to endpoint information.
+ * \param[in] dir Endpoint direction.
+ * \param[in] code Information code passed.
+ */
+static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code)
+{
+	uint8_t epn = USB_EP_GET_N(ept->ep);
+	;
+	const uint8_t intflags[2] = {USB_D_BANK0_INT_FLAGS, USB_D_BANK1_INT_FLAGS};
+	if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) {
+		return;
+	}
+	/* Stop transfer */
+	if (dir) {
+		/* NAK IN */
+		_usbd_ep_set_in_rdy(epn, 1, false);
+	} else {
+		/* NAK OUT */
+		_usbd_ep_set_out_rdy(epn, 0, false);
+	}
+	_usbd_ep_int_ack(epn, intflags[dir]);
+	_usbd_ep_int_dis(epn, intflags[dir]);
+	_usb_d_dev_trans_done(ept, code);
+}
+
+int32_t _usb_d_dev_ep_read_req(const uint8_t ep, uint8_t *req_buf)
+{
+	uint8_t            epn   = USB_EP_GET_N(ep);
+	UsbDeviceDescBank *bank  = prvt_inst.desc_table[epn].DeviceDescBank;
+	uint32_t           addr  = bank[0].ADDR.reg;
+	uint16_t           bytes = bank[0].PCKSIZE.bit.BYTE_COUNT;
+
+	if (epn > CONF_USB_D_MAX_EP_N || !req_buf) {
+		return -USB_ERR_PARAM;
+	}
+	if (!_usbd_ep_is_ctrl(epn)) {
+		return -USB_ERR_FUNC;
+	}
+	if (!_usbd_ep_is_setup(epn)) {
+		return ERR_NONE;
+	}
+	memcpy(req_buf, (void *)addr, 8);
+	_usbd_ep_ack_setup(epn);
+
+	return bytes;
+}
+
+int32_t _usb_d_dev_ep_trans(const struct usb_d_transfer *trans)
+{
+	uint8_t               epn = USB_EP_GET_N(trans->ep);
+	bool                  dir = USB_EP_GET_DIR(trans->ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+
+	uint16_t size_mask      = (ept->size == 1023) ? 1023 : (ept->size - 1);
+	bool     size_n_aligned = (trans->size & size_mask);
+
+	bool use_cache = false;
+
+	volatile hal_atomic_t flags;
+
+	if (epn > CONF_USB_D_MAX_EP_N) {
+		return -USB_ERR_PARAM;
+	}
+
+	/* Cases that needs cache:
+	 * 1. Buffer not in RAM (cache all).
+	 * 2. IN/OUT with unaligned buffer (cache all).
+	 * 3. OUT with unaligned packet size (cache last packet).
+	 * 4. OUT size < 8 (sub-case for 3).
+	 */
+	if (!_usb_is_addr4dma(trans->buf, trans->size) || (!_usb_is_aligned(trans->buf))
+	    || (!dir && (trans->size < ept->size))) {
+		if (!ept->cache) {
+			return -USB_ERR_FUNC;
+		}
+		/* Use cache all the time. */
+		use_cache = true;
+	}
+	if (!dir && size_n_aligned) {
+		if (!ept->cache) {
+			return -USB_ERR_PARAM;
+		}
+		/* Set 'use_cache' on last packet. */
+	}
+
+	/* Check halt */
+	if (ept->flags.bits.is_stalled) {
+		return USB_HALTED;
+	}
+
+	/* Try to start transactions. */
+
+	atomic_enter_critical(&flags);
+	if (_usb_d_dev_ep_is_busy(ept)) {
+		atomic_leave_critical(&flags);
+		return USB_BUSY;
+	}
+	ept->flags.bits.is_busy = 1;
+	atomic_leave_critical(&flags);
+
+	/* Copy transaction information. */
+	ept->trans_buf   = trans->buf;
+	ept->trans_size  = trans->size;
+	ept->trans_count = 0;
+
+	ept->flags.bits.dir       = dir;
+	ept->flags.bits.use_cache = use_cache;
+	ept->flags.bits.need_zlp  = (trans->zlp && (!size_n_aligned));
+
+	if (dir) {
+		_usb_d_dev_in_next(ept, false);
+	} else {
+		_usb_d_dev_out_next(ept, false);
+	}
+
+	return ERR_NONE;
+}
+
+void _usb_d_dev_ep_abort(const uint8_t ep)
+{
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+	if (epn > CONF_USB_D_MAX_EP_N) {
+		return;
+	}
+	_usb_d_dev_trans_stop(ept, dir, USB_TRANS_ABORT);
+}
+
+int32_t _usb_d_dev_ep_get_status(const uint8_t ep, struct usb_d_trans_status *stat)
+{
+	uint8_t               epn = USB_EP_GET_N(ep);
+	bool                  dir = USB_EP_GET_DIR(ep);
+	struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
+	bool                  busy, stall;
+
+	if (epn > CONF_USB_D_MAX_EP_N) {
+		return USB_ERR_PARAM;
+	}
+	busy  = ept->flags.bits.is_busy;
+	stall = ept->flags.bits.is_stalled;
+	if (stat) {
+		stat->stall = stall;
+		stat->busy  = busy;
+		stat->setup = USB->DEVICE.DeviceEndpoint[epn].EPINTFLAG.bit.RXSTP;
+		stat->dir   = ept->flags.bits.dir;
+		stat->size  = ept->trans_size;
+		stat->count = ept->trans_count;
+		stat->ep    = ep;
+		stat->xtype = ept->flags.bits.eptype - 1;
+	}
+	if (stall) {
+		return USB_HALTED;
+	}
+	if (busy) {
+		return USB_BUSY;
+	}
+	return USB_OK;
+}
+
+void _usb_d_dev_register_callback(const enum usb_d_cb_type type, const FUNC_PTR func)
+{
+	FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func;
+	if (type == USB_D_CB_EVENT) {
+		dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)f;
+	} else if (type == USB_D_CB_SOF) {
+		dev_inst.callbacks.sof = (_usb_d_dev_sof_cb_t)f;
+	}
+}
+
+void _usb_d_dev_register_ep_callback(const enum usb_d_dev_ep_cb_type type, const FUNC_PTR func)
+{
+	FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func;
+	if (type == USB_D_DEV_EP_CB_SETUP) {
+		dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)f;
+	} else if (type == USB_D_DEV_EP_CB_MORE) {
+		dev_inst.ep_callbacks.more = (_usb_d_dev_ep_cb_more_t)f;
+	} else if (type == USB_D_DEV_EP_CB_DONE) {
+		dev_inst.ep_callbacks.done = (_usb_d_dev_ep_cb_done_t)f;
+	}
+}
+
+/**
+ * \brief USB interrupt handler
+ */
+void USB_0_Handler(void)
+{
+
+	_usb_d_dev_handler();
+}
+/**
+ * \brief USB interrupt handler
+ */
+void USB_1_Handler(void)
+{
+
+	_usb_d_dev_handler();
+}
+/**
+ * \brief USB interrupt handler
+ */
+void USB_2_Handler(void)
+{
+
+	_usb_d_dev_handler();
+}
+/**
+ * \brief USB interrupt handler
+ */
+void USB_3_Handler(void)
+{
+
+	_usb_d_dev_handler();
+}