aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c1766
1 files changed, 1766 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c
new file mode 100644
index 0000000..e5cbcf8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/app_usbd.c
@@ -0,0 +1,1766 @@
+/**
+ * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
+ *
+ * All rights reserved.
+ *
+ * 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, except as embedded into a Nordic
+ * Semiconductor ASA integrated circuit in a product or a software update for
+ * such product, 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. Neither the name of Nordic Semiconductor ASA nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ * Nordic Semiconductor ASA integrated circuit.
+ *
+ * 5. Any software provided in binary form under this license must not be reverse
+ * engineered, decompiled, modified and/or disassembled.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS 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.
+ *
+ */
+#include "sdk_common.h"
+#if NRF_MODULE_ENABLED(APP_USBD)
+
+#include "app_usbd.h"
+#include "app_usbd_core.h"
+#include "nrf_power.h"
+#include "nrf_drv_clock.h"
+#include "nrf_drv_power.h"
+#if APP_USBD_CONFIG_EVENT_QUEUE_ENABLE
+#include "nrf_atfifo.h"
+#include "nrf_atomic.h"
+#endif
+
+#define NRF_LOG_MODULE_NAME app_usbd
+
+#if APP_USBD_CONFIG_LOG_ENABLED
+#define NRF_LOG_LEVEL APP_USBD_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR APP_USBD_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR APP_USBD_CONFIG_DEBUG_COLOR
+#else //APP_USBD_CONFIG_LOG_ENABLED
+#define NRF_LOG_LEVEL 0
+#endif //APP_USBD_CONFIG_LOG_ENABLED
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+
+/* Base variables tests */
+
+/* Check event of app_usbd_event_type_t enumerator */
+STATIC_ASSERT((int32_t)APP_USBD_EVT_FIRST_POWER == (int32_t)NRF_DRV_USBD_EVT_CNT);
+STATIC_ASSERT(sizeof(app_usbd_event_type_t) == sizeof(nrf_drv_usbd_event_type_t));
+
+STATIC_ASSERT(sizeof(app_usbd_descriptor_header_t) == 2);
+STATIC_ASSERT(sizeof(app_usbd_descriptor_device_t) == 18);
+STATIC_ASSERT(sizeof(app_usbd_descriptor_configuration_t) == 9);
+STATIC_ASSERT(sizeof(app_usbd_descriptor_iface_t) == 9);
+STATIC_ASSERT(sizeof(app_usbd_descriptor_ep_t) == 7);
+STATIC_ASSERT(sizeof(app_usbd_descriptor_iad_t) == 8);
+
+STATIC_ASSERT(sizeof(app_usbd_setup_t) == sizeof(nrf_drv_usbd_setup_t));
+
+/**
+ * @internal
+ * @defgroup app_usbd_internals USBD library internals
+ * @ingroup app_usbd
+ *
+ * Internal variables, auxiliary macros and functions of USBD library.
+ * @{
+ */
+
+#if (APP_USBD_PROVIDE_SOF_TIMESTAMP) || defined(__SDK_DOXYGEN__)
+/**
+ * @brief The last received frame number.
+ */
+static uint16_t m_last_frame;
+#endif
+
+/**
+ * @brief Variable type for endpoint configuration
+ *
+ * Each endpoint would have assigned this type of configuration structure.
+ */
+typedef struct
+{
+ /**
+ * @brief The class instance
+ *
+ * The pointer to the class instance that is connected to the endpoint.
+ */
+ app_usbd_class_inst_t const * p_cinst;
+
+ /**
+ * @brief Endpoint event handler.
+ *
+ * Event handler for the endpoint.
+ * It is set to event handler for the class instance during connection by default,
+ * but it can be then updated for as a reaction for @ref APP_USBD_EVT_ATTACHED event.
+ * This way we can speed up the interpretation of endpoint related events.
+ */
+ app_usbd_ep_event_handler_t event_handler;
+}app_usbd_ep_conf_t;
+
+
+/**
+ * @brief Internal event with SOF counter.
+ */
+typedef struct
+{
+ app_usbd_internal_evt_t evt; //!< Internal event type
+
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE) \
+ || defined(__SDK_DOXYGEN__)
+ uint16_t sof_cnt; //!< Number of the SOF events that appears before current event
+ uint16_t start_frame; //!< Number of the SOF frame that starts this event
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+
+} app_usbd_internal_queue_evt_t;
+
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || defined(__SDK_DOXYGEN__)
+/**
+ * @brief Event queue
+ *
+ * The queue with events to be processed
+ */
+NRF_ATFIFO_DEF(m_event_queue, app_usbd_internal_queue_evt_t, APP_USBD_CONFIG_EVENT_QUEUE_SIZE);
+
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE) \
+ || defined(__SDK_DOXYGEN__)
+
+/** @brief SOF events counter */
+static nrf_atomic_u32_t m_sof_events_cnt;
+
+/** @brief SOF Frame counter */
+static uint16_t m_event_frame;
+
+// Limit of SOF events stacked until warning message.
+#define APP_USBD_SOF_WARNING_LIMIT 500
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ // || defined(__SDK_DOXYGEN__)
+
+#endif
+
+/**
+ * @brief Instances connected with IN endpoints
+ *
+ * Array of instance pointers connected with every IN endpoint.
+ * @sa m_epout_instances
+ */
+static app_usbd_ep_conf_t m_epin_conf[NRF_USBD_EPIN_CNT];
+
+/**
+ * @brief Instances connected with OUT endpoints
+ *
+ * Array of instance pointers connected with every OUT endpoint.
+ * @sa m_epin_instances
+ */
+static app_usbd_ep_conf_t m_epout_conf[NRF_USBD_EPIN_CNT];
+
+/**
+ * @brief Beginning of classes list
+ *
+ * All enabled in current configuration instances are connected into
+ * a single linked list chain.
+ * This variable points to first element.
+ * Core class instance (connected to endpoint 0) is not listed here.
+ */
+static app_usbd_class_inst_t const * m_p_first_cinst;
+
+/**
+ * @brief Classes list that requires SOF events
+ *
+ * @todo RK Implement and documentation
+ */
+static app_usbd_class_inst_t const * m_p_first_sof_cinst;
+
+/**
+ * @brief Default configuration (when NULL is passed to @ref app_usbd_init).
+ */
+static const app_usbd_config_t m_default_conf = {
+#if (!(APP_USBD_CONFIG_EVENT_QUEUE_ENABLE)) || defined(__SDK_DOXYGEN__)
+ .ev_handler = app_usbd_event_execute,
+#endif
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || defined(__SDK_DOXYGEN__)
+ .ev_isr_handler = NULL,
+#endif
+ .ev_state_proc = NULL,
+ .enable_sof = false
+};
+
+/**
+ * @brief SUSPEND state machine states
+ *
+ * The enumeration of internal SUSPEND state machine states.
+ */
+typedef enum
+{
+ SUSTATE_STOPPED, /**< The USB driver was not started */
+ SUSTATE_STARTED, /**< The USB driver was started - waiting for USB RESET */
+ SUSTATE_ACTIVE, /**< Active state */
+ SUSTATE_SUSPENDING, /**< Suspending - waiting for the user to acknowledge */
+ SUSTATE_SUSPEND, /**< Suspended */
+ SUSTATE_RESUMING, /**< Resuming - waiting for clock */
+ SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ, /**< Waking up - waiting for clock and WUREQ from driver */
+ SUSTATE_WAKINGUP_WAITING_HFCLK, /**< Waking up - waiting for HFCLK (WUREQ detected) */
+ SUSTATE_WAKINGUP_WAITING_WREQ, /**< Waking up - waiting for WREQ (HFCLK active) */
+}app_usbd_sustate_t;
+
+/**
+ * @brief Current suspend state
+ *
+ * The state of the suspend state machine
+ */
+static app_usbd_sustate_t m_sustate;
+
+/**
+ * @brief Remote wake-up register/unregister
+ *
+ * Counter incremented when appended instance required remote wake-up functionality.
+ * It should be decremented when the class is removed.
+ * When this counter is not zero, remote wake-up functionality is activated inside core.
+ */
+static uint8_t m_rwu_registered_counter;
+
+/**
+ * @brief Current configuration.
+ */
+static app_usbd_config_t m_current_conf;
+
+/**
+ * @brief Class interface call: event handler
+ *
+ * @ref app_usbd_class_interface_t::event_handler
+ *
+ * @param[in] p_cinst Class instance
+ * @param[in] p_event Event passed to class instance
+ *
+ * @return Standard error code @ref ret_code_t
+ * @retval NRF_SUCCESS event handled successfully
+ * @retval NRF_ERROR_NOT_SUPPORTED unsupported event
+ * */
+static inline ret_code_t class_event_handler(app_usbd_class_inst_t const * const p_cinst,
+ app_usbd_complex_evt_t const * const p_event)
+{
+ ASSERT(p_cinst != NULL);
+ ASSERT(p_cinst->p_class_methods != NULL);
+ ASSERT(p_cinst->p_class_methods->event_handler != NULL);
+ return p_cinst->p_class_methods->event_handler(p_cinst, p_event);
+}
+
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || defined(__SDK_DOXYGEN__)
+/**
+ * @brief User event handler call (passed via configuration).
+ *
+ * @param p_event Handler of an event that is going to be added into queue.
+ * @param queued The event is visible in the queue.
+ */
+static inline void user_event_handler(app_usbd_internal_evt_t const * const p_event, bool queued)
+{
+ if ((m_current_conf.ev_isr_handler) != NULL)
+ {
+ m_current_conf.ev_isr_handler(p_event, queued);
+ }
+}
+#endif
+
+/**
+ * @brief User event processor call (passed via configuration)
+ *
+ * @param event Event type.
+ */
+static inline void user_event_state_proc(app_usbd_event_type_t event)
+{
+ if ((m_current_conf.ev_state_proc) != NULL)
+ {
+ m_current_conf.ev_state_proc(event);
+ }
+}
+
+/**
+ * @brief Find a specified descriptor
+ *
+ * @param[in] p_cinst Class instance
+ * @param[in] desc_type Descriptor type @ref app_usbd_descriptor_t
+ * @param[in] desc_index Descriptor index
+ * @param[out] p_desc Pointer to escriptor
+ * @param[out] p_desc_len Length of descriptor
+ *
+ * @return Standard error code @ref ret_code_t
+ * @retval NRF_SUCCESS descriptor successfully found
+ * @retval NRF_ERROR_NOT_FOUND descriptor not found
+ * */
+ret_code_t app_usbd_class_descriptor_find(app_usbd_class_inst_t const * const p_cinst,
+ uint8_t desc_type,
+ uint8_t desc_index,
+ uint8_t * p_desc,
+ size_t * p_desc_len)
+{
+ app_usbd_class_descriptor_ctx_t siz;
+ APP_USBD_CLASS_DESCRIPTOR_INIT(&siz);
+ uint32_t total_size = 0;
+ while(p_cinst->p_class_methods->feed_descriptors(&siz, p_cinst, NULL, sizeof(uint8_t)))
+ {
+ total_size++;
+ }
+
+ uint8_t cur_len = 0;
+ uint32_t cur_size = 0;
+
+ uint8_t index = 0;
+ app_usbd_class_descriptor_ctx_t descr;
+ APP_USBD_CLASS_DESCRIPTOR_INIT(&descr);
+
+ while(cur_size < total_size)
+ {
+ /* First byte of a descriptor is its size */
+ UNUSED_RETURN_VALUE(p_cinst->p_class_methods->feed_descriptors(&descr,
+ p_cinst,
+ &cur_len,
+ sizeof(uint8_t)));
+
+ /* Second byte is type of the descriptor */
+ uint8_t type;
+ UNUSED_RETURN_VALUE(p_cinst->p_class_methods->feed_descriptors(&descr,
+ p_cinst,
+ &type,
+ sizeof(uint8_t)));
+
+ if(type == desc_type)
+ {
+ if(index == desc_index)
+ {
+ /* Copy the length of descriptor to *p_desc_len */
+ *p_desc_len = cur_len;
+ /* Two first bytes of descriptor have already been fed - copy them to *p_desc */
+ *p_desc++ = cur_len;
+ *p_desc++ = desc_type;
+ /* Copy the rest of descriptor to *p_desc */
+ UNUSED_RETURN_VALUE(p_cinst->p_class_methods->feed_descriptors(&descr,
+ p_cinst,
+ p_desc,
+ cur_len-2));
+ return NRF_SUCCESS;
+ }
+ else
+ {
+ index++;
+ }
+ }
+ /* Fast-forward through unmatched descriptor */
+ UNUSED_RETURN_VALUE(p_cinst->p_class_methods->feed_descriptors(&descr,
+ p_cinst,
+ NULL,
+ cur_len-2));
+ cur_size += cur_len;
+ }
+ return NRF_ERROR_NOT_FOUND;
+}
+
+/**
+ * @brief Access into selected endpoint configuration structure
+ *
+ * @param ep Endpoint address
+ * @return A pointer to the endpoint configuration structure
+ *
+ * @note This function would assert when endpoint number is not correct and debugging is enabled.
+ */
+static app_usbd_ep_conf_t * app_usbd_ep_conf_access(nrf_drv_usbd_ep_t ep)
+{
+ if (NRF_USBD_EPIN_CHECK(ep))
+ {
+ uint8_t nr = NRF_USBD_EP_NR_GET(ep);
+ ASSERT(nr < NRF_USBD_EPIN_CNT);
+ return &m_epin_conf[nr];
+ }
+ else
+ {
+ uint8_t nr = NRF_USBD_EP_NR_GET(ep);
+ ASSERT(nr < NRF_USBD_EPOUT_CNT);
+ return &m_epout_conf[nr];
+ }
+}
+
+/**
+ * @brief Accessing instance connected with selected endpoint
+ *
+ * @param ep Endpoint number
+ *
+ * @return The pointer to the instance connected with endpoint
+ */
+static inline app_usbd_class_inst_t const * app_usbd_ep_instance_get(nrf_drv_usbd_ep_t ep)
+{
+ return app_usbd_ep_conf_access(ep)->p_cinst;
+}
+
+/**
+ * @brief Connect instance with selected endpoint
+ *
+ * This function configures instance connected to endpoint but also sets
+ * default event handler function pointer.
+ *
+ * @param ep Endpoint number
+ * @param p_cinst The instance to connect into the selected endpoint.
+ * NULL if endpoint is going to be disconnected.
+ *
+ * @note Disconnecting EP0 is not allowed and protected by assertion.
+ */
+static void app_usbd_ep_instance_set(nrf_drv_usbd_ep_t ep, app_usbd_class_inst_t const * p_cinst)
+{
+ app_usbd_ep_conf_t * p_ep_conf = app_usbd_ep_conf_access(ep);
+ /* Set instance and default event handler */
+ p_ep_conf->p_cinst = p_cinst;
+ if (p_cinst == NULL)
+ {
+ ASSERT((ep != NRF_DRV_USBD_EPOUT0) && (ep != NRF_DRV_USBD_EPIN0)); /* EP0 should never be disconnected */
+ p_ep_conf->event_handler = NULL;
+ }
+ else
+ {
+ p_ep_conf->event_handler = p_cinst->p_class_methods->event_handler;
+ }
+}
+
+/**
+ * @brief Call the core handler
+ *
+ * Core instance is special kind of instance that is connected only to endpoint 0.
+ * It is not present in instance list.
+ * This auxiliary function makes future changes easier.
+ * Just call the event instance for core module here.
+ */
+static inline ret_code_t app_usbd_core_handler_call(app_usbd_internal_evt_t const * const p_event)
+{
+ return m_epout_conf[0].event_handler(
+ m_epout_conf[0].p_cinst,
+ (app_usbd_complex_evt_t const *)p_event);
+}
+
+
+
+/**
+ * @brief Add event for execution
+ *
+ * Dependent on configuration event would be executed in place or would be added into queue
+ * to be executed later.
+ *
+ * @param p_event Event to be executed
+ */
+static inline void app_usbd_event_add(app_usbd_internal_evt_t const * const p_event)
+{
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE)
+
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ if (p_event->app_evt.type == APP_USBD_EVT_DRV_SOF)
+ {
+ CRITICAL_REGION_ENTER();
+ if (m_sof_events_cnt == 0)
+ {
+ m_event_frame = p_event->drv_evt.data.sof.framecnt;
+ }
+ UNUSED_RETURN_VALUE(nrf_atomic_u32_add(&m_sof_events_cnt, 1));
+ CRITICAL_REGION_EXIT();
+
+ user_event_handler(p_event, true);
+ if (m_sof_events_cnt == APP_USBD_SOF_WARNING_LIMIT)
+ {
+ NRF_LOG_WARNING("Stacked over %d SOF events.", APP_USBD_SOF_WARNING_LIMIT);
+ }
+ return;
+ }
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+
+ if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_INTERRUPT)
+ {
+ if (p_event->app_evt.type == APP_USBD_EVT_DRV_SOF)
+ {
+ user_event_handler(p_event, false);
+ app_usbd_event_execute(p_event);
+ return;
+ }
+ }
+
+ nrf_atfifo_item_put_t cx;
+ app_usbd_internal_queue_evt_t * p_event_item = nrf_atfifo_item_alloc(m_event_queue, &cx);
+
+ if (NULL != p_event_item)
+ {
+ bool visible;
+ p_event_item->evt = *p_event;
+
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ CRITICAL_REGION_ENTER();
+ p_event_item->start_frame = m_event_frame - m_sof_events_cnt + 1;
+ p_event_item->sof_cnt = nrf_atomic_u32_fetch_store(&m_sof_events_cnt, 0);
+ CRITICAL_REGION_EXIT();
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+
+ visible = nrf_atfifo_item_put(m_event_queue, &cx);
+ user_event_handler(p_event, visible);
+ }
+ else
+ {
+ NRF_LOG_ERROR("Event queue full.");
+ }
+#else
+ m_current_conf.ev_handler(p_event);
+#endif
+}
+
+/**
+ * @brief Power event handler
+ *
+ * The function that pushes power events into the queue
+ * @param p_event
+ */
+#if APP_USBD_CONFIG_POWER_EVENTS_PROCESS
+static void app_usbd_power_event_handler(nrf_drv_power_usb_evt_t event)
+{
+ switch(event)
+ {
+ case NRF_DRV_POWER_USB_EVT_DETECTED:
+ {
+ static const app_usbd_internal_evt_t ev = {
+ .type = APP_USBD_EVT_POWER_DETECTED
+ };
+ app_usbd_event_add(&ev);
+ break;
+ }
+ case NRF_DRV_POWER_USB_EVT_REMOVED:
+ {
+ static const app_usbd_internal_evt_t ev = {
+ .type = APP_USBD_EVT_POWER_REMOVED
+ };
+ app_usbd_event_add(&ev);
+ break;
+ }
+ case NRF_DRV_POWER_USB_EVT_READY:
+ {
+ static const app_usbd_internal_evt_t ev = {
+ .type = APP_USBD_EVT_POWER_READY
+ };
+ app_usbd_event_add(&ev);
+ break;
+ }
+ default:
+ ASSERT(false);
+ }
+}
+#endif
+
+/**
+ * @brief Event handler
+ *
+ * The function that pushes the event into the queue
+ * @param p_event
+ */
+static void app_usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event)
+{
+ app_usbd_event_add((app_usbd_internal_evt_t const *)p_event);
+}
+
+/**
+ * @brief HF clock ready event handler
+ *
+ * Function that is called when high frequency clock is started
+ *
+ * @param event Event type that comes from clock driver
+ */
+static void app_usbd_hfclk_ready(nrf_drv_clock_evt_type_t event)
+{
+ ASSERT(NRF_DRV_CLOCK_EVT_HFCLK_STARTED == event);
+ static const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_HFCLK_READY
+ };
+ app_usbd_event_add((app_usbd_internal_evt_t const * )&evt_data);
+}
+
+/**
+ * @brief Check if the HFCLK was requested in selected suspend state machine state
+ *
+ *
+ * @param sustate State to be checked
+ *
+ * @retval true High frequency clock was requested in selected state
+ * @retval false High frequency clock was released in selected state
+ */
+static inline bool app_usbd_sustate_with_requested_hfclk(app_usbd_sustate_t sustate)
+{
+ switch(sustate)
+ {
+ case SUSTATE_STOPPED: return false;
+ case SUSTATE_STARTED: return false;
+ case SUSTATE_ACTIVE: return true;
+ case SUSTATE_SUSPENDING: return false;
+ case SUSTATE_SUSPEND: return false;
+ case SUSTATE_RESUMING: return true;
+ case SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ: return true;
+ case SUSTATE_WAKINGUP_WAITING_HFCLK: return true;
+ case SUSTATE_WAKINGUP_WAITING_WREQ: return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * @brief Check it the HFCLK is running in selected suspend state machine state
+ *
+ * @param sustate State to be checked
+ *
+ * @retval true High frequency clock is running in selected state
+ * @retval false High frequency clock is released in selected state
+ */
+static inline bool app_usbd_sustate_with_running_hfclk(app_usbd_sustate_t sustate)
+{
+ switch(sustate)
+ {
+ case SUSTATE_STOPPED: return false;
+ case SUSTATE_STARTED: return false;
+ case SUSTATE_ACTIVE: return true;
+ case SUSTATE_SUSPENDING: return false;
+ case SUSTATE_SUSPEND: return false;
+ case SUSTATE_RESUMING: return false;
+ case SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ: return false;
+ case SUSTATE_WAKINGUP_WAITING_HFCLK: return false;
+ case SUSTATE_WAKINGUP_WAITING_WREQ: return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * @brief Get current suspend state machine state
+ *
+ * @return The state of the suspend state machine
+ */
+static inline app_usbd_sustate_t sustate_get(void)
+{
+ return m_sustate;
+}
+
+/**
+ * @brief Set current suspend state machine state
+ *
+ * @param sustate The requested state of the state machine
+ */
+static inline void sustate_set(app_usbd_sustate_t sustate)
+{
+ if (app_usbd_sustate_with_requested_hfclk(sustate) != app_usbd_sustate_with_requested_hfclk(m_sustate))
+ {
+ if (app_usbd_sustate_with_requested_hfclk(sustate))
+ {
+ static nrf_drv_clock_handler_item_t clock_handler_item =
+ {
+ .event_handler = app_usbd_hfclk_ready
+ };
+ nrf_drv_clock_hfclk_request(&clock_handler_item);
+ }
+ else
+ {
+ nrf_drv_clock_hfclk_release();
+ }
+ }
+ if (app_usbd_sustate_with_running_hfclk(sustate) != app_usbd_sustate_with_running_hfclk(m_sustate))
+ {
+ if (app_usbd_sustate_with_running_hfclk(sustate))
+ {
+ nrf_drv_usbd_active_irq_config();
+ }
+ else
+ {
+ nrf_drv_usbd_suspend_irq_config();
+ }
+ }
+ m_sustate = sustate;
+}
+
+/**
+ * @brief Default selection function for interface
+ *
+ * This function just enables and clears interface endpoints
+ *
+ * @param p_inst See the documentation for @ref app_usbd_iface_select
+ * @param iface_idx See the documentation for @ref app_usbd_iface_select
+ * @param alternate See the documentation for @ref app_usbd_iface_select
+ * @return
+ */
+static inline ret_code_t default_iface_select(
+ app_usbd_class_inst_t const * const p_inst,
+ uint8_t iface_idx,
+ uint8_t alternate)
+{
+ ASSERT(iface_idx <= app_usbd_class_iface_count_get(p_inst));
+
+ if (alternate != 0)
+ {
+ return NRF_ERROR_INVALID_PARAM;
+ }
+
+ app_usbd_class_iface_conf_t const * p_iface = app_usbd_class_iface_get(p_inst, iface_idx);
+ uint8_t ep_count = app_usbd_class_iface_ep_count_get(p_iface);
+
+ for (uint8_t i = 0; i < ep_count; ++i)
+ {
+ /* Enable every endpoint */
+ app_usbd_class_ep_conf_t const * p_ep = app_usbd_class_iface_ep_get(p_iface, i);
+ app_usbd_ep_enable(p_ep->address);
+ }
+ return NRF_SUCCESS;
+}
+
+/**
+ * @brief Default deselection function for interface
+ *
+ * This function just disables all interface endpoints.
+ *
+ * @param p_inst See the documentation for @ref app_usbd_iface_deselect
+ * @param iface_idx See the documentation for @ref app_usbd_iface_deselect
+ */
+static inline void default_iface_deselect(
+ app_usbd_class_inst_t const * const p_inst,
+ uint8_t iface_idx)
+{
+ ASSERT(iface_idx <= app_usbd_class_iface_count_get(p_inst));
+
+ app_usbd_class_iface_conf_t const * p_iface = app_usbd_class_iface_get(p_inst, iface_idx);
+ uint8_t ep_count = app_usbd_class_iface_ep_count_get(p_iface);
+
+ for (uint8_t i = 0; i < ep_count; ++i)
+ {
+ /* Disable every endpoint */
+ app_usbd_class_ep_conf_t const * p_ep = app_usbd_class_iface_ep_get(p_iface, i);
+ app_usbd_ep_disable(p_ep->address);
+ }
+}
+
+
+/** @} */
+
+#if (APP_USBD_PROVIDE_SOF_TIMESTAMP) || defined(__SDK_DOXYGEN__)
+uint32_t app_usbd_sof_timestamp_get(void)
+{
+ return m_last_frame;
+}
+#endif
+
+ret_code_t app_usbd_init(app_usbd_config_t const * p_config)
+{
+ ASSERT(nrf_drv_clock_init_check());
+ ret_code_t ret;
+
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || defined(__SDK_DOXYGEN__)
+ ret = NRF_ATFIFO_INIT(m_event_queue);
+ if (NRF_SUCCESS != ret)
+ {
+ return NRF_ERROR_INTERNAL;
+ }
+#endif
+
+ /* This is called at the beginning to secure multiple calls to init function */
+ ret = nrf_drv_usbd_init(app_usbd_event_handler);
+ if (NRF_SUCCESS != ret)
+ {
+ return ret;
+ }
+
+ /* Clear the variables */
+ m_sustate = SUSTATE_STOPPED;
+ m_p_first_cinst = NULL;
+ m_p_first_sof_cinst = NULL;
+ memset(m_epin_conf , 0, sizeof(m_epin_conf ));
+ memset(m_epout_conf, 0, sizeof(m_epout_conf));
+ /* Save the new configuration */
+ if (p_config == NULL)
+ {
+ m_current_conf = m_default_conf;
+ }
+ else
+ {
+ m_current_conf = *p_config;
+ }
+
+#if (!(APP_USBD_CONFIG_EVENT_QUEUE_ENABLE))
+ if(m_current_conf.ev_handler == NULL)
+ {
+ m_current_conf.ev_handler = m_default_conf.ev_handler;
+ }
+#endif
+
+#if APP_USBD_CONFIG_POWER_EVENTS_PROCESS
+ ret = nrf_drv_power_init(NULL);
+ if ((ret != NRF_SUCCESS) && (ret != NRF_ERROR_MODULE_ALREADY_INITIALIZED))
+ {
+ /* This should never happen */
+ APP_ERROR_HANDLER(ret);
+ }
+#endif
+
+ /*Pin core class to required endpoints*/
+ uint8_t iface_idx;
+ app_usbd_class_iface_conf_t const * p_iface;
+ app_usbd_class_inst_t const * const p_inst = app_usbd_core_instance_access();
+ iface_idx = 0;
+ while ((p_iface = app_usbd_class_iface_get(p_inst, iface_idx++)) != NULL)
+ {
+ uint8_t ep_idx = 0;
+ app_usbd_class_ep_conf_t const * p_ep;
+ while ((p_ep = app_usbd_class_iface_ep_get(p_iface, ep_idx++)) != NULL)
+ {
+ app_usbd_ep_instance_set(app_usbd_class_ep_address_get(p_ep), p_inst);
+ }
+ }
+
+ /* Successfully attached */
+ const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_INST_APPEND
+ };
+
+ ret = class_event_handler(p_inst, (app_usbd_complex_evt_t const *)(&evt_data));
+ if (NRF_SUCCESS != ret)
+ {
+ UNUSED_RETURN_VALUE(nrf_drv_usbd_uninit());
+ return ret;
+ }
+
+ return NRF_SUCCESS;
+}
+
+
+ret_code_t app_usbd_uninit(void)
+{
+#if APP_USBD_CONFIG_POWER_EVENTS_PROCESS
+ nrf_drv_power_usbevt_uninit();
+#endif
+
+ /* We get this error at very beginning but it would be used at the end of the function */
+ const ret_code_t ret = nrf_drv_usbd_uninit();
+
+ /* Unchain instance list */
+ app_usbd_class_inst_t const * * pp_inst;
+ pp_inst = &m_p_first_cinst;
+ while (NULL != (*pp_inst))
+ {
+ app_usbd_class_inst_t const * * pp_next = &app_usbd_class_data_access(*pp_inst)->p_next;
+ (*pp_inst) = NULL;
+ pp_inst = pp_next;
+ }
+
+ /* Unchain SOF list */
+ pp_inst = &m_p_first_sof_cinst;
+ while (NULL != (*pp_inst))
+ {
+ app_usbd_class_inst_t const * * pp_next = &app_usbd_class_data_access(*pp_inst)->p_sof_next;
+ (*pp_inst) = NULL;
+ pp_inst = pp_next;
+ }
+
+ /* Clear all endpoints configurations */
+ memset(m_epin_conf , 0, sizeof(m_epin_conf ));
+ memset(m_epout_conf, 0, sizeof(m_epout_conf));
+ /* Clear current configuration */
+ memset(&m_current_conf, 0, sizeof(m_current_conf));
+
+ return ret;
+}
+
+
+#if APP_USBD_CONFIG_POWER_EVENTS_PROCESS
+ret_code_t app_usbd_power_events_enable(void)
+{
+ if (!nrf_drv_usbd_is_initialized() || nrf_drv_usbd_is_enabled())
+ {
+ return NRF_ERROR_INVALID_STATE;
+ }
+
+ ASSERT((!APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || (USBD_CONFIG_IRQ_PRIORITY == POWER_CONFIG_IRQ_PRIORITY));
+
+ ret_code_t ret;
+ static const nrf_drv_power_usbevt_config_t config =
+ {
+ .handler = app_usbd_power_event_handler
+ };
+
+ ret = nrf_drv_power_usbevt_init(&config);
+ APP_ERROR_CHECK(ret);
+
+ return NRF_SUCCESS;
+}
+#endif /* APP_USBD_CONFIG_POWER_EVENTS_PROCESS */
+
+
+void app_usbd_enable(void)
+{
+ nrf_drv_usbd_enable();
+}
+
+
+void app_usbd_disable(void)
+{
+ ASSERT(!nrf_drv_usbd_is_started());
+ nrf_drv_usbd_disable();
+}
+
+
+void app_usbd_start(void)
+{
+ ASSERT(nrf_drv_usbd_is_enabled());
+
+ /* Check if interface numbers are in correct order */
+ if (APP_USBD_CONFIG_LOG_ENABLED)
+ {
+ uint8_t next_iface = 0;
+ for (app_usbd_class_inst_t const * * pp_inst = &m_p_first_cinst;
+ (*pp_inst) != NULL;
+ pp_inst = &(app_usbd_class_data_access(*pp_inst)->p_next))
+ {
+ uint8_t iface_idx = 0;
+ app_usbd_class_iface_conf_t const * p_iface;
+ while (NULL != (p_iface = app_usbd_class_iface_get(*pp_inst, iface_idx++)))
+ {
+ if (p_iface->number != next_iface)
+ {
+ NRF_LOG_WARNING("Unexpected interface number, expected %d, got %d",
+ next_iface,
+ p_iface->number);
+ }
+ ++next_iface;
+ }
+ }
+ }
+
+ /* Power should be already enabled - wait just in case if user calls
+ * app_usbd_start just after app_usbd_enable without waiting for the event. */
+ while (!nrf_power_usbregstatus_outrdy_get())
+ {
+ /* Wait for the power but terminate the function if USBD power disappears */
+ if (!nrf_power_usbregstatus_vbusdet_get())
+ return;
+ }
+
+ static const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_START_REQ
+ };
+ app_usbd_event_add((app_usbd_internal_evt_t const * )&evt_data);
+}
+
+
+void app_usbd_stop(void)
+{
+ const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_STOP_REQ
+ };
+ app_usbd_event_add((app_usbd_internal_evt_t const * )&evt_data);
+}
+
+void app_usbd_suspend_req(void)
+{
+ const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_SUSPEND_REQ
+ };
+ app_usbd_event_add((app_usbd_internal_evt_t const * )&evt_data);
+}
+
+bool app_usbd_wakeup_req(void)
+{
+ ASSERT(app_usbd_class_rwu_enabled_check());
+ if (!app_usbd_core_feature_state_get(APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP))
+ return false;
+
+ const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_WAKEUP_REQ
+ };
+ app_usbd_event_add((app_usbd_internal_evt_t const * )&evt_data);
+ return true;
+}
+
+bool app_usbd_active_check(void)
+{
+ return (sustate_get() == SUSTATE_ACTIVE);
+}
+
+void app_usbd_event_execute(app_usbd_internal_evt_t const * const p_event)
+{
+ ASSERT(NULL != m_p_first_cinst);
+ /* If no event queue is implemented, it has to be ensured that this function is never called
+ * from the context higher than USB interrupt level
+ * If queue is implemented it would be called always from Thread level
+ * if the library is used correctly.
+ * NOTE: Higher interrupt level -> lower priority value.
+ */
+ ASSERT(USBD_CONFIG_IRQ_PRIORITY <= current_int_priority_get());
+
+ /* Note - there should never be situation that event is generated on disconnected endpoint */
+ switch (p_event->type)
+ {
+ case APP_USBD_EVT_START_REQ:
+ {
+ static const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_STARTED
+ };
+
+ /* Send event to all classes */
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call((app_usbd_internal_evt_t const * )&evt_data));
+ app_usbd_all_call((app_usbd_complex_evt_t const *)&evt_data);
+ user_event_state_proc(APP_USBD_EVT_STARTED);
+
+ nrf_drv_usbd_start((NULL != m_p_first_sof_cinst) || (m_current_conf.enable_sof) || (APP_USBD_PROVIDE_SOF_TIMESTAMP));
+ app_usbd_all_iface_deselect();
+ sustate_set(SUSTATE_STARTED);
+ break;
+ }
+ case APP_USBD_EVT_STOP_REQ:
+ {
+ static const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_STOPPED
+ };
+
+ app_usbd_all_iface_deselect();
+ nrf_drv_usbd_stop();
+ sustate_set(SUSTATE_STOPPED);
+
+ /* Send event to all classes */
+ app_usbd_all_call((app_usbd_complex_evt_t const * )&evt_data);
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call((app_usbd_internal_evt_t const *)&evt_data));
+ user_event_state_proc(APP_USBD_EVT_STOPPED);
+ if (app_usbd_sustate_with_requested_hfclk(sustate_get()))
+ {
+ nrf_drv_clock_hfclk_release();
+ }
+
+ break;
+ }
+ case APP_USBD_EVT_HFCLK_READY:
+ {
+ switch(sustate_get())
+ {
+ case SUSTATE_RESUMING:
+ {
+ sustate_set(SUSTATE_ACTIVE);
+ break;
+ }
+ case SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ:
+ {
+ sustate_set(SUSTATE_WAKINGUP_WAITING_WREQ);
+ break;
+ }
+ case SUSTATE_WAKINGUP_WAITING_HFCLK:
+ {
+ sustate_set(SUSTATE_ACTIVE);
+ break;
+ }
+ default:
+ break; // Just ignore - it can happen in specyfic situation
+ }
+ break;
+ }
+ case APP_USBD_EVT_SUSPEND_REQ:
+ {
+ /* Suspend request can be only processed when we are in suspending state */
+ if (SUSTATE_SUSPENDING == sustate_get())
+ {
+ if (nrf_drv_usbd_suspend())
+ {
+ sustate_set(SUSTATE_SUSPEND);
+ }
+ }
+ break;
+ }
+ case APP_USBD_EVT_WAKEUP_REQ:
+ {
+ /* Suspend temporary if no suspend function was called from the application.
+ * This makes it possible to generate APP_USBD_EVT_DRV_WUREQ event from the driver */
+ if (sustate_get() == SUSTATE_SUSPENDING)
+ {
+ if (nrf_drv_usbd_suspend())
+ {
+ sustate_set(SUSTATE_SUSPEND);
+ }
+ }
+ if (nrf_drv_usbd_wakeup_req())
+ {
+ sustate_set(SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ);
+ }
+ break;
+ }
+
+ case APP_USBD_EVT_DRV_SOF:
+ {
+#if (APP_USBD_PROVIDE_SOF_TIMESTAMP) || defined(__SDK_DOXYGEN__)
+ m_last_frame = p_event->drv_evt.data.sof.framecnt;
+#endif
+ user_event_state_proc(APP_USBD_EVT_DRV_SOF);
+
+ app_usbd_class_inst_t const * p_inst = app_usbd_class_sof_first_get();
+ while (NULL != p_inst)
+ {
+ ret_code_t r = class_event_handler(p_inst, (app_usbd_complex_evt_t const *)p_event);
+ UNUSED_VARIABLE(r);
+ p_inst = app_usbd_class_sof_next_get(p_inst);
+ }
+ break;
+ }
+
+ case APP_USBD_EVT_DRV_RESET:
+ {
+ app_usbd_all_iface_deselect();
+ sustate_set(SUSTATE_ACTIVE);
+ user_event_state_proc(APP_USBD_EVT_DRV_RESET);
+ /* Processing core interface (connected only to EP0) and then all instances from the list */
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call(p_event));
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+ case APP_USBD_EVT_DRV_RESUME:
+ {
+ sustate_set(SUSTATE_RESUMING);
+ user_event_state_proc(APP_USBD_EVT_DRV_RESUME);
+ /* Processing core interface (connected only to EP0) and then all instances from the list */
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call(p_event));
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+ case APP_USBD_EVT_DRV_WUREQ:
+ {
+ static const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_DRV_RESUME
+ };
+ user_event_state_proc(APP_USBD_EVT_DRV_RESUME);
+ /* Processing core interface (connected only to EP0) and then all instances from the list */
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call((app_usbd_internal_evt_t const *)&evt_data));
+ app_usbd_all_call((app_usbd_complex_evt_t const *)&evt_data);
+
+ switch(sustate_get())
+ {
+ case SUSTATE_WAKINGUP_WAITING_HFCLK_WREQ:
+ sustate_set(SUSTATE_WAKINGUP_WAITING_HFCLK);
+ break;
+ case SUSTATE_WAKINGUP_WAITING_WREQ:
+ sustate_set(SUSTATE_ACTIVE);
+ break;
+ default:
+ {
+ // This should not happen - but try to recover by setting directly active state
+ NRF_LOG_WARNING("Unexpected state on WUREQ event (%u)", sustate_get());
+ sustate_set(SUSTATE_ACTIVE);
+ }
+ }
+ break;
+ }
+ case APP_USBD_EVT_DRV_SUSPEND:
+ {
+ sustate_set(SUSTATE_SUSPENDING);
+
+ user_event_state_proc(APP_USBD_EVT_DRV_SUSPEND);
+
+ /* Processing all instances from the list and then core interface (connected only to EP0) */
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call(p_event));
+ break;
+ }
+
+ case APP_USBD_EVT_STATE_CHANGED:
+ {
+ user_event_state_proc(APP_USBD_EVT_STATE_CHANGED);
+ /* Processing all instances from the list and then core interface (connected only to EP0) */
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+
+ case APP_USBD_EVT_DRV_SETUP:
+ {
+ UNUSED_RETURN_VALUE(app_usbd_core_handler_call(p_event));
+ break;
+ }
+
+ case APP_USBD_EVT_DRV_EPTRANSFER:
+ {
+ app_usbd_ep_conf_t const * p_ep_conf =
+ app_usbd_ep_conf_access(p_event->drv_evt.data.eptransfer.ep);
+ ASSERT(NULL != p_ep_conf->p_cinst);
+ ASSERT(NULL != p_ep_conf->event_handler);
+
+ if (NRF_SUCCESS != p_ep_conf->event_handler(p_ep_conf->p_cinst,
+ (app_usbd_complex_evt_t const *)p_event))
+ {
+ /* If error returned, every bulk/interrupt endpoint would be stalled */
+ if (!(0 == NRF_USBD_EP_NR_GET(p_event->drv_evt.data.eptransfer.ep) ||
+ NRF_USBD_EPISO_CHECK(p_event->drv_evt.data.eptransfer.ep)))
+ {
+ nrf_drv_usbd_ep_stall(p_event->drv_evt.data.eptransfer.ep);
+ }
+ }
+ break;
+ }
+#if APP_USBD_CONFIG_POWER_EVENTS_PROCESS
+ case APP_USBD_EVT_POWER_DETECTED:
+ {
+ user_event_state_proc(APP_USBD_EVT_POWER_DETECTED);
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+ case APP_USBD_EVT_POWER_REMOVED:
+ {
+ user_event_state_proc(APP_USBD_EVT_POWER_REMOVED);
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+ case APP_USBD_EVT_POWER_READY:
+ {
+ user_event_state_proc(APP_USBD_EVT_POWER_READY);
+ app_usbd_all_call((app_usbd_complex_evt_t const *)p_event);
+ break;
+ }
+#endif
+ default:
+ ASSERT(0);
+ break;
+ }
+}
+
+
+#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) || defined(__SDK_DOXYGEN__)
+bool app_usbd_event_queue_process(void)
+{
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ app_usbd_internal_evt_t sof_event = {
+ .app_evt.type = APP_USBD_EVT_DRV_SOF
+ };
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ static nrf_atfifo_item_get_t cx;
+ static app_usbd_internal_queue_evt_t * p_event_item = NULL;
+ if (NULL == p_event_item)
+ {
+ p_event_item = nrf_atfifo_item_get(m_event_queue, &cx);
+ }
+
+ if (NULL != p_event_item)
+ {
+
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ if (p_event_item->sof_cnt > 0)
+ {
+ if (p_event_item->start_frame > USBD_FRAMECNTR_FRAMECNTR_Msk)
+ {
+ p_event_item->start_frame = 0;
+ }
+ sof_event.drv_evt.data.sof.framecnt = (p_event_item->start_frame)++;
+ --(p_event_item->sof_cnt);
+ app_usbd_event_execute(&sof_event);
+ return true;
+ }
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+
+ app_usbd_event_execute(&(p_event_item->evt));
+ UNUSED_RETURN_VALUE(nrf_atfifo_item_free(m_event_queue, &cx));
+ p_event_item = NULL;
+ return true;
+ }
+#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ else if (m_sof_events_cnt > 0)
+ {
+ CRITICAL_REGION_ENTER();
+ if (m_event_frame > USBD_FRAMECNTR_FRAMECNTR_Msk)
+ {
+ m_event_frame = 0;
+ }
+ sof_event.drv_evt.data.sof.framecnt = m_event_frame++;
+ UNUSED_RETURN_VALUE(nrf_atomic_u32_sub_hs(&m_sof_events_cnt, 1));
+ CRITICAL_REGION_EXIT();
+ app_usbd_event_execute(&sof_event);
+ return true;
+ }
+#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
+ else
+ {
+ return false;
+ }
+}
+#endif
+
+
+ret_code_t app_usbd_class_append(app_usbd_class_inst_t const * p_cinst)
+{
+ ASSERT(NULL != p_cinst);
+ ASSERT(NULL != p_cinst->p_class_methods);
+ ASSERT(NULL != p_cinst->p_class_methods->event_handler);
+ ASSERT(NULL == app_usbd_class_data_access(p_cinst)->p_next);
+
+ /* This should be only called if USBD is disabled
+ * We simply assume that USBD is enabled if its interrupts are */
+ ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
+
+ /* Check if all required endpoints are available
+ * Checking is splitted from setting to avoid situation that anything
+ * is modified and then operation finishes with error */
+ uint8_t iface_idx;
+ app_usbd_class_iface_conf_t const * p_iface;
+
+ iface_idx = 0;
+ while (NULL != (p_iface = app_usbd_class_iface_get(p_cinst, iface_idx++)))
+ {
+ uint8_t ep_idx = 0;
+ app_usbd_class_ep_conf_t const * p_ep;
+ while (NULL != (p_ep = app_usbd_class_iface_ep_get(p_iface, ep_idx++)))
+ {
+ if (NULL != app_usbd_ep_instance_get(app_usbd_class_ep_address_get(p_ep)))
+ {
+ return NRF_ERROR_BUSY;
+ }
+ }
+ }
+
+ /* Connecting all required endpoints */
+ iface_idx = 0;
+ while (NULL != (p_iface = app_usbd_class_iface_get(p_cinst, iface_idx++)))
+ {
+ uint8_t ep_idx = 0;
+ app_usbd_class_ep_conf_t const * p_ep;
+ while (NULL != (p_ep = app_usbd_class_iface_ep_get(p_iface, ep_idx++)))
+ {
+ app_usbd_ep_instance_set(app_usbd_class_ep_address_get(p_ep), p_cinst);
+ }
+ }
+
+ /* Adding pointer to this instance to the end of the chain */
+ app_usbd_class_inst_t const * * pp_last = &m_p_first_cinst;
+ while (NULL != (*pp_last))
+ {
+ ASSERT((*pp_last) != p_cinst);
+ pp_last = &(app_usbd_class_data_access(*pp_last)->p_next);
+ }
+ (*pp_last) = p_cinst;
+
+ /* Successfully attached */
+ const app_usbd_evt_t evt_data = {.type = APP_USBD_EVT_INST_APPEND };
+ return class_event_handler(p_cinst, (app_usbd_complex_evt_t const *)(&evt_data));
+}
+
+
+ret_code_t app_usbd_class_remove(app_usbd_class_inst_t const * p_cinst)
+{
+ ASSERT(NULL != p_cinst);
+ ASSERT(NULL != p_cinst->p_class_methods);
+ ASSERT(NULL != p_cinst->p_class_methods->event_handler);
+ /** This function should be only called if USBD is disabled */
+ ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
+ ret_code_t ret;
+ /* Remove this class from the chain */
+ app_usbd_class_inst_t const * * pp_last = &m_p_first_cinst;
+ while (NULL != (*pp_last))
+ {
+ if ((*pp_last) == p_cinst)
+ {
+ /* Inform class instance that removing process is going to be started */
+ const app_usbd_evt_t evt_data = {
+ .type = APP_USBD_EVT_INST_REMOVE
+ };
+ ret = class_event_handler(p_cinst, (app_usbd_complex_evt_t const *)(&evt_data));
+ if (ret != NRF_SUCCESS)
+ {
+ return ret;
+ }
+
+ /* Breaking chain */
+ (*pp_last) = (app_usbd_class_data_access(p_cinst)->p_next);
+ app_usbd_class_data_access(p_cinst)->p_next = NULL;
+
+ /* Disconnecting endpoints */
+ uint8_t ep_idx;
+ for (ep_idx = 0; ep_idx < NRF_USBD_EPIN_CNT; ++ep_idx)
+ {
+ nrf_drv_usbd_ep_t ep = NRF_DRV_USBD_EPIN(ep_idx);
+ if (app_usbd_ep_instance_get(ep) == p_cinst)
+ {
+ app_usbd_ep_instance_set(ep, NULL);
+ }
+ }
+ for (ep_idx = 0; ep_idx < NRF_USBD_EPOUT_CNT; ++ep_idx)
+ {
+ nrf_drv_usbd_ep_t ep = NRF_DRV_USBD_EPOUT(ep_idx);
+ if (app_usbd_ep_instance_get(ep) == p_cinst)
+ {
+ app_usbd_ep_instance_set(ep, NULL);
+ }
+ }
+
+ return NRF_SUCCESS;
+ }
+ pp_last = &(app_usbd_class_data_access(*pp_last)->p_next);
+ }
+
+ return NRF_ERROR_NOT_FOUND;
+}
+
+
+ret_code_t app_usbd_class_remove_all(void)
+{
+ ret_code_t ret = NRF_SUCCESS;
+ while (NULL != m_p_first_cinst)
+ {
+ ret = app_usbd_class_remove(m_p_first_cinst);
+ if (ret != NRF_SUCCESS)
+ {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+ret_code_t app_usbd_ep_handler_set(app_usbd_class_inst_t const * const p_cinst,
+ nrf_drv_usbd_ep_t ep,
+ app_usbd_ep_event_handler_t handler)
+{
+ ASSERT(NULL != p_cinst);
+ ASSERT(NULL != handler);
+ /** This function should be only called if USBD is disabled */
+ ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
+
+ if (p_cinst != app_usbd_ep_instance_get(ep))
+ {
+ return NRF_ERROR_INVALID_PARAM;
+ }
+
+ (app_usbd_ep_conf_access(ep))->event_handler = handler;
+ return NRF_SUCCESS;
+}
+
+
+ret_code_t app_usbd_class_sof_register(app_usbd_class_inst_t const * p_cinst)
+{
+ ASSERT(NULL != p_cinst);
+ ASSERT(NULL != p_cinst->p_class_methods);
+ ASSERT(NULL != p_cinst->p_class_methods->event_handler);
+ /** This function should be only called if USBD is disabled */
+ ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
+
+ /* Next SOF event requiring instance have to be null now */
+ ASSERT(NULL == (app_usbd_class_data_access(p_cinst)->p_sof_next));
+
+ /* Adding pointer to this instance to the end of the chain */
+ app_usbd_class_inst_t const * * pp_last = &m_p_first_sof_cinst;
+ while (NULL != (*pp_last))
+ {
+
+ ASSERT((*pp_last) != p_cinst);
+ pp_last = &(app_usbd_class_data_access(*pp_last)->p_sof_next);
+ }
+ (*pp_last) = p_cinst;
+
+ return NRF_SUCCESS;
+}
+
+
+ret_code_t app_usbd_class_sof_unregister(app_usbd_class_inst_t const * p_cinst)
+{
+ ASSERT(NULL != p_cinst);
+ /** This function should be only called if USBD is disabled */
+ ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
+
+ app_usbd_class_inst_t const * * pp_last = &m_p_first_sof_cinst;
+ while (NULL != (*pp_last))
+ {
+ if ((*pp_last) == p_cinst)
+ {
+ /* Breaking chain */
+ (*pp_last) = (app_usbd_class_data_access(p_cinst)->p_sof_next);
+ app_usbd_class_data_access(p_cinst)->p_sof_next = NULL;
+
+ return NRF_SUCCESS;
+ }
+ pp_last = &(app_usbd_class_data_access(*pp_last)->p_sof_next);
+ }
+ return NRF_ERROR_NOT_FOUND;
+}
+
+
+ret_code_t app_usbd_class_rwu_register(app_usbd_class_inst_t const * const p_inst)
+{
+ ASSERT(p_inst != NULL);
+ ++m_rwu_registered_counter;
+ /*Overflow check*/
+ ASSERT(m_rwu_registered_counter != 0);
+
+ return NRF_SUCCESS;
+}
+
+
+ret_code_t app_usbd_class_rwu_unregister(app_usbd_class_inst_t const * const p_inst)
+{
+ ASSERT(p_inst != NULL);
+ /* Usage validation. If counter is 0 unregister is not possible.*/
+ ASSERT(m_rwu_registered_counter != 0);
+ --m_rwu_registered_counter;
+
+ return NRF_SUCCESS;
+}
+
+bool app_usbd_class_rwu_enabled_check(void)
+{
+ return (m_rwu_registered_counter != 0);
+}
+
+ret_code_t app_usbd_interface_ep_reset(app_usbd_class_inst_t const * const p_cinst,
+ uint8_t iface)
+{
+ uint8_t iface_count = app_usbd_class_iface_count_get(p_cinst);
+
+ app_usbd_class_iface_conf_t const * p_iface = NULL;
+ for (uint8_t j = 0; j < iface_count; ++j)
+ {
+ p_iface = app_usbd_class_iface_get(p_cinst, j);
+ if (app_usbd_class_iface_number_get(p_iface) == iface)
+ {
+ break;
+ }
+ }
+
+ if (p_iface == NULL)
+ {
+ return NRF_ERROR_NOT_SUPPORTED;
+ }
+
+ uint8_t ep_count = app_usbd_class_iface_ep_count_get(p_iface);
+
+ for (uint8_t j = 0; j < ep_count; ++j)
+ {
+ /*Clear stall for every endpoint*/
+ app_usbd_class_ep_conf_t const * p_ep = app_usbd_class_iface_ep_get(p_iface, j);
+
+ if (!NRF_USBD_EPISO_CHECK(p_ep->address))
+ {
+ nrf_drv_usbd_ep_dtoggle_clear(p_ep->address);
+ nrf_drv_usbd_ep_stall_clear(p_ep->address);
+ }
+
+ }
+
+ return NRF_SUCCESS;
+}
+
+void app_usbd_ep_enable(nrf_drv_usbd_ep_t ep)
+{
+ if (!NRF_USBD_EPISO_CHECK(ep))
+ {
+ nrf_drv_usbd_ep_dtoggle_clear(ep);
+ nrf_drv_usbd_ep_stall_clear(ep);
+ }
+ nrf_drv_usbd_ep_enable(ep);
+}
+
+void app_usbd_ep_disable(nrf_drv_usbd_ep_t ep)
+{
+ nrf_drv_usbd_ep_disable(ep);
+}
+
+
+app_usbd_class_inst_t const * app_usbd_class_first_get(void)
+{
+ return m_p_first_cinst;
+}
+
+app_usbd_class_inst_t const * app_usbd_class_sof_first_get(void)
+{
+ return m_p_first_sof_cinst;
+}
+
+app_usbd_class_inst_t const * app_usbd_iface_find(uint8_t iface, uint8_t * p_iface_idx)
+{
+ app_usbd_class_inst_t const * p_inst = app_usbd_class_first_get();
+ while (p_inst != NULL)
+ {
+ uint8_t iface_count = app_usbd_class_iface_count_get(p_inst);
+ /* Iterate over interfaces */
+ for (uint8_t i = 0; i < iface_count; ++i)
+ {
+ app_usbd_class_iface_conf_t const * p_iface;
+ p_iface = app_usbd_class_iface_get(p_inst, i);
+ if (app_usbd_class_iface_number_get(p_iface) == iface)
+ {
+ if (p_iface_idx != NULL)
+ {
+ (*p_iface_idx) = i;
+ }
+ return p_inst;
+ }
+ }
+ p_inst = app_usbd_class_next_get(p_inst);
+ }
+ return NULL;
+}
+
+ret_code_t app_usbd_iface_call(
+ app_usbd_class_inst_t const * const p_class_inst,
+ uint8_t iface_idx,
+ app_usbd_complex_evt_t const * const p_event)
+{
+ UNUSED_PARAMETER(iface_idx);
+ return class_event_handler(p_class_inst, p_event);
+}
+
+ret_code_t app_usbd_ep_call(nrf_drv_usbd_ep_t ep, app_usbd_complex_evt_t const * const p_event)
+{
+ if (NRF_USBD_EP_VALIDATE(ep))
+ {
+ app_usbd_class_inst_t const * p_inst = app_usbd_ep_conf_access(ep)->p_cinst;
+ if (p_inst != NULL)
+ {
+ return class_event_handler(p_inst, p_event);
+ }
+ }
+ return NRF_ERROR_INVALID_ADDR;
+}
+
+void app_usbd_all_call(app_usbd_complex_evt_t const * const p_event)
+{
+ app_usbd_class_inst_t const * p_inst;
+ for (p_inst = app_usbd_class_first_get(); NULL != p_inst;
+ p_inst = app_usbd_class_next_get(p_inst))
+ {
+ UNUSED_RETURN_VALUE(class_event_handler(p_inst, p_event));
+ }
+}
+
+ret_code_t app_usbd_all_until_served_call(app_usbd_complex_evt_t const * const p_event)
+{
+ app_usbd_class_inst_t const * p_inst;
+ ret_code_t ret = NRF_ERROR_NOT_SUPPORTED;
+ /* Try to process via every instance */
+ for (p_inst = app_usbd_class_first_get(); NULL != p_inst;
+ p_inst = app_usbd_class_next_get(p_inst))
+ {
+
+ ret = class_event_handler(p_inst, p_event);
+ if (NRF_ERROR_NOT_SUPPORTED != ret)
+ {
+ /* Processing finished */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+ret_code_t app_usbd_ep_transfer(
+ nrf_drv_usbd_ep_t ep,
+ nrf_drv_usbd_transfer_t const * const p_transfer)
+{
+ if (!nrf_drv_usbd_ep_enable_check(ep))
+ {
+ return NRF_ERROR_INVALID_STATE;
+ }
+ if (m_sustate != SUSTATE_ACTIVE)
+ {
+ return NRF_ERROR_INVALID_STATE;
+ }
+ return nrf_drv_usbd_ep_transfer(ep, p_transfer);
+}
+
+ret_code_t app_usbd_ep_handled_transfer(
+ nrf_drv_usbd_ep_t ep,
+ nrf_drv_usbd_handler_desc_t const * const p_handler)
+{
+ if (!nrf_drv_usbd_ep_enable_check(ep))
+ {
+ return NRF_ERROR_INVALID_STATE;
+ }
+ if (m_sustate != SUSTATE_ACTIVE)
+ {
+ return NRF_ERROR_INVALID_STATE;
+ }
+ return nrf_drv_usbd_ep_handled_transfer(ep, p_handler);
+}
+
+ret_code_t app_usbd_iface_select(
+ app_usbd_class_inst_t const * const p_inst,
+ uint8_t iface_idx,
+ uint8_t alternate)
+{
+ ret_code_t ret = NRF_ERROR_NOT_SUPPORTED;
+
+ if (p_inst->p_class_methods->iface_select != NULL)
+ {
+ ret = p_inst->p_class_methods->iface_select(p_inst, iface_idx, alternate);
+ }
+
+ if(ret == NRF_ERROR_NOT_SUPPORTED)
+ {
+ ret = default_iface_select(p_inst, iface_idx, alternate);
+ }
+ return ret;
+}
+
+void app_usbd_iface_deselect(
+ app_usbd_class_inst_t const * const p_inst,
+ uint8_t iface_idx)
+{
+ if (p_inst->p_class_methods->iface_deselect != NULL)
+ {
+ p_inst->p_class_methods->iface_deselect(p_inst, iface_idx);
+ }
+ default_iface_deselect(p_inst, iface_idx);
+}
+
+uint8_t app_usbd_iface_selection_get(
+ app_usbd_class_inst_t const * const p_inst,
+ uint8_t iface_idx)
+{
+ uint8_t alt = 0;
+ if (p_inst->p_class_methods->iface_selection_get != NULL)
+ {
+ alt = p_inst->p_class_methods->iface_selection_get(p_inst, iface_idx);
+ }
+ return alt;
+}
+
+void app_usbd_all_iface_select_0(void)
+{
+ app_usbd_class_inst_t const * p_inst = app_usbd_class_first_get();
+ while (p_inst != NULL)
+ {
+ uint8_t iface_count = app_usbd_class_iface_count_get(p_inst);
+ for (uint8_t i = 0; i < iface_count; ++i)
+ {
+ ret_code_t ret;
+ ret = app_usbd_iface_select(p_inst, i, 0);
+ ASSERT(ret == NRF_SUCCESS);
+ UNUSED_VARIABLE(ret);
+ }
+ p_inst = app_usbd_class_next_get(p_inst);
+ }
+}
+
+void app_usbd_all_iface_deselect(void)
+{
+ app_usbd_class_inst_t const * p_inst = app_usbd_class_first_get();
+ while (p_inst != NULL)
+ {
+ uint8_t iface_count = app_usbd_class_iface_count_get(p_inst);
+ for (uint8_t i = 0; i < iface_count; ++i)
+ {
+ app_usbd_iface_deselect(p_inst, i);
+ }
+ p_inst = app_usbd_class_next_get(p_inst);
+ }
+}
+
+#endif //NRF_MODULE_ENABLED(APP_USBD)