summaryrefslogtreecommitdiffstats
path: root/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c
diff options
context:
space:
mode:
Diffstat (limited to 'tmk_core/protocol/arm_atsam/usb/usb_device_udd.c')
-rw-r--r--tmk_core/protocol/arm_atsam/usb/usb_device_udd.c1097
1 files changed, 1097 insertions, 0 deletions
diff --git a/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c b/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c
new file mode 100644
index 000000000..b31256df7
--- /dev/null
+++ b/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c
@@ -0,0 +1,1097 @@
+/**
+ * \file
+ *
+ * \brief USB Device wrapper layer for compliance with common driver UDD
+ *
+ * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The name of Atmel may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 4. This software may only be redistributed and used in connection with an
+ * Atmel microcontroller product.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#include "samd51j18a.h"
+#include <string.h>
+#include <stdlib.h>
+
+// Get USB device configuration
+#include "conf_usb.h"
+#include "udd.h"
+#include "usb.h"
+#include "status_codes.h"
+
+/**
+ * \ingroup usb_device_group
+ * \defgroup usb_device_udd_group USB Device Driver Implement (UDD)
+ * USB low-level driver for USB device mode
+ * @{
+ */
+// Check USB device configuration
+#ifdef USB_DEVICE_HS_SUPPORT
+# error The High speed mode is not supported on this part, please remove USB_DEVICE_HS_SUPPORT in conf_usb.h
+#endif
+
+//Note: This driver is adapted for SAMD51
+
+#ifndef UDC_REMOTEWAKEUP_LPM_ENABLE
+#define UDC_REMOTEWAKEUP_LPM_ENABLE()
+#endif
+#ifndef UDC_REMOTEWAKEUP_LPM_DISABLE
+#define UDC_REMOTEWAKEUP_LPM_DISABLE()
+#endif
+#ifndef UDC_SUSPEND_LPM_EVENT
+#define UDC_SUSPEND_LPM_EVENT()
+#endif
+
+/* for debug text */
+#ifdef USB_DEBUG
+# define dbg_print printf
+#else
+# define dbg_print(...)
+#endif
+
+/** Maximum size of a transfer in multi-packet mode */
+#define UDD_ENDPOINT_MAX_TRANS ((8*1024)-1)
+
+/** USB software device instance structure */
+struct usb_module usb_device;
+
+/**
+ * \name Clock management
+ *
+ * @{
+ */
+
+#define UDD_CLOCK_GEN 0
+
+static inline void udd_wait_clock_ready(void)
+{
+
+}
+
+/**
+ * \name Power management
+ *
+ * @{
+ */
+#define udd_sleep_mode(arg)
+/** @} */
+
+/**
+ * \name Control endpoint low level management routine.
+ *
+ * This function performs control endpoint management.
+ * It handles the SETUP/DATA/HANDSHAKE phases of a control transaction.
+ *
+ * @{
+ */
+
+/**
+ * \brief Buffer to store the data received on control endpoint (SETUP/OUT endpoint 0)
+ *
+ * Used to avoid a RAM buffer overflow in case of the payload buffer
+ * is smaller than control endpoint size
+ */
+UDC_BSS(4)
+uint8_t udd_ctrl_buffer[USB_DEVICE_EP_CTRL_SIZE];
+
+/** Bit definitions about endpoint control state machine for udd_ep_control_state */
+typedef enum {
+ UDD_EPCTRL_SETUP = 0, //!< Wait a SETUP packet
+ UDD_EPCTRL_DATA_OUT = 1, //!< Wait a OUT data packet
+ UDD_EPCTRL_DATA_IN = 2, //!< Wait a IN data packet
+ UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP = 3, //!< Wait a IN ZLP packet
+ UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP = 4, //!< Wait a OUT ZLP packet
+ UDD_EPCTRL_STALL_REQ = 5, //!< STALL enabled on IN & OUT packet
+} udd_ctrl_ep_state_t;
+
+/** Global variable to give and record information of the set up request management */
+udd_ctrl_request_t udd_g_ctrlreq;
+
+/** State of the endpoint control management */
+static udd_ctrl_ep_state_t udd_ep_control_state;
+
+/** Total number of data received/sent during data packet phase with previous payload buffers */
+static uint16_t udd_ctrl_prev_payload_nb_trans;
+
+/** Number of data received/sent to/from udd_g_ctrlreq.payload buffer */
+static uint16_t udd_ctrl_payload_nb_trans;
+
+/** @} */
+
+/**
+ * \name Management of bulk/interrupt/isochronous endpoints
+ *
+ * The UDD manages the data transfer on endpoints:
+ * - Start data transfer on endpoint with USB Device DMA
+ * - Send a ZLP packet if requested
+ * - Call callback registered to signal end of transfer
+ * The transfer abort and stall feature are supported.
+ *
+ * @{
+ */
+
+/**
+ * \brief Buffer to store the data received on bulk/interrupt endpoints
+ *
+ * Used to avoid a RAM buffer overflow in case of the user buffer
+ * is smaller than endpoint size
+ *
+ * \warning The protected interrupt endpoint size is 512 bytes maximum.
+ * \warning The isochronous and endpoint is not protected by this system and
+ * the user must always use a buffer corresponding at endpoint size.
+ */
+
+#if (defined USB_DEVICE_LOW_SPEED)
+UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][8];
+#elif (defined USB_DEVICE_HS_SUPPORT)
+UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][512];
+#else
+UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][64];
+#endif
+
+/** Structure definition about job registered on an endpoint */
+typedef struct {
+ union {
+ //! Callback to call at the end of transfer
+ udd_callback_trans_t call_trans;
+ //! Callback to call when the endpoint halt is cleared
+ udd_callback_halt_cleared_t call_nohalt;
+ };
+ //! Buffer located in internal RAM to send or fill during job
+ uint8_t *buf;
+ //! Size of buffer to send or fill
+ iram_size_t buf_size;
+ //! Total number of data transferred on endpoint
+ iram_size_t nb_trans;
+ //! Endpoint size
+ uint16_t ep_size;
+ //! A job is registered on this endpoint
+ uint8_t busy:1;
+ //! A short packet is requested for this job on endpoint IN
+ uint8_t b_shortpacket:1;
+ //! The cache buffer is currently used on endpoint OUT
+ uint8_t b_use_out_cache_buffer:1;
+} udd_ep_job_t;
+
+/** Array to register a job on bulk/interrupt/isochronous endpoint */
+static udd_ep_job_t udd_ep_job[2 * USB_DEVICE_MAX_EP];
+
+/** @} */
+
+/**
+ * \brief Get the detailed job by endpoint number
+ * \param[in] ep Endpoint Address
+ * \retval pointer to an udd_ep_job_t structure instance
+ */
+static udd_ep_job_t* udd_ep_get_job(udd_ep_id_t ep)
+{
+ if ((ep == 0) || (ep == 0x80)) {
+ return NULL;
+ } else {
+ return &udd_ep_job[(2 * (ep & USB_EP_ADDR_MASK) + ((ep & USB_EP_DIR_IN) ? 1 : 0)) - 2];
+ }
+}
+
+/**
+ * \brief Endpoint IN process, continue to send packets or zero length packet
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ep_trans_in_next(void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+ udd_ep_id_t ep = ep_callback_para->endpoint_address;
+ uint16_t ep_size, nb_trans;
+ uint16_t next_trans;
+ udd_ep_id_t ep_num;
+ udd_ep_job_t *ptr_job;
+
+ ptr_job = udd_ep_get_job(ep);
+ ep_num = ep & USB_EP_ADDR_MASK;
+
+ ep_size = ptr_job->ep_size;
+ /* Update number of data transferred */
+ nb_trans = ep_callback_para->sent_bytes;
+ ptr_job->nb_trans += nb_trans;
+
+ /* Need to send other data */
+ if (ptr_job->nb_trans != ptr_job->buf_size) {
+ next_trans = ptr_job->buf_size - ptr_job->nb_trans;
+ if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
+ /* The USB hardware support a maximum
+ * transfer size of UDD_ENDPOINT_MAX_TRANS Bytes */
+ next_trans = UDD_ENDPOINT_MAX_TRANS -(UDD_ENDPOINT_MAX_TRANS % ep_size);
+ }
+ /* Need ZLP, if requested and last packet is not a short packet */
+ ptr_job->b_shortpacket = ptr_job->b_shortpacket && (0 == (next_trans % ep_size));
+ usb_device_endpoint_write_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],next_trans);
+ return;
+ }
+
+ /* Need to send a ZLP after all data transfer */
+ if (ptr_job->b_shortpacket) {
+ ptr_job->b_shortpacket = false;
+ /* Start new transfer */
+ usb_device_endpoint_write_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],0);
+ return;
+ }
+
+ /* Job complete then call callback */
+ ptr_job->busy = false;
+ if (NULL != ptr_job->call_trans) {
+ ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep);
+ }
+}
+
+/**
+ * \brief Endpoint OUT process, continue to receive packets or zero length packet
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ep_trans_out_next(void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+ udd_ep_id_t ep = ep_callback_para->endpoint_address;
+ uint16_t ep_size, nb_trans;
+ uint16_t next_trans;
+ udd_ep_id_t ep_num;
+ udd_ep_job_t *ptr_job;
+
+ ptr_job = udd_ep_get_job(ep);
+ ep_num = ep & USB_EP_ADDR_MASK;
+
+ ep_size = ptr_job->ep_size;
+ /* Update number of data transferred */
+ nb_trans = ep_callback_para->received_bytes;
+
+ /* Can be necessary to copy data receive from cache buffer to user buffer */
+ if (ptr_job->b_use_out_cache_buffer) {
+ memcpy(&ptr_job->buf[ptr_job->nb_trans], udd_ep_out_cache_buffer[ep_num - 1], ptr_job->buf_size % ep_size);
+ }
+
+ /* Update number of data transferred */
+ ptr_job->nb_trans += nb_trans;
+ if (ptr_job->nb_trans > ptr_job->buf_size) {
+ ptr_job->nb_trans = ptr_job->buf_size;
+ }
+
+ /* If all previous data requested are received and user buffer not full
+ * then need to receive other data */
+ if ((nb_trans == ep_callback_para->out_buffer_size) && (ptr_job->nb_trans != ptr_job->buf_size)) {
+ next_trans = ptr_job->buf_size - ptr_job->nb_trans;
+ if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
+ /* The USB hardware support a maximum transfer size
+ * of UDD_ENDPOINT_MAX_TRANS Bytes */
+ next_trans = UDD_ENDPOINT_MAX_TRANS - (UDD_ENDPOINT_MAX_TRANS % ep_size);
+ } else {
+ next_trans -= next_trans % ep_size;
+ }
+
+ if (next_trans < ep_size) {
+ /* Use the cache buffer for Bulk or Interrupt size endpoint */
+ ptr_job->b_use_out_cache_buffer = true;
+ usb_device_endpoint_read_buffer_job(&usb_device,ep_num,udd_ep_out_cache_buffer[ep_num - 1],ep_size);
+ } else {
+ usb_device_endpoint_read_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],next_trans);
+ }
+ return;
+ }
+
+ /* Job complete then call callback */
+ ptr_job->busy = false;
+ if (NULL != ptr_job->call_trans) {
+ ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep);
+ }
+}
+
+/**
+ * \brief Endpoint Transfer Complete callback function, to do the next transfer depends on the direction(IN or OUT)
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ep_transfer_process(struct usb_module *module_inst, void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+ udd_ep_id_t ep = ep_callback_para->endpoint_address;
+
+ if (ep & USB_EP_DIR_IN) {
+ udd_ep_trans_in_next(pointer);
+ } else {
+ udd_ep_trans_out_next(pointer);
+ }
+}
+
+void udd_ep_abort(udd_ep_id_t ep)
+{
+ udd_ep_job_t *ptr_job;
+
+ usb_device_endpoint_abort_job(&usb_device, ep);
+
+ /* Job complete then call callback */
+ ptr_job = udd_ep_get_job(ep);
+ if (!ptr_job->busy) {
+ return;
+ }
+ ptr_job->busy = false;
+ if (NULL != ptr_job->call_trans) {
+ /* It can be a Transfer or stall callback */
+ ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->nb_trans, ep);
+ }
+}
+
+bool udd_is_high_speed(void)
+{
+ return false;
+}
+
+uint16_t udd_get_frame_number(void)
+{
+ return usb_device_get_frame_number(&usb_device);
+}
+
+uint16_t udd_get_micro_frame_number(void)
+{
+ return usb_device_get_micro_frame_number(&usb_device);
+}
+
+void udd_ep_free(udd_ep_id_t ep)
+{
+ struct usb_device_endpoint_config config_ep;
+ usb_device_endpoint_get_config_defaults(&config_ep);
+
+ uint8_t ep_num = ep & USB_EP_ADDR_MASK;
+ udd_ep_abort(ep);
+
+ config_ep.ep_address = ep;
+ config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_DISABLE;
+ usb_device_endpoint_set_config(&usb_device, &config_ep);
+ usb_device_endpoint_unregister_callback(&usb_device,ep_num,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT);
+ usb_device_endpoint_disable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT);
+}
+
+bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes, uint16_t MaxEndpointSize)
+{
+ struct usb_device_endpoint_config config_ep;
+ usb_device_endpoint_get_config_defaults(&config_ep);
+
+ config_ep.ep_address = ep;
+
+ if(MaxEndpointSize <= 8) {
+ config_ep.ep_size = USB_ENDPOINT_8_BYTE;
+ } else if(MaxEndpointSize <= 16) {
+ config_ep.ep_size = USB_ENDPOINT_16_BYTE;
+ } else if(MaxEndpointSize <= 32) {
+ config_ep.ep_size = USB_ENDPOINT_32_BYTE;
+ } else if(MaxEndpointSize <= 64) {
+ config_ep.ep_size = USB_ENDPOINT_64_BYTE;
+ } else if(MaxEndpointSize <= 128) {
+ config_ep.ep_size = USB_ENDPOINT_128_BYTE;
+ } else if(MaxEndpointSize <= 256) {
+ config_ep.ep_size = USB_ENDPOINT_256_BYTE;
+ } else if(MaxEndpointSize <= 512) {
+ config_ep.ep_size = USB_ENDPOINT_512_BYTE;
+ } else if(MaxEndpointSize <= 1023) {
+ config_ep.ep_size = USB_ENDPOINT_1023_BYTE;
+ } else {
+ return false;
+ }
+ udd_ep_job_t *ptr_job = udd_ep_get_job(ep);
+ ptr_job->ep_size = MaxEndpointSize;
+
+ bmAttributes = bmAttributes & USB_EP_TYPE_MASK;
+
+ /* Check endpoint type */
+ if(USB_EP_TYPE_ISOCHRONOUS == bmAttributes) {
+ config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_ISOCHRONOUS;
+ } else if (USB_EP_TYPE_BULK == bmAttributes) {
+ config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_BULK;
+ } else if (USB_EP_TYPE_INTERRUPT == bmAttributes) {
+ config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_INTERRUPT;
+ } else {
+ return false;
+ }
+
+ uint8_t ep_num = ep & USB_EP_ADDR_MASK;
+
+ if (STATUS_OK != usb_device_endpoint_set_config(&usb_device, &config_ep)) {
+ return false;
+ }
+ usb_device_endpoint_register_callback(&usb_device,ep_num,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT,udd_ep_transfer_process);
+ usb_device_endpoint_enable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT);
+ usb_device_endpoint_enable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL);
+
+ return true;
+}
+
+bool udd_ep_is_halted(udd_ep_id_t ep)
+{
+ return usb_device_endpoint_is_halted(&usb_device, ep);
+}
+
+bool udd_ep_set_halt(udd_ep_id_t ep)
+{
+ uint8_t ep_num = ep & USB_EP_ADDR_MASK;
+
+ if (USB_DEVICE_MAX_EP < ep_num) {
+ return false;
+ }
+
+ usb_device_endpoint_set_halt(&usb_device, ep);
+
+ udd_ep_abort(ep);
+ return true;
+}
+
+bool udd_ep_clear_halt(udd_ep_id_t ep)
+{
+ udd_ep_job_t *ptr_job;
+ uint8_t ep_num = ep & USB_EP_ADDR_MASK;
+
+ if (USB_DEVICE_MAX_EP < ep_num) {
+ return false;
+ }
+ ptr_job = udd_ep_get_job(ep);
+
+ usb_device_endpoint_clear_halt(&usb_device, ep);
+
+ /* If a job is register on clear halt action then execute callback */
+ if (ptr_job->busy == true) {
+ ptr_job->busy = false;
+ ptr_job->call_nohalt();
+ }
+
+ return true;
+}
+
+bool udd_ep_wait_stall_clear(udd_ep_id_t ep, udd_callback_halt_cleared_t callback)
+{
+ udd_ep_id_t ep_num;
+ udd_ep_job_t *ptr_job;
+
+ ep_num = ep & USB_EP_ADDR_MASK;
+ if (USB_DEVICE_MAX_EP < ep_num) {
+ return false;
+ }
+
+ ptr_job = udd_ep_get_job(ep);
+ if (ptr_job->busy == true) {
+ return false; /* Job already on going */
+ }
+
+ /* Wait clear halt endpoint */
+ if (usb_device_endpoint_is_halted(&usb_device, ep)) {
+ /* Endpoint halted then registers the callback */
+ ptr_job->busy = true;
+ ptr_job->call_nohalt = callback;
+ return true;
+ } else if (usb_device_endpoint_is_configured(&usb_device, ep)) {
+ callback(); /* Endpoint not halted then call directly callback */
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * \brief Control Endpoint stall sending data
+ */
+static void udd_ctrl_stall_data(void)
+{
+ udd_ep_control_state = UDD_EPCTRL_STALL_REQ;
+
+ usb_device_endpoint_set_halt(&usb_device, USB_EP_DIR_IN);
+ usb_device_endpoint_clear_halt(&usb_device, USB_EP_DIR_OUT);
+}
+
+bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket, uint8_t *buf, iram_size_t buf_size, udd_callback_trans_t callback)
+{
+ udd_ep_id_t ep_num;
+ udd_ep_job_t *ptr_job;
+ uint32_t irqflags;
+
+ ep_num = ep & USB_EP_ADDR_MASK;
+
+ if ((USB_DEVICE_MAX_EP < ep_num) || (udd_ep_is_halted(ep))) {
+ return false;
+ }
+
+ ptr_job = udd_ep_get_job(ep);
+
+ irqflags = __get_PRIMASK();
+ __disable_irq();
+ __DMB();
+
+ if (ptr_job->busy == true) {
+ __DMB();
+ __set_PRIMASK(irqflags);
+ return false; /* Job already on going */
+ }
+
+ ptr_job->busy = true;
+ __DMB();
+ __set_PRIMASK(irqflags);
+
+ /* No job running, set up a new one */
+ ptr_job->buf = buf;
+ ptr_job->buf_size = buf_size;
+ ptr_job->nb_trans = 0;
+ ptr_job->call_trans = callback;
+ ptr_job->b_shortpacket = b_shortpacket;
+ ptr_job->b_use_out_cache_buffer = false;
+
+ /* Initialize value to simulate a empty transfer */
+ uint16_t next_trans;
+
+ if (ep & USB_EP_DIR_IN) {
+ if (0 != ptr_job->buf_size) {
+ next_trans = ptr_job->buf_size;
+ if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
+ next_trans = UDD_ENDPOINT_MAX_TRANS - (UDD_ENDPOINT_MAX_TRANS % ptr_job->ep_size);
+ }
+ ptr_job->b_shortpacket = ptr_job->b_shortpacket && (0 == (next_trans % ptr_job->ep_size));
+ } else if (true == ptr_job->b_shortpacket) {
+ ptr_job->b_shortpacket = false; /* avoid to send zero length packet again */
+ next_trans = 0;
+ } else {
+ ptr_job->busy = false;
+ if (NULL != ptr_job->call_trans) {
+ ptr_job->call_trans(UDD_EP_TRANSFER_OK, 0, ep);
+ }
+ return true;
+ }
+ return (STATUS_OK ==
+ usb_device_endpoint_write_buffer_job(&usb_device,
+ ep_num,&ptr_job->buf[0],next_trans));
+ } else {
+ if (0 != ptr_job->buf_size) {
+ next_trans = ptr_job->buf_size;
+ if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
+ /* The USB hardware support a maximum transfer size
+ * of UDD_ENDPOINT_MAX_TRANS Bytes */
+ next_trans = UDD_ENDPOINT_MAX_TRANS -
+ (UDD_ENDPOINT_MAX_TRANS % ptr_job->ep_size);
+ } else {
+ next_trans -= next_trans % ptr_job->ep_size;
+ }
+ if (next_trans < ptr_job->ep_size) {
+ ptr_job->b_use_out_cache_buffer = true;
+ return (STATUS_OK ==
+ usb_device_endpoint_read_buffer_job(&usb_device, ep_num,
+ udd_ep_out_cache_buffer[ep_num - 1],
+ ptr_job->ep_size));
+ } else {
+ return (STATUS_OK ==
+ usb_device_endpoint_read_buffer_job(&usb_device, ep_num,
+ &ptr_job->buf[0],next_trans));
+ }
+ } else {
+ ptr_job->busy = false;
+ if (NULL != ptr_job->call_trans) {
+ ptr_job->call_trans(UDD_EP_TRANSFER_OK, 0, ep);
+ }
+ return true;
+ }
+ }
+}
+
+void udd_set_address(uint8_t address)
+{
+ usb_device_set_address(&usb_device,address);
+}
+
+uint8_t udd_getaddress(void)
+{
+ return usb_device_get_address(&usb_device);
+}
+
+void udd_send_remotewakeup(void)
+{
+ uint32_t try = 5;
+ udd_wait_clock_ready();
+ udd_sleep_mode(UDD_STATE_IDLE);
+ while(2 != usb_get_state_machine_status(&usb_device) && try --) {
+ usb_device_send_remote_wake_up(&usb_device);
+ }
+}
+
+void udd_set_setup_payload( uint8_t *payload, uint16_t payload_size )
+{
+ udd_g_ctrlreq.payload = payload;
+ udd_g_ctrlreq.payload_size = payload_size;
+}
+
+/**
+ * \brief Control Endpoint translate the data in buffer into Device Request Struct
+ */
+static void udd_ctrl_fetch_ram(void)
+{
+ udd_g_ctrlreq.req.bmRequestType = udd_ctrl_buffer[0];
+ udd_g_ctrlreq.req.bRequest = udd_ctrl_buffer[1];
+ udd_g_ctrlreq.req.wValue = ((uint16_t)(udd_ctrl_buffer[3]) << 8) + udd_ctrl_buffer[2];
+ udd_g_ctrlreq.req.wIndex = ((uint16_t)(udd_ctrl_buffer[5]) << 8) + udd_ctrl_buffer[4];
+ udd_g_ctrlreq.req.wLength = ((uint16_t)(udd_ctrl_buffer[7]) << 8) + udd_ctrl_buffer[6];
+}
+
+/**
+ * \brief Control Endpoint send out zero length packet
+ */
+static void udd_ctrl_send_zlp_in(void)
+{
+ udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP;
+ usb_device_endpoint_setup_buffer_job(&usb_device,udd_ctrl_buffer);
+ usb_device_endpoint_write_buffer_job(&usb_device,0,udd_g_ctrlreq.payload,0);
+}
+
+/**
+ * \brief Process control endpoint IN transaction
+ */
+static void udd_ctrl_in_sent(void)
+{
+ static bool b_shortpacket = false;
+ uint16_t nb_remain;
+
+ nb_remain = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans;
+
+ if (0 == nb_remain) {
+ /* All content of current buffer payload are sent Update number of total data sending by previous payload buffer */
+ udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans;
+ if ((udd_g_ctrlreq.req.wLength == udd_ctrl_prev_payload_nb_trans) || b_shortpacket) {
+ /* All data requested are transferred or a short packet has been sent, then it is the end of data phase.
+ * Generate an OUT ZLP for handshake phase */
+ udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP;
+ usb_device_endpoint_setup_buffer_job(&usb_device,udd_ctrl_buffer);
+ return;
+ }
+ /* Need of new buffer because the data phase is not complete */
+ if ((!udd_g_ctrlreq.over_under_run) || (!udd_g_ctrlreq.over_under_run())) {
+ /* Under run then send zlp on IN
+ * Here nb_remain=0, this allows to send a IN ZLP */
+ } else {
+ /* A new payload buffer is given */
+ udd_ctrl_payload_nb_trans = 0;
+ nb_remain = udd_g_ctrlreq.payload_size;
+ }
+ }
+
+ /* Continue transfer and send next data */
+ if (nb_remain >= USB_DEVICE_EP_CTRL_SIZE) {
+ nb_remain = USB_DEVICE_EP_CTRL_SIZE;
+ b_shortpacket = false;
+ } else {
+ b_shortpacket = true;
+ }
+
+ /* Link payload buffer directly on USB hardware */
+ usb_device_endpoint_write_buffer_job(&usb_device,0,udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans,nb_remain);
+
+ udd_ctrl_payload_nb_trans += nb_remain;
+}
+
+/**
+ * \brief Process control endpoint OUT transaction
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ctrl_out_received(void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+
+ uint16_t nb_data;
+ nb_data = ep_callback_para->received_bytes; /* Read data received during OUT phase */
+
+ if (udd_g_ctrlreq.payload_size < (udd_ctrl_payload_nb_trans + nb_data)) {
+ /* Payload buffer too small */
+ nb_data = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans;
+ }
+
+ memcpy((uint8_t *) (udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans), udd_ctrl_buffer, nb_data);
+ udd_ctrl_payload_nb_trans += nb_data;
+
+ if ((USB_DEVICE_EP_CTRL_SIZE != nb_data) || \
+ (udd_g_ctrlreq.req.wLength <= (udd_ctrl_prev_payload_nb_trans + udd_ctrl_payload_nb_trans))) {
+ /* End of reception because it is a short packet
+ * or all data are transferred */
+
+ /* Before send ZLP, call intermediate callback
+ * in case of data receive generate a stall */
+ udd_g_ctrlreq.payload_size = udd_ctrl_payload_nb_trans;
+ if (NULL != udd_g_ctrlreq.over_under_run) {
+ if (!udd_g_ctrlreq.over_under_run()) {
+ /* Stall ZLP */
+ udd_ep_control_state = UDD_EPCTRL_STALL_REQ;
+ /* Stall all packets on IN & OUT control endpoint */
+ udd_ep_set_halt(0);
+ /* Ack reception of OUT to replace NAK by a STALL */
+ return;
+ }
+ }
+ /* Send IN ZLP to ACK setup request */
+ udd_ctrl_send_zlp_in();
+ return;
+ }
+
+ if (udd_g_ctrlreq.payload_size == udd_ctrl_payload_nb_trans) {
+ /* Overrun then request a new payload buffer */
+ if (!udd_g_ctrlreq.over_under_run) {
+ /* No callback available to request a new payload buffer
+ * Stall ZLP */
+ udd_ep_control_state = UDD_EPCTRL_STALL_REQ;
+ /* Stall all packets on IN & OUT control endpoint */
+ udd_ep_set_halt(0);
+ return;
+ }
+ if (!udd_g_ctrlreq.over_under_run()) {
+ /* No new payload buffer delivered
+ * Stall ZLP */
+ udd_ep_control_state = UDD_EPCTRL_STALL_REQ;
+ /* Stall all packets on IN & OUT control endpoint */
+ udd_ep_set_halt(0);
+ return;
+ }
+ /* New payload buffer available
+ * Update number of total data received */
+ udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans;
+
+ /* Reinitialize reception on payload buffer */
+ udd_ctrl_payload_nb_trans = 0;
+ }
+ usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE);
+}
+
+/**
+ * \internal
+ * \brief Endpoint 0 (control) SETUP received callback
+ * \param[in] module_inst pointer to USB module instance
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void _usb_ep0_on_setup(struct usb_module *module_inst, void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+
+ if (UDD_EPCTRL_SETUP != udd_ep_control_state) {
+ if (NULL != udd_g_ctrlreq.callback) {
+ udd_g_ctrlreq.callback();
+ }
+ udd_ep_control_state = UDD_EPCTRL_SETUP;
+ }
+ if ( 8 != ep_callback_para->received_bytes) {
+ udd_ctrl_stall_data();
+ return;
+ } else {
+ udd_ctrl_fetch_ram();
+ if (false == udc_process_setup()) {
+ udd_ctrl_stall_data();
+ return;
+ } else if (Udd_setup_is_in()) {
+ udd_ctrl_prev_payload_nb_trans = 0;
+ udd_ctrl_payload_nb_trans = 0;
+ udd_ep_control_state = UDD_EPCTRL_DATA_IN;
+ usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE);
+ udd_ctrl_in_sent();
+ } else {
+ if(0 == udd_g_ctrlreq.req.wLength) {
+ udd_ctrl_send_zlp_in();
+ return;
+ } else {
+ udd_ctrl_prev_payload_nb_trans = 0;
+ udd_ctrl_payload_nb_trans = 0;
+ udd_ep_control_state = UDD_EPCTRL_DATA_OUT;
+ /* Initialize buffer size and enable OUT bank */
+ usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE);
+ }
+ }
+ }
+}
+
+/**
+ * \brief Control Endpoint Process when underflow condition has occurred
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ctrl_underflow(void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+
+ if (UDD_EPCTRL_DATA_OUT == udd_ep_control_state) {
+ /* Host want to stop OUT transaction
+ * then stop to wait OUT data phase and wait IN ZLP handshake */
+ udd_ctrl_send_zlp_in();
+ } else if (UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP == udd_ep_control_state) {
+ /* A OUT handshake is waiting by device,
+ * but host want extra IN data then stall extra IN data */
+ usb_device_endpoint_set_halt(&usb_device, ep_callback_para->endpoint_address);
+ }
+}
+
+/**
+ * \brief Control Endpoint Process when overflow condition has occurred
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void udd_ctrl_overflow(void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+
+ if (UDD_EPCTRL_DATA_IN == udd_ep_control_state) {
+ /* Host want to stop IN transaction
+ * then stop to wait IN data phase and wait OUT ZLP handshake */
+ udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP;
+ } else if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) {
+ /* A IN handshake is waiting by device,
+ * but host want extra OUT data then stall extra OUT data and following status stage */
+ usb_device_endpoint_set_halt(&usb_device, ep_callback_para->endpoint_address);
+ }
+}
+
+/**
+ * \internal
+ * \brief Control endpoint transfer fail callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void _usb_ep0_on_tansfer_fail(struct usb_module *module_inst, void* pointer)
+{
+ struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer;
+
+ if(ep_callback_para->endpoint_address & USB_EP_DIR_IN) {
+ udd_ctrl_underflow(pointer);
+ } else {
+ udd_ctrl_overflow(pointer);
+ }
+}
+
+/**
+ * \internal
+ * \brief Control endpoint transfer complete callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer.
+ */
+static void _usb_ep0_on_tansfer_ok(struct usb_module *module_inst, void *pointer)
+{
+ if (UDD_EPCTRL_DATA_OUT == udd_ep_control_state) { /* handshake Out for status stage */
+ udd_ctrl_out_received(pointer);
+ } else if (UDD_EPCTRL_DATA_IN == udd_ep_control_state) { /* handshake In for status stage */
+ udd_ctrl_in_sent();
+ } else {
+ if (NULL != udd_g_ctrlreq.callback) {
+ udd_g_ctrlreq.callback();
+ }
+ udd_ep_control_state = UDD_EPCTRL_SETUP;
+ }
+}
+
+/**
+ * \brief Enable Control Endpoint
+ * \param[in] module_inst Pointer to USB module instance
+ */
+static void udd_ctrl_ep_enable(struct usb_module *module_inst)
+{
+ /* USB Device Endpoint0 Configuration */
+ struct usb_device_endpoint_config config_ep0;
+
+ usb_device_endpoint_get_config_defaults(&config_ep0);
+ config_ep0.ep_size = (enum usb_endpoint_size)(32 - clz(((uint32_t)Min(Max(USB_DEVICE_EP_CTRL_SIZE, 8), 1024) << 1) - 1) - 1 - 3);
+ usb_device_endpoint_set_config(module_inst,&config_ep0);
+
+ usb_device_endpoint_setup_buffer_job(module_inst,udd_ctrl_buffer);
+
+ usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_RXSTP, _usb_ep0_on_setup );
+ usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT,_usb_ep0_on_tansfer_ok );
+ usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL,_usb_ep0_on_tansfer_fail );
+ usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_RXSTP);
+ usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT);
+ usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL);
+
+#ifdef USB_DEVICE_LPM_SUPPORT
+ // Enable LPM feature
+ usb_device_set_lpm_mode(module_inst, USB_DEVICE_LPM_ACK);
+#endif
+
+ udd_ep_control_state = UDD_EPCTRL_SETUP;
+}
+
+/**
+ * \internal
+ * \brief Control endpoint Suspend callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the callback parameter from driver layer.
+ */
+static void _usb_on_suspend(struct usb_module *module_inst, void *pointer)
+{
+ usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP);
+ udd_sleep_mode(UDD_STATE_SUSPEND);
+#ifdef UDC_SUSPEND_EVENT
+ UDC_SUSPEND_EVENT();
+#endif
+}
+
+#ifdef USB_DEVICE_LPM_SUPPORT
+static void _usb_device_lpm_suspend(struct usb_module *module_inst, void *pointer)
+{
+ dbg_print("LPM_SUSP\n");
+
+ uint32_t *lpm_wakeup_enable;
+ lpm_wakeup_enable = (uint32_t *)pointer;
+
+ usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP);
+ usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP);
+
+//#warning Here the sleep mode must be choose to have a DFLL startup time < bmAttribut.HIRD
+ udd_sleep_mode(UDD_STATE_SUSPEND_LPM); // Enter in LPM SUSPEND mode
+ if ((*lpm_wakeup_enable)) {
+ UDC_REMOTEWAKEUP_LPM_ENABLE();
+ }
+ if (!(*lpm_wakeup_enable)) {
+ UDC_REMOTEWAKEUP_LPM_DISABLE();
+ }
+ UDC_SUSPEND_LPM_EVENT();
+}
+#endif
+
+/**
+ * \internal
+ * \brief Control endpoint SOF callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the callback parameter from driver layer.
+ */
+static void _usb_on_sof_notify(struct usb_module *module_inst, void *pointer)
+{
+ udc_sof_notify();
+#ifdef UDC_SOF_EVENT
+ UDC_SOF_EVENT();
+#endif
+}
+
+/**
+ * \internal
+ * \brief Control endpoint Reset callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the callback parameter from driver layer.
+ */
+static void _usb_on_bus_reset(struct usb_module *module_inst, void *pointer)
+{
+ // Reset USB Device Stack Core
+ udc_reset();
+ usb_device_set_address(module_inst,0);
+ udd_ctrl_ep_enable(module_inst);
+}
+
+/**
+ * \internal
+ * \brief Control endpoint Wakeup callback function
+ * \param[in] module_inst Pointer to USB module instance
+ * \param[in] pointer Pointer to the callback parameter from driver layer.
+ */
+static void _usb_on_wakeup(struct usb_module *module_inst, void *pointer)
+{
+ udd_wait_clock_ready();
+
+ usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND);
+#ifdef USB_DEVICE_LPM_SUPPORT
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP, _usb_device_lpm_suspend);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP);
+#endif
+ udd_sleep_mode(UDD_STATE_IDLE);
+#ifdef UDC_RESUME_EVENT
+ UDC_RESUME_EVENT();
+#endif
+}
+
+void udd_detach(void)
+{
+ usb_device_detach(&usb_device);
+ udd_sleep_mode(UDD_STATE_SUSPEND);
+}
+
+void udd_attach(void)
+{
+ udd_sleep_mode(UDD_STATE_IDLE);
+ usb_device_attach(&usb_device);
+
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND, _usb_on_suspend);
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_SOF, _usb_on_sof_notify);
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_RESET, _usb_on_bus_reset);
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP, _usb_on_wakeup);
+
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SOF);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_RESET);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP);
+#ifdef USB_DEVICE_LPM_SUPPORT
+ usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP, _usb_device_lpm_suspend);
+ usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP);
+#endif
+}
+
+void udd_enable(void)
+{
+ uint32_t irqflags;
+
+ /* To avoid USB interrupt before end of initialization */
+ irqflags = __get_PRIMASK();
+ __disable_irq();
+ __DMB();
+
+ struct usb_config config_usb;
+
+ /* USB Module configuration */
+ usb_get_config_defaults(&config_usb);
+ config_usb.source_generator = UDD_CLOCK_GEN;
+ usb_init(&usb_device, USB, &config_usb);
+
+ /* USB Module Enable */
+ usb_enable(&usb_device);
+
+ /* Check clock after enable module, request the clock */
+ udd_wait_clock_ready();
+
+ udd_sleep_mode(UDD_STATE_SUSPEND);
+
+ // No VBus detect, assume always high
+#ifndef USB_DEVICE_ATTACH_AUTO_DISABLE
+ udd_attach();
+#endif
+
+ __DMB();
+ __set_PRIMASK(irqflags);
+}
+
+void udd_disable(void)
+{
+ udd_detach();
+
+ udd_sleep_mode(UDD_STATE_OFF);
+}
+/** @} */