| /** |
| * Copyright (c) 2016 - 2019, 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 "app_usbd_request.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. |
| * |
| * Pointer to first class that requires SOF events. |
| */ |
| static app_usbd_class_inst_t const * m_p_first_sof_cinst; |
| |
| /** |
| * @brief Classes list that require SOF events in interrupt. |
| * |
| * Pointer to first class that requires SOF events in interrupt. |
| */ |
| static app_usbd_class_inst_t const * m_p_first_sof_interrupt_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__) |
| static inline void class_sof_interrupt_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_data != NULL); |
| ASSERT(p_cinst->p_data->sof_handler != NULL); |
| p_cinst->p_data->sof_handler(p_event->drv_evt.data.sof.framecnt); |
| } |
| |
| /** |
| * @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_input Event to be executed. |
| */ |
| static inline void app_usbd_event_add(app_usbd_internal_evt_t const * const p_event_input) |
| { |
| app_usbd_internal_evt_t const * p_event = p_event_input; |
| |
| |
| if (p_event->type == APP_USBD_EVT_DRV_SETUP) |
| { |
| uint8_t bRequest = nrf_usbd_setup_brequest_get(); |
| uint8_t bmRequestType = nrf_usbd_setup_bmrequesttype_get(); |
| |
| if ((bmRequestType == app_usbd_setup_req_val( |
| APP_USBD_SETUP_REQREC_DEVICE, |
| APP_USBD_SETUP_REQTYPE_STD, |
| APP_USBD_SETUP_REQDIR_OUT)) |
| && (bRequest == APP_USBD_SETUP_STDREQ_SET_ADDRESS)) |
| { |
| static const app_usbd_internal_evt_t event_setaddress = |
| { |
| .type = APP_USBD_EVT_SETUP_SETADDRESS, |
| }; |
| p_event = &event_setaddress; |
| } |
| } |
| |
| #if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE) |
| |
| if (p_event->app_evt.type == APP_USBD_EVT_DRV_SOF) |
| { |
| /* Propagate SOF event to classes that need it in interrupt */ |
| app_usbd_class_inst_t const * p_inst = app_usbd_class_sof_interrupt_first_get(); |
| while (NULL != p_inst) |
| { |
| class_sof_interrupt_handler(p_inst, (app_usbd_complex_evt_t const *)p_event); |
| p_inst = app_usbd_class_sof_interrupt_next_get(p_inst); |
| } |
| |
| #if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE) |
| 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) |
| |
| user_event_handler(p_event, false); |
| app_usbd_event_execute(p_event); |
| return; |
| |
| #endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_INTERRUPT) |
| } |
| |
| 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 Event from power driver to map into APP_USBD_EVT_POWER_ 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 Event to push. |
| */ |
| 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[in] p_inst Class instance. |
| * @param[in] iface_idx Interface index. |
| * @param[in] alternate Interface alternate setting. |
| * |
| * @note Currently only alternate setting 0 is supported. |
| * |
| * @return Standard error code @ref ret_code_t |
| * @retval NRF_SUCCESS Endpoints enabled and cleared. |
| * @retval NRF_ERROR_INVALID_PARAM Unsupported alternate selected. |
| */ |
| 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[in] p_inst Class instance. |
| * @param[in] iface_idx Interface index. |
| */ |
| 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; |
| } |
| |
| /* Unchain SOF interrupt list */ |
| pp_inst = &m_p_first_sof_interrupt_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); |
| |
| app_usbd_all_iface_deselect(); |
| app_usbd_core_ep0_disable(); |
| nrf_drv_usbd_start((NULL != m_p_first_sof_cinst) || (m_current_conf.enable_sof) || (APP_USBD_PROVIDE_SOF_TIMESTAMP)); |
| 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 specific 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 |
| /* Wake up if suspended */ |
| if ((sustate_get() == SUSTATE_SUSPENDING) || (sustate_get() == SUSTATE_WAKINGUP_WAITING_WREQ)) |
| { |
| static const app_usbd_evt_t evt_data = { |
| .type = APP_USBD_EVT_DRV_RESUME |
| }; |
| app_usbd_event_execute((app_usbd_internal_evt_t *)&evt_data); |
| } |
| |
| 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(); |
| app_usbd_core_ep0_enable(); |
| 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: |
| { |
| if (sustate_get() == SUSTATE_WAKINGUP_WAITING_WREQ) |
| { |
| sustate_set(SUSTATE_ACTIVE); |
| nrf_drv_usbd_force_bus_wakeup(); |
| } |
| else |
| { |
| 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_SETUP_SETADDRESS: |
| { |
| 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()); |
| |
| /* Make sure it's not in interrupt SOF list */ |
| app_usbd_class_inst_t const * * pp_last = &m_p_first_sof_interrupt_cinst; |
| while (NULL != (*pp_last)) |
| { |
| ASSERT((*pp_last) != p_cinst); |
| pp_last = &(app_usbd_class_data_access(*pp_last)->p_sof_next); |
| } |
| |
| /* Next SOF event requiring instance has 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 */ |
| 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_sof_interrupt_register(app_usbd_class_inst_t const * p_cinst, app_usbd_sof_interrupt_handler_t handler) |
| { |
| ASSERT(NULL != p_cinst); |
| ASSERT(NULL != p_cinst->p_class_methods); |
| ASSERT(NULL != 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 has to be NULL now */ |
| ASSERT(NULL == (app_usbd_class_data_access(p_cinst)->p_sof_next)); |
| |
| app_usbd_class_data_access(p_cinst)->sof_handler = handler; |
| |
| /* Make sure it's not in normal SOF list */ |
| 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); |
| } |
| |
| /* Adding pointer to this instance to the end of the interrupt chain */ |
| pp_last = &m_p_first_sof_interrupt_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_interrupt_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_interrupt_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_class_sof_interrupt_first_get(void) |
| { |
| return m_p_first_sof_interrupt_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) |