1920 lines
60 KiB
C
1920 lines
60 KiB
C
/**
|
|
* 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)
|