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/hal/src/hal_usb_device.c b/sysmoOCTSIM/hal/src/hal_usb_device.c
new file mode 100644
index 0000000..aa80ede
--- /dev/null
+++ b/sysmoOCTSIM/hal/src/hal_usb_device.c
@@ -0,0 +1,592 @@
+/**
+ * \file
+ *
+ * \brief SAM USB device HAL
+ *
+ * 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 "hal_usb_device.h"
+#include "hal_atomic.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** USB device HAL driver version. */
+#define USB_D_VERSION 0x00000001u
+
+/**
+ *  Endpoint callbacks for data transfer.
+ */
+struct usb_d_ep_callbacks {
+	/** Callback that is invoked when setup packet is received. */
+	usb_d_ep_cb_setup_t req;
+	/** Callback invoked when buffer is done, but last packet is full size
+	 *  packet without ZLP. Return \c true if new transfer has been submitted.
+	 */
+	usb_d_ep_cb_more_t more;
+	/** Callback invoked when transfer is finished/halted/aborted or error
+	 *  occurs.
+	 */
+	usb_d_ep_cb_xfer_t xfer;
+};
+
+/**
+ *  Endpoint transfer descriptor header.
+ */
+struct usb_ep_xfer_hdr {
+	/** Transfer type, reuse \ref usb_ep_type. */
+	uint8_t type;
+	/** Endpoint address. */
+	uint8_t ep;
+	/** Endpoint state. */
+	uint8_t state;
+	/** Last status code. */
+	uint8_t status;
+};
+
+/**
+ *  Transfer descriptor.
+ */
+struct usb_ep_xfer {
+	/** General transfer descriptor. */
+	struct usb_ep_xfer_hdr hdr;
+	/** Pointer to data buffer. */
+	uint8_t *buf;
+	/** Transfer size. */
+	uint32_t size;
+	/** Control request packet. */
+	uint8_t req[8];
+};
+
+/**
+ *  USB device endpoint descriptor.
+ */
+struct usb_d_ep {
+	/** On-going transfer on the endpoint. */
+	struct usb_ep_xfer xfer;
+	/** Endpoint callbacks. */
+	struct usb_d_ep_callbacks callbacks;
+};
+
+/**
+ *  USB device HAL driver descriptor.
+ */
+struct usb_d_descriptor {
+	/** USB device endpoints. */
+	struct usb_d_ep ep[CONF_USB_D_NUM_EP_SP];
+};
+
+/** The USB HAL driver descriptor instance. */
+static struct usb_d_descriptor usb_d_inst;
+
+/** \brief Find the endpoint.
+ * \param[in] ep Endpoint address.
+ * \return Index of endpoint descriptor.
+ * \retval >=0 The index.
+ * \retval <0 Not found (endpoint is not initialized).
+ */
+static int8_t _usb_d_find_ep(const uint8_t ep)
+{
+	int8_t i;
+	for (i = 0; i < CONF_USB_D_NUM_EP_SP; i++) {
+		if (usb_d_inst.ep[i].xfer.hdr.ep == ep) {
+			return i;
+		}
+		if (usb_d_inst.ep[i].xfer.hdr.type == USB_EP_XTYPE_CTRL
+		    && (ep & USB_EP_N_MASK) == usb_d_inst.ep[i].xfer.hdr.ep) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+/**
+ * \brief Start transactions
+ * \param[in] ep Endpoint address.
+ * \param[in] dir Endpoint transfer direction.
+ * \param[in] buf Pointer to transfer buffer.
+ * \param[in] size Transfer size.
+ * \param[in] zlp Auto append ZLP for IN, or wait ZLP for OUT.
+ */
+static inline int32_t _usb_d_trans(const uint8_t ep, const bool dir, const uint8_t *buf, const uint32_t size,
+                                   const uint8_t zlp)
+{
+	struct usb_d_transfer trans
+	    = {(uint8_t *)buf, size, dir ? (uint8_t)(ep | USB_EP_DIR) : (uint8_t)(ep & USB_EP_N_MASK), zlp};
+
+	return _usb_d_dev_ep_trans(&trans);
+}
+
+/**
+ * \brief Dummy callback that returns false
+ * \param[in] unused0 Unused parameter.
+ * \param[in] unused1 Unused parameter.
+ * \param[in] unused2 Unused parameter.
+ * \return Always \c false.
+ */
+static bool usb_d_dummy_cb_false(uint32_t unused0, uint32_t unused1, uint32_t unused2)
+{
+	(void)unused0;
+	(void)unused1;
+	(void)unused2;
+	return false;
+}
+
+/**
+ * \brief Callback invoked when SETUP packet is ready
+ * \param[in] ep Endpoint number with transfer direction on bit 8.
+ */
+static void usb_d_cb_trans_setup(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	uint8_t *        req      = ept->xfer.req;
+
+	uint8_t n = _usb_d_dev_ep_read_req(ep, req);
+	if (n != 8) {
+		_usb_d_dev_ep_stall(ep, USB_EP_STALL_SET);
+		_usb_d_dev_ep_stall(ep | USB_EP_DIR, USB_EP_STALL_SET);
+		return;
+	}
+
+	_usb_d_dev_ep_stall(ep, USB_EP_STALL_CLR);
+	_usb_d_dev_ep_stall(ep | USB_EP_DIR, USB_EP_STALL_CLR);
+	ept->xfer.hdr.state = USB_EP_S_IDLE;
+	if (!ept->callbacks.req(ep, req)) {
+		ept->xfer.hdr.state = USB_EP_S_HALTED;
+		_usb_d_dev_ep_stall(ep, USB_EP_STALL_SET);
+		_usb_d_dev_ep_stall(ep | USB_EP_DIR, USB_EP_STALL_SET);
+	}
+}
+
+/**
+ * \brief Callback invoked when request more data
+ * \param[in] ep Endpoint number with transfer direction on bit 8.
+ * \param[in] transfered Number of bytes transfered.
+ */
+static bool usb_d_cb_trans_more(const uint8_t ep, const uint32_t transfered)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	if (ept->xfer.hdr.state == USB_EP_S_X_DATA) {
+		return ept->callbacks.more(ep, transfered);
+	}
+	return false;
+}
+
+/**
+ * \brief Handles the case that control endpoint transactions are done
+ * \param[in,out] ept Pointer to endpoint information.
+ */
+static inline void usb_d_ctrl_trans_done(struct usb_d_ep *ept)
+{
+	uint8_t state   = ept->xfer.hdr.state;
+	bool    req_dir = USB_GET_bmRequestType(ept->xfer.req) & USB_REQ_TYPE_IN;
+
+	if (state == USB_EP_S_X_DATA) {
+		/* Data stage -> Status stage */
+		bool err = ept->callbacks.xfer(ept->xfer.hdr.ep, USB_XFER_DATA, ept->xfer.req);
+		if (err) {
+			ept->xfer.hdr.state  = USB_EP_S_HALTED;
+			ept->xfer.hdr.status = USB_XFER_HALT;
+			_usb_d_dev_ep_stall(req_dir ? ept->xfer.hdr.ep : (ept->xfer.hdr.ep | USB_EP_DIR), USB_EP_STALL_SET);
+		} else {
+			ept->xfer.hdr.state = USB_EP_S_X_STATUS;
+			_usb_d_trans(ept->xfer.hdr.ep, !req_dir, NULL, 0, 1);
+		}
+	} else {
+		/* Status stage done */
+		ept->callbacks.xfer(ept->xfer.hdr.ep, USB_XFER_DONE, ept->xfer.req);
+		ept->xfer.hdr.state = USB_EP_S_X_SETUP;
+	}
+}
+
+/**
+ * Callback when USB transactions are finished.
+ */
+static void _usb_d_cb_trans_done(const uint8_t ep, const int32_t code, const uint32_t transferred)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+
+	if (code == USB_TRANS_DONE) {
+		ept->xfer.hdr.status = USB_XFER_DONE;
+		if (ept->xfer.hdr.type == USB_EP_XTYPE_CTRL) {
+			usb_d_ctrl_trans_done(ept);
+			return;
+		}
+		ept->xfer.hdr.state = USB_EP_S_IDLE;
+	} else if (code == USB_TRANS_STALL) {
+		ept->xfer.hdr.status = USB_XFER_HALT;
+		if (ept->xfer.hdr.type == USB_EP_XTYPE_CTRL) {
+			ept->xfer.hdr.state = USB_EP_S_X_SETUP;
+			_usb_d_dev_ep_stall(ep, USB_EP_STALL_CLR);
+		} else {
+			ept->xfer.hdr.state = USB_EP_S_HALTED;
+		}
+	} else if (code == USB_TRANS_ABORT) {
+		ept->xfer.hdr.status = USB_XFER_ABORT;
+		if (ept->xfer.hdr.type == USB_EP_XTYPE_CTRL) {
+			ept->xfer.hdr.state = USB_EP_S_X_SETUP;
+			return;
+		}
+		ept->xfer.hdr.state = USB_EP_S_IDLE;
+	} else if (code == USB_TRANS_RESET) {
+		ept->xfer.hdr.state  = USB_EP_S_DISABLED;
+		ept->xfer.hdr.status = USB_XFER_RESET;
+	} else {
+		ept->xfer.hdr.state  = USB_EP_S_ERROR;
+		ept->xfer.hdr.status = USB_XFER_ERROR;
+	}
+
+	ept->callbacks.xfer(ep, (enum usb_xfer_code)ept->xfer.hdr.status, (void *)transferred);
+}
+
+int32_t usb_d_init(void)
+{
+	int32_t rc = _usb_d_dev_init();
+	uint8_t i;
+	if (rc < 0) {
+		return rc;
+	}
+	memset(usb_d_inst.ep, 0x00, sizeof(struct usb_d_ep) * CONF_USB_D_NUM_EP_SP);
+	for (i = 0; i < CONF_USB_D_NUM_EP_SP; i++) {
+		usb_d_inst.ep[i].xfer.hdr.ep    = 0xFF;
+		usb_d_inst.ep[i].callbacks.req  = (usb_d_ep_cb_setup_t)usb_d_dummy_cb_false;
+		usb_d_inst.ep[i].callbacks.more = (usb_d_ep_cb_more_t)usb_d_dummy_cb_false;
+		usb_d_inst.ep[i].callbacks.xfer = (usb_d_ep_cb_xfer_t)usb_d_dummy_cb_false;
+	}
+	/* Handles device driver endpoint callbacks to build transfer. */
+	_usb_d_dev_register_ep_callback(USB_D_DEV_EP_CB_SETUP, (FUNC_PTR)usb_d_cb_trans_setup);
+	_usb_d_dev_register_ep_callback(USB_D_DEV_EP_CB_MORE, (FUNC_PTR)usb_d_cb_trans_more);
+	_usb_d_dev_register_ep_callback(USB_D_DEV_EP_CB_DONE, (FUNC_PTR)_usb_d_cb_trans_done);
+	return ERR_NONE;
+}
+
+void usb_d_deinit(void)
+{
+	_usb_d_dev_deinit();
+}
+
+void usb_d_register_callback(const enum usb_d_cb_type type, const FUNC_PTR func)
+{
+	/* Directly uses device driver callback. */
+	_usb_d_dev_register_callback(type, func);
+}
+
+int32_t usb_d_enable(void)
+{
+	return _usb_d_dev_enable();
+}
+
+void usb_d_disable(void)
+{
+	_usb_d_dev_disable();
+}
+
+void usb_d_attach(void)
+{
+	_usb_d_dev_attach();
+}
+
+void usb_d_detach(void)
+{
+	_usb_d_dev_detach();
+}
+
+enum usb_speed usb_d_get_speed(void)
+{
+	return _usb_d_dev_get_speed();
+}
+
+uint16_t usb_d_get_frame_num(void)
+{
+	return _usb_d_dev_get_frame_n();
+}
+
+uint8_t usb_d_get_uframe_num(void)
+{
+	return _usb_d_dev_get_uframe_n();
+}
+
+void usb_d_set_address(const uint8_t addr)
+{
+	_usb_d_dev_set_address(addr);
+}
+
+void usb_d_send_remotewakeup(void)
+{
+	_usb_d_dev_send_remotewakeup();
+}
+
+int32_t usb_d_ep0_init(const uint8_t max_pkt_size)
+{
+	return usb_d_ep_init(0, USB_EP_XTYPE_CTRL, max_pkt_size);
+}
+
+int32_t usb_d_ep_init(const uint8_t ep, const uint8_t attr, const uint16_t max_pkt_size)
+{
+	int32_t          rc;
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	if (ep_index >= 0) {
+		return -USB_ERR_REDO;
+	} else {
+		ep_index = _usb_d_find_ep(0xFF);
+		if (ep_index < 0) {
+			return -USB_ERR_ALLOC_FAIL;
+		}
+		ept = &usb_d_inst.ep[ep_index];
+	}
+	rc = _usb_d_dev_ep_init(ep, attr, max_pkt_size);
+	if (rc < 0) {
+		return rc;
+	}
+	ept->xfer.hdr.ep   = ep;
+	ept->xfer.hdr.type = attr & USB_EP_XTYPE_MASK;
+	return ERR_NONE;
+}
+
+void usb_d_ep_deinit(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	if (ep_index < 0) {
+		return;
+	}
+	_usb_d_dev_ep_deinit(ep);
+	ept->xfer.hdr.ep = 0xFF;
+}
+
+int32_t usb_d_ep_enable(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	int32_t          rc;
+	if (ep_index < 0) {
+		return -USB_ERR_PARAM;
+	}
+	ept->xfer.hdr.state = (ept->xfer.hdr.type == USB_EP_XTYPE_CTRL) ? USB_EP_S_X_SETUP : USB_EP_S_IDLE;
+	rc                  = _usb_d_dev_ep_enable(ep);
+	if (rc < 0) {
+		ept->xfer.hdr.state = USB_EP_S_DISABLED;
+	}
+	return rc;
+}
+
+void usb_d_ep_disable(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	if (ep_index < 0) {
+		return;
+	}
+	_usb_d_dev_ep_disable(ep);
+	ept->xfer.hdr.state = USB_EP_S_DISABLED;
+}
+
+uint8_t *usb_d_ep_get_req(const uint8_t ep)
+{
+	int8_t ep_index = _usb_d_find_ep(ep);
+	if (ep_index < 0) {
+		return NULL;
+	}
+	return usb_d_inst.ep[ep_index].xfer.req;
+}
+
+int32_t usb_d_ep_transfer(const struct usb_d_transfer *xfer)
+{
+	int8_t                ep_index = _usb_d_find_ep(xfer->ep);
+	struct usb_d_ep *     ept      = &usb_d_inst.ep[ep_index];
+	bool                  dir = USB_EP_GET_DIR(xfer->ep), zlp = xfer->zlp;
+	uint32_t              len = xfer->size;
+	int32_t               rc;
+	volatile uint8_t      state;
+	volatile hal_atomic_t flags;
+
+	if (ep_index < 0) {
+		return -USB_ERR_PARAM;
+	}
+
+	atomic_enter_critical(&flags);
+	state = ept->xfer.hdr.state;
+	if (state == USB_EP_S_IDLE) {
+		ept->xfer.hdr.state = USB_EP_S_X_DATA;
+		atomic_leave_critical(&flags);
+	} else {
+		atomic_leave_critical(&flags);
+		switch (state) {
+		case USB_EP_S_HALTED:
+			return USB_HALTED;
+		case USB_EP_S_ERROR:
+			return -USB_ERROR;
+		case USB_EP_S_DISABLED:
+			return -USB_ERR_FUNC;
+		default: /* USB_EP_S_X_xxxx  */
+			return USB_BUSY;
+		}
+	}
+
+	if (ept->xfer.hdr.type == USB_EP_XTYPE_CTRL) {
+		uint16_t req_len = USB_GET_wLength(ept->xfer.req);
+		/* SETUP without data: ZLP IN as status. */
+		if (req_len == 0) {
+			dir                 = true;
+			len                 = 0;
+			zlp                 = true;
+			ept->xfer.hdr.state = USB_EP_S_X_STATUS;
+		} else {
+			dir = (USB_GET_bmRequestType(ept->xfer.req) & USB_REQ_TYPE_IN);
+			/* Data length not exceed requested. */
+			if (len > req_len) {
+				len = req_len;
+			}
+			if (dir) {
+				/* Setup -> In */
+				zlp = (req_len > len);
+			} else {
+				zlp = false;
+			}
+		}
+	}
+
+	rc = _usb_d_trans(xfer->ep, dir, xfer->buf, len, zlp);
+	return rc;
+}
+
+void usb_d_ep_abort(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	if (ep_index < 0) {
+		return;
+	}
+	_usb_d_dev_ep_abort(ep);
+	ept->xfer.hdr.state  = USB_EP_S_IDLE;
+	ept->xfer.hdr.status = USB_XFER_ABORT;
+}
+
+int32_t usb_d_ep_get_status(const uint8_t ep, struct usb_d_ep_status *stat)
+{
+	int8_t                    ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *         ept      = &usb_d_inst.ep[ep_index];
+	struct usb_d_trans_status tmp;
+	uint8_t                   state = ept->xfer.hdr.state;
+	if (ep_index < 0) {
+		return -USB_ERR_PARAM;
+	}
+	if (stat) {
+		/* Check transaction status if transferring data. */
+		_usb_d_dev_ep_get_status(ep, &tmp);
+		stat->ep    = ep;
+		stat->state = state;
+		stat->code  = ept->xfer.hdr.status;
+		stat->count = tmp.count;
+		stat->size  = tmp.size;
+	}
+	switch (state) {
+	case USB_EP_S_IDLE:
+		return USB_OK;
+	case USB_EP_S_HALTED:
+		return USB_HALTED;
+	case USB_EP_S_ERROR:
+		return -USB_ERROR;
+	case USB_EP_S_DISABLED:
+		return -USB_ERR_FUNC;
+	default:
+		/* Busy */
+		return USB_BUSY;
+	}
+}
+
+static inline int32_t _usb_d_ep_halt_clr(const uint8_t ep)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	int32_t          rc;
+	if (ep_index < 0) {
+		return -USB_ERR_PARAM;
+	}
+	if (_usb_d_dev_ep_stall(ep, USB_EP_STALL_GET)) {
+		rc = _usb_d_dev_ep_stall(ep, USB_EP_STALL_CLR);
+		if (rc < 0) {
+			return rc;
+		}
+		ept->xfer.hdr.state  = USB_EP_S_IDLE;
+		ept->xfer.hdr.status = USB_XFER_UNHALT;
+		ept->callbacks.xfer(ep, USB_XFER_UNHALT, NULL);
+	}
+	return ERR_NONE;
+}
+
+int32_t usb_d_ep_halt(const uint8_t ep, const enum usb_ep_halt_ctrl ctrl)
+{
+	if (ctrl == USB_EP_HALT_CLR) {
+		return _usb_d_ep_halt_clr(ep);
+	} else if (ctrl == USB_EP_HALT_SET) {
+		return _usb_d_dev_ep_stall(ep, USB_EP_STALL_SET);
+	} else {
+		return _usb_d_dev_ep_stall(ep, USB_EP_STALL_GET);
+	}
+}
+
+void usb_d_ep_register_callback(const uint8_t ep, const enum usb_d_ep_cb_type type, const FUNC_PTR func)
+{
+	int8_t           ep_index = _usb_d_find_ep(ep);
+	struct usb_d_ep *ept      = &usb_d_inst.ep[ep_index];
+	FUNC_PTR         f        = func ? (FUNC_PTR)func : (FUNC_PTR)usb_d_dummy_cb_false;
+	if (ep_index < 0) {
+		return;
+	}
+	switch (type) {
+	case USB_D_EP_CB_SETUP:
+		ept->callbacks.req = (usb_d_ep_cb_setup_t)f;
+		break;
+	case USB_D_EP_CB_MORE:
+		ept->callbacks.more = (usb_d_ep_cb_more_t)f;
+		break;
+	case USB_D_EP_CB_XFER:
+		ept->callbacks.xfer = (usb_d_ep_cb_xfer_t)f;
+		break;
+	default:
+		break;
+	}
+}
+
+uint32_t usb_d_get_version(void)
+{
+	return USB_D_VERSION;
+}
+
+#ifdef __cplusplus
+}
+#endif