mirror of
https://github.com/jam422470459/EPD-nRF52-hema213.git
synced 2025-12-19 23:03:21 +08:00
899 lines
27 KiB
C
899 lines
27 KiB
C
/**
|
|
* Copyright (c) 2016 - 2017, 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_config.h"
|
|
#if APP_USBD_ENABLED
|
|
#include "sdk_common.h"
|
|
#include "app_usbd.h"
|
|
#include "app_usbd_core.h"
|
|
|
|
/* Base variables tests */
|
|
|
|
/* Check event of app_usbd_event_type_t enumerator */
|
|
STATIC_ASSERT((int32_t)APP_USBD_EVT_FIRST_APP == (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.
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @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 Instances connected with IN endpoints
|
|
*
|
|
* Array of instance pointers connected with every IN endpoint.
|
|
* @sa m_epout_instances
|
|
*/
|
|
static app_usbd_ep_conf_t m_epin_conf[NRF_USBD_EPIN_CNT];
|
|
|
|
/**
|
|
* @brief Instances connected with OUT endpoints
|
|
*
|
|
* Array of instance pointers connected with every OUT endpoint.
|
|
* @sa m_epin_instances
|
|
*/
|
|
static app_usbd_ep_conf_t m_epout_conf[NRF_USBD_EPIN_CNT];
|
|
|
|
/**
|
|
* @brief Beginning of classes list
|
|
*
|
|
* All enabled in current configuration instances are connected into
|
|
* a single linked list chain.
|
|
* This variable points to first element.
|
|
* Core class instance (connected to endpoint 0) is not listed here.
|
|
*/
|
|
static app_usbd_class_inst_t const * m_p_first_cinst;
|
|
|
|
/**
|
|
* @brief Classes list that requires SOF events
|
|
*
|
|
* @todo RK Implement and documentation
|
|
*/
|
|
static app_usbd_class_inst_t const * m_p_first_sof_cinst;
|
|
|
|
/**
|
|
* @brief 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);
|
|
}
|
|
|
|
/**
|
|
* @brief Class interface call: get descriptors
|
|
*
|
|
* @ref app_usbd_class_interface_t::get_descriptors
|
|
*
|
|
* @param[in] p_inst Class instance
|
|
* @param[out] p_size Descriptors size
|
|
*
|
|
* @return Class descriptors start address
|
|
* */
|
|
static inline const void * class_get_descriptors(app_usbd_class_inst_t const * const p_cinst,
|
|
size_t * p_size)
|
|
{
|
|
ASSERT(p_cinst != NULL);
|
|
ASSERT(p_cinst->p_class_methods != NULL);
|
|
ASSERT(p_cinst->p_class_methods->get_descriptors != NULL);
|
|
return p_cinst->p_class_methods->get_descriptors(p_cinst, p_size);
|
|
}
|
|
|
|
const void * app_usbd_class_descriptor_find(app_usbd_class_inst_t const * const p_cinst,
|
|
uint8_t desc_type,
|
|
uint8_t desc_index,
|
|
size_t * p_desc_len)
|
|
{
|
|
app_usbd_descriptor_header_t const * p_header;
|
|
uint8_t const * p_raw = class_get_descriptors(p_cinst, p_desc_len);
|
|
if (p_raw == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
size_t pos = 0;
|
|
uint8_t index = 0;
|
|
while (pos < *p_desc_len)
|
|
{
|
|
p_header = (app_usbd_descriptor_header_t const *)(p_raw + pos);
|
|
if (p_header->bDescriptorType == desc_type)
|
|
{
|
|
if (desc_index == index)
|
|
{
|
|
*p_desc_len = p_header->bLength;
|
|
return p_header;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
pos += p_header->bLength;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @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(nrf_drv_usbd_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 USBD event handler
|
|
*
|
|
*
|
|
*/
|
|
static void app_usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event)
|
|
{
|
|
ASSERT(NULL != m_p_first_cinst);
|
|
|
|
/* Note - there should never be situation that event is generated on disconnected endpoint */
|
|
switch(p_event->type)
|
|
{
|
|
case NRF_DRV_USBD_EVT_SOF:
|
|
{
|
|
app_usbd_class_inst_t const * p_inst = app_usbd_class_sof_first_get();
|
|
ASSERT(NULL != p_inst); /* This should not happen - when no SOF instances are in the list, SOF event is disabled */
|
|
|
|
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;
|
|
}
|
|
|
|
/* Reset and */
|
|
case NRF_DRV_USBD_EVT_RESET:
|
|
case NRF_DRV_USBD_EVT_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 NRF_DRV_USBD_EVT_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 NRF_DRV_USBD_EVT_SETUP:
|
|
{
|
|
UNUSED_RETURN_VALUE(app_usbd_core_handler_call(p_event));
|
|
break;
|
|
}
|
|
|
|
case NRF_DRV_USBD_EVT_EPTRANSFER:
|
|
{
|
|
app_usbd_ep_conf_t const * p_ep_conf =
|
|
app_usbd_ep_conf_access(p_event->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->data.eptransfer.ep) ||
|
|
NRF_USBD_EPISO_CHECK(p_event->data.eptransfer.ep)))
|
|
{
|
|
nrf_drv_usbd_ep_stall(p_event->data.eptransfer.ep);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** @} */
|
|
|
|
|
|
ret_code_t app_usbd_init(void)
|
|
{
|
|
ret_code_t ret;
|
|
|
|
ret = nrf_drv_usbd_init(app_usbd_event_handler);
|
|
if (NRF_SUCCESS != ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Clear variables */
|
|
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));
|
|
|
|
/*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
|
|
};
|
|
|
|
return class_event_handler(p_inst, (app_usbd_complex_evt_t const *)(&evt_data));
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_uninit(void)
|
|
{
|
|
ret_code_t ret;
|
|
|
|
ret = nrf_drv_usbd_uninit();
|
|
if(NRF_SUCCESS != ret)
|
|
return ret;
|
|
|
|
/* Unchain instance list */
|
|
app_usbd_class_inst_t const * * pp_inst;
|
|
pp_inst = &m_p_first_cinst;
|
|
while (NULL != (*pp_inst))
|
|
{
|
|
app_usbd_class_inst_t const * * pp_next = &app_usbd_class_data_access(*pp_inst)->p_next;
|
|
(*pp_inst) = NULL;
|
|
pp_inst = pp_next;
|
|
}
|
|
|
|
/* Unchain SOF list */
|
|
pp_inst = &m_p_first_sof_cinst;
|
|
while (NULL != (*pp_inst))
|
|
{
|
|
app_usbd_class_inst_t const * * pp_next = &app_usbd_class_data_access(*pp_inst)->p_sof_next;
|
|
(*pp_inst) = NULL;
|
|
pp_inst = pp_next;
|
|
}
|
|
|
|
/* Clear all endpoints configurations */
|
|
memset(m_epin_conf , 0, sizeof(m_epin_conf ));
|
|
memset(m_epout_conf, 0, sizeof(m_epout_conf));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void app_usbd_enable(void)
|
|
{
|
|
nrf_drv_usbd_enable();
|
|
while (!app_usbd_core_power_regulator_is_ready())
|
|
;
|
|
}
|
|
|
|
|
|
void app_usbd_disable(void)
|
|
{
|
|
if (nrf_drv_usbd_is_started())
|
|
{
|
|
app_usbd_stop();
|
|
}
|
|
nrf_drv_usbd_disable();
|
|
}
|
|
|
|
|
|
void app_usbd_start(void)
|
|
{
|
|
const app_usbd_evt_t evt_data = {
|
|
.type = APP_USBD_EVT_START
|
|
};
|
|
|
|
/* Enable all connected endpoints */
|
|
uint8_t n;
|
|
for (n = 1; n < ARRAY_SIZE(m_epin_conf); ++n)
|
|
{
|
|
if (NULL != m_epin_conf[n].p_cinst)
|
|
{
|
|
nrf_drv_usbd_ep_enable(NRF_DRV_USBD_EPIN(n));
|
|
}
|
|
}
|
|
for (n = 1; n < ARRAY_SIZE(m_epout_conf); ++n)
|
|
{
|
|
if (NULL != m_epout_conf[n].p_cinst)
|
|
{
|
|
nrf_drv_usbd_ep_enable(NRF_DRV_USBD_EPOUT(n));
|
|
}
|
|
}
|
|
|
|
/* Send event to all classes */
|
|
UNUSED_RETURN_VALUE(app_usbd_core_handler_call((nrf_drv_usbd_evt_t const * )&evt_data));
|
|
app_usbd_all_call((app_usbd_complex_evt_t const *)&evt_data);
|
|
|
|
nrf_drv_usbd_start(NULL != m_p_first_sof_cinst);
|
|
}
|
|
|
|
|
|
void app_usbd_stop(void)
|
|
{
|
|
const app_usbd_evt_t evt_data = {
|
|
.type = APP_USBD_EVT_STOP
|
|
};
|
|
|
|
nrf_drv_usbd_stop();
|
|
|
|
/* 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((nrf_drv_usbd_evt_t const *)&evt_data));
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_class_append(app_usbd_class_inst_t const * p_cinst)
|
|
{
|
|
ASSERT(NULL != p_cinst);
|
|
ASSERT(NULL != p_cinst->p_class_methods);
|
|
ASSERT(NULL != p_cinst->p_class_methods->event_handler);
|
|
ASSERT(NULL == app_usbd_class_data_access(p_cinst)->p_next);
|
|
|
|
/* This should be only called if USBD is disabled
|
|
* We simply assume that USBD is enabled if its interrupts are */
|
|
ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
|
|
|
|
/* Check if all required endpoints are available
|
|
* Checking is splitted from setting to avoid situation that anything
|
|
* is modified and then operation finishes with error */
|
|
uint8_t iface_idx;
|
|
app_usbd_class_iface_conf_t const * p_iface;
|
|
|
|
iface_idx = 0;
|
|
while (NULL != (p_iface = app_usbd_class_iface_get(p_cinst, iface_idx++)))
|
|
{
|
|
uint8_t ep_idx = 0;
|
|
app_usbd_class_ep_conf_t const * p_ep;
|
|
while (NULL != (p_ep = app_usbd_class_iface_ep_get(p_iface, ep_idx++)))
|
|
{
|
|
if (NULL != app_usbd_ep_instance_get(app_usbd_class_ep_address_get(p_ep)))
|
|
{
|
|
return NRF_ERROR_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Connecting all required endpoints */
|
|
iface_idx = 0;
|
|
while (NULL != (p_iface = app_usbd_class_iface_get(p_cinst, iface_idx++)))
|
|
{
|
|
uint8_t ep_idx = 0;
|
|
app_usbd_class_ep_conf_t const * p_ep;
|
|
while (NULL != (p_ep = app_usbd_class_iface_ep_get(p_iface, ep_idx++)))
|
|
{
|
|
app_usbd_ep_instance_set(app_usbd_class_ep_address_get(p_ep), p_cinst);
|
|
}
|
|
}
|
|
|
|
/* Adding pointer to this instance to the end of the chain */
|
|
app_usbd_class_inst_t const * * pp_last = &m_p_first_cinst;
|
|
while (NULL != (*pp_last))
|
|
{
|
|
ASSERT((*pp_last) != p_cinst);
|
|
pp_last = &(app_usbd_class_data_access(*pp_last)->p_next);
|
|
}
|
|
(*pp_last) = p_cinst;
|
|
|
|
/* Successfully attached */
|
|
const app_usbd_evt_t evt_data = {.type = APP_USBD_EVT_INST_APPEND };
|
|
return class_event_handler(p_cinst, (app_usbd_complex_evt_t const *)(&evt_data));
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_class_remove(app_usbd_class_inst_t const * p_cinst)
|
|
{
|
|
ASSERT(NULL != p_cinst);
|
|
ASSERT(NULL != p_cinst->p_class_methods);
|
|
ASSERT(NULL != p_cinst->p_class_methods->event_handler);
|
|
/** This function should be only called if USBD is disabled */
|
|
ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
|
|
ret_code_t ret;
|
|
/* Remove this class from the chain */
|
|
app_usbd_class_inst_t const * * pp_last = &m_p_first_cinst;
|
|
while (NULL != (*pp_last))
|
|
{
|
|
if ((*pp_last) == p_cinst)
|
|
{
|
|
/* Inform class instance that removing process is going to be started */
|
|
const app_usbd_evt_t evt_data = {
|
|
.type = APP_USBD_EVT_INST_REMOVE
|
|
};
|
|
ret = class_event_handler(p_cinst, (app_usbd_complex_evt_t const *)(&evt_data));
|
|
if (ret != NRF_SUCCESS)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Breaking chain */
|
|
(*pp_last) = (app_usbd_class_data_access(p_cinst)->p_next);
|
|
app_usbd_class_data_access(p_cinst)->p_next = NULL;
|
|
|
|
/* Disconnecting endpoints */
|
|
uint8_t ep_idx;
|
|
for (ep_idx = 0; ep_idx < NRF_USBD_EPIN_CNT; ++ep_idx)
|
|
{
|
|
nrf_drv_usbd_ep_t ep = NRF_DRV_USBD_EPIN(ep_idx);
|
|
if (app_usbd_ep_instance_get(ep) == p_cinst)
|
|
{
|
|
app_usbd_ep_instance_set(ep, NULL);
|
|
}
|
|
}
|
|
for (ep_idx = 0; ep_idx < NRF_USBD_EPOUT_CNT; ++ep_idx)
|
|
{
|
|
nrf_drv_usbd_ep_t ep = NRF_DRV_USBD_EPOUT(ep_idx);
|
|
if (app_usbd_ep_instance_get(ep) == p_cinst)
|
|
{
|
|
app_usbd_ep_instance_set(ep, NULL);
|
|
}
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
pp_last = &(app_usbd_class_data_access(*pp_last)->p_next);
|
|
}
|
|
|
|
return NRF_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_class_remove_all(void)
|
|
{
|
|
ret_code_t ret = NRF_SUCCESS;
|
|
while (NULL != m_p_first_cinst)
|
|
{
|
|
ret = app_usbd_class_remove(m_p_first_cinst);
|
|
if (ret != NRF_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_ep_handler_set(app_usbd_class_inst_t const * const p_cinst,
|
|
nrf_drv_usbd_ep_t ep,
|
|
app_usbd_ep_event_handler_t handler)
|
|
{
|
|
ASSERT(NULL != p_cinst);
|
|
ASSERT(NULL != handler);
|
|
/** This function should be only called if USBD is disabled */
|
|
ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
|
|
|
|
if (p_cinst != app_usbd_ep_instance_get(ep))
|
|
{
|
|
return NRF_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
(app_usbd_ep_conf_access(ep))->event_handler = handler;
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_class_sof_register(app_usbd_class_inst_t const * p_cinst)
|
|
{
|
|
ASSERT(NULL != p_cinst);
|
|
ASSERT(NULL != p_cinst->p_class_methods);
|
|
ASSERT(NULL != p_cinst->p_class_methods->event_handler);
|
|
/** This function should be only called if USBD is disabled */
|
|
ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
|
|
|
|
/* Next SOF event requiring instance have to be null now */
|
|
ASSERT(NULL == (app_usbd_class_data_access(p_cinst)->p_sof_next));
|
|
|
|
/* Adding pointer to this instance to the end of the chain */
|
|
app_usbd_class_inst_t const * * pp_last = &m_p_first_sof_cinst;
|
|
while (NULL != (*pp_last))
|
|
{
|
|
|
|
ASSERT((*pp_last) != p_cinst);
|
|
pp_last = &(app_usbd_class_data_access(*pp_last)->p_sof_next);
|
|
}
|
|
(*pp_last) = p_cinst;
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
ret_code_t app_usbd_class_sof_unregister(app_usbd_class_inst_t const * p_cinst)
|
|
{
|
|
ASSERT(NULL != p_cinst);
|
|
/** This function should be only called if USBD is disabled */
|
|
ASSERT(!nrf_drv_usbd_is_enabled() && nrf_drv_usbd_is_initialized());
|
|
|
|
app_usbd_class_inst_t const * * pp_last = &m_p_first_sof_cinst;
|
|
while (NULL != (*pp_last))
|
|
{
|
|
if ((*pp_last) == p_cinst)
|
|
{
|
|
/* Breaking chain */
|
|
(*pp_last) = (app_usbd_class_data_access(p_cinst)->p_sof_next);
|
|
app_usbd_class_data_access(p_cinst)->p_sof_next = NULL;
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
pp_last = &(app_usbd_class_data_access(*pp_last)->p_sof_next);
|
|
}
|
|
return NRF_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
ret_code_t app_usbd_interface_std_req_handle(app_usbd_class_inst_t const * p_cinst,
|
|
app_usbd_setup_evt_t const * p_setup_ev)
|
|
{
|
|
switch (p_setup_ev->setup.bmRequest)
|
|
{
|
|
case APP_USBD_SETUP_STDREQ_GET_STATUS:
|
|
{
|
|
size_t tx_size;
|
|
uint16_t * p_tx_buff = app_usbd_core_setup_transfer_buff_get(&tx_size);
|
|
|
|
p_tx_buff[0] = 0;
|
|
return app_usbd_core_setup_rsp(&(p_setup_ev->setup), p_tx_buff, sizeof(uint16_t));
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
ret_code_t app_usbd_endpoint_std_req_handle(app_usbd_class_inst_t const * p_cinst,
|
|
app_usbd_setup_evt_t const * p_setup_ev)
|
|
{
|
|
size_t i;
|
|
nrf_drv_usbd_ep_t ep_addr = NRF_DRV_USBD_EPIN0;
|
|
for (i = 0; i < app_usbd_class_iface_count_get(p_cinst); ++i)
|
|
{
|
|
app_usbd_class_iface_conf_t const * p_iface_conf = app_usbd_class_iface_get(p_cinst, i);
|
|
const uint8_t ep_count = app_usbd_class_iface_ep_count_get(p_iface_conf);
|
|
|
|
ep_addr = (nrf_drv_usbd_ep_t)(p_setup_ev->setup.wIndex.lb);
|
|
uint8_t ep_idx = app_usbd_class_iface_ep_idx_get(p_iface_conf, ep_addr);
|
|
if (ep_idx == ep_count)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (i == app_usbd_class_iface_count_get(p_cinst))
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
switch (p_setup_ev->setup.bmRequest)
|
|
{
|
|
case APP_USBD_SETUP_STDREQ_GET_STATUS:
|
|
{
|
|
size_t tx_size;
|
|
uint16_t * p_tx_buff = app_usbd_core_setup_transfer_buff_get(&tx_size);
|
|
|
|
p_tx_buff[0] = nrf_drv_usbd_ep_stall_check(ep_addr) ? 1 : 0;
|
|
return app_usbd_core_setup_rsp(&(p_setup_ev->setup), p_tx_buff, sizeof(uint16_t));
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_SET_FEATURE:
|
|
{
|
|
if (p_setup_ev->setup.wValue.w != APP_USBD_SETUP_STDFEATURE_ENDPOINT_HALT)
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
nrf_drv_usbd_ep_stall(ep_addr);
|
|
return NRF_SUCCESS;
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_CLEAR_FEATURE:
|
|
{
|
|
if (p_setup_ev->setup.wValue.w != APP_USBD_SETUP_STDFEATURE_ENDPOINT_HALT)
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (nrf_usbd_dtoggle_get(ep_addr) != NRF_USBD_DTOGGLE_DATA0)
|
|
{
|
|
nrf_usbd_dtoggle_set(ep_addr, NRF_USBD_DTOGGLE_DATA0);
|
|
}
|
|
|
|
if (NRF_USBD_EPISO_CHECK(ep_addr) == 0)
|
|
{
|
|
nrf_drv_usbd_ep_stall_clear(ep_addr);
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
default:
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
ret_code_t app_usbd_req_std_set_interface(app_usbd_class_inst_t const * const p_cinst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
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 (p_iface->number == p_setup_ev->setup.wIndex.w)
|
|
{
|
|
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_dtoggle_get(p_ep->address) != NRF_USBD_DTOGGLE_DATA0)
|
|
{
|
|
nrf_usbd_dtoggle_set(p_ep->address, NRF_USBD_DTOGGLE_DATA0);
|
|
}
|
|
|
|
if (NRF_USBD_EPISO_CHECK(p_ep->address) == 0)
|
|
{
|
|
nrf_drv_usbd_ep_stall_clear(p_ep->address);
|
|
}
|
|
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ret_code_t app_usbd_iface_call(uint8_t iface, app_usbd_complex_evt_t const * const p_event)
|
|
{
|
|
ASSERT(NULL != m_p_first_cinst);
|
|
/*Iterate over classes*/
|
|
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)
|
|
{
|
|
return class_event_handler(p_inst, p_event);
|
|
}
|
|
}
|
|
p_inst = app_usbd_class_next_get(p_inst);
|
|
}
|
|
|
|
return NRF_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
ret_code_t app_usbd_ep_call(nrf_drv_usbd_ep_t ep, app_usbd_complex_evt_t const * const p_event)
|
|
{
|
|
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;
|
|
}
|
|
|
|
#endif // APP_USBD_ENABLED
|