mirror of
https://github.com/pengwon/epd42.git
synced 2026-03-19 09:29:49 +08:00
1181 lines
40 KiB
C
1181 lines
40 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 "app_usbd_core.h"
|
|
#include "app_usbd.h"
|
|
#include "app_usbd_request.h"
|
|
#include "app_usbd_string_desc.h"
|
|
#include "nrf.h"
|
|
#include "nrf_atomic.h"
|
|
#include "app_util_platform.h"
|
|
#include "app_usbd.h"
|
|
#include "app_usbd_class_base.h"
|
|
|
|
/* Test if VID was configured */
|
|
#ifndef APP_USBD_VID
|
|
#error APP_USBD_VID not properly defined.
|
|
#endif
|
|
|
|
/* USBD device version creating if defined in major/minor form */
|
|
#ifndef APP_USBD_DEVICE_VER
|
|
#if defined(APP_USBD_DEVICE_VER_MAJOR) && defined(APP_USBD_DEVICE_VER_MINOR)
|
|
#if ((APP_USBD_DEVICE_VER_MAJOR)) > 99 || ((APP_USBD_DEVICE_VER_MINOR) > 99)
|
|
#error Major and minor device version value have to be limited to 99.
|
|
#endif
|
|
#define APP_USBD_DEVICE_VER (((APP_USBD_DEVICE_VER_MAJOR)*100UL) + (APP_USBD_DEVICE_VER_MINOR))
|
|
#else
|
|
#error The definition of APP_USBD_DEVICE_VER or a pair APP_USBD_DEVICE_VER_MAJOR and APP_USBD_DEVICE_VER_MINOR required.
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* @internal
|
|
* @defgroup app_usbd_core_internals USB Device high level library core module internals
|
|
* @ingroup app_usbd_core
|
|
*
|
|
* Internal variables, auxiliary macros and functions of USBD high level core module.
|
|
* @{
|
|
*/
|
|
|
|
/** @brief Make USB BCD version */
|
|
#define APP_USBD_VERSION_BCD_MAKE(major, minor) (((0x##major) << 8) | (0x##minor))
|
|
|
|
/** @brief Make USB power value */
|
|
#define APP_USBD_POWER_MAKE(ma) (((ma) + 1) / 2)
|
|
|
|
/** @brief Convert device version to BCD notation */
|
|
#define APP_USBD_BCD_DEVICE ( \
|
|
((((APP_USBD_DEVICE_VER) % 10000) / 1000) * 0x1000) + \
|
|
((((APP_USBD_DEVICE_VER) % 1000) / 100) * 0x100) + \
|
|
((((APP_USBD_DEVICE_VER) % 100) / 10) * 0x10) + \
|
|
((((APP_USBD_DEVICE_VER) % 10) / 1) * 0x1) \
|
|
)
|
|
|
|
/**
|
|
@brief Default device descriptor initializer @ref app_usbd_descriptor_device_t
|
|
* */
|
|
#define APP_USBD_CORE_DEVICE_DESCRIPTOR { \
|
|
.bLength = sizeof(app_usbd_descriptor_device_t), /* descriptor size */ \
|
|
.bDescriptorType = APP_USBD_DESCRIPTOR_DEVICE, /* descriptor type */ \
|
|
.bcdUSB = APP_USBD_VERSION_BCD_MAKE(2,0), /* USB BCD version: 2.0 */ \
|
|
.bDeviceClass = 0, /* device class: 0 - specified by interface */ \
|
|
.bDeviceSubClass = 0, /* device subclass: 0 - specified by interface */ \
|
|
.bDeviceProtocol = 0, /* device protocol: 0 - specified by interface */ \
|
|
.bMaxPacketSize0 = NRF_DRV_USBD_EPSIZE, /* endpoint size: fixed to: NRF_DRV_USBD_EPSIZE*/ \
|
|
.idVendor = APP_USBD_VID, /* Vendor ID*/ \
|
|
.idProduct = APP_USBD_PID, /* Product ID*/ \
|
|
.bcdDevice = APP_USBD_BCD_DEVICE, /* Device version BCD */ \
|
|
.iManufacturer = APP_USBD_STRING_ID_MANUFACTURER, /* String ID: manufacturer */ \
|
|
.iProduct = APP_USBD_STRING_ID_PRODUCT, /* String ID: product */ \
|
|
.iSerialNumber = APP_USBD_STRING_ID_SERIAL, /* String ID: serial */ \
|
|
.bNumConfigurations = 1, /* Fixed value: only one configuration supported*/ \
|
|
}
|
|
|
|
|
|
#define APP_USBD_CORE_CONFIGURATION_DESCRIPTOR { \
|
|
.bLength = sizeof(app_usbd_descriptor_configuration_t), \
|
|
.bDescriptorType = APP_USBD_DESCRIPTOR_CONFIGURATION, \
|
|
.wTotalLength = 0, /*Calculated dynamically*/ \
|
|
.bNumInterfaces = 0, /*Calculated dynamically*/ \
|
|
.bConfigurationValue = 1, /*Value passed to set configuration*/ \
|
|
.iConfiguration = 0, /*Configuration ID: fixed to 0*/ \
|
|
.bmAttributes = APP_USBD_DESCRIPTOR_CONFIGURATION_ATTRIBUTE_ALWAYS_SET_MASK | \
|
|
APP_USBD_DESCRIPTOR_CONFIGURATION_ATTRIBUTE_SELF_POWERED_MASK, \
|
|
.bMaxPower = APP_USBD_POWER_MAKE(500), \
|
|
}
|
|
|
|
/**
|
|
* @brief USB descriptors
|
|
*/
|
|
typedef struct {
|
|
app_usbd_descriptor_device_t device;
|
|
app_usbd_descriptor_configuration_t * const configuration;
|
|
} app_usbd_core_descriptors_t;
|
|
|
|
/**
|
|
* @brief Configuration descriptor instance.
|
|
*
|
|
* Not const: some values must be calculated dynamically
|
|
*/
|
|
static app_usbd_descriptor_configuration_t m_configuration_dsc =
|
|
APP_USBD_CORE_CONFIGURATION_DESCRIPTOR;
|
|
|
|
/**
|
|
* @brief Core descriptors
|
|
*
|
|
* Descriptors: device, configuration, strings
|
|
*
|
|
*/
|
|
static const app_usbd_core_descriptors_t m_descriptors = {
|
|
.device = APP_USBD_CORE_DEVICE_DESCRIPTOR,
|
|
.configuration = &m_configuration_dsc,
|
|
};
|
|
|
|
|
|
/* Required early declaration of event handler function */
|
|
static ret_code_t app_usbd_core_event_handler(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_complex_evt_t const * const p_event);
|
|
|
|
/**
|
|
* @brief Current USB device state
|
|
*
|
|
* This variable is updated automatically by core library.
|
|
*/
|
|
static app_usbd_state_t app_usbd_state = APP_USBD_STATE_Disabled;
|
|
|
|
/**
|
|
* @brief Active device features
|
|
*
|
|
* @note Only @ref APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP is supported for device
|
|
*/
|
|
static uint8_t m_device_features_state;
|
|
|
|
/**
|
|
* @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_counter;
|
|
|
|
/**
|
|
* @brief Remote wake-up pending flag
|
|
*/
|
|
static nrf_atomic_flag_t m_rwu_pending;
|
|
|
|
|
|
/**
|
|
* @brief Core class methods
|
|
*
|
|
* Base methods interface for core class.
|
|
* This is quite specific class - it would be only connected into endpoint 0.
|
|
* Not connected into instances list.
|
|
*/
|
|
static const app_usbd_class_methods_t m_core_methods = {
|
|
.event_handler = app_usbd_core_event_handler,
|
|
.get_descriptors = NULL,
|
|
};
|
|
|
|
/**
|
|
* @brief Setup transfer buffer
|
|
*/
|
|
static uint8_t m_setup_transfer_buff[NRF_DRV_USBD_EPSIZE];
|
|
|
|
|
|
/**
|
|
* @brief Handler for outgoing setup data
|
|
*
|
|
* @todo RK documentation
|
|
*/
|
|
static app_usbd_core_setup_data_handler_desc_t m_ep0_handler_desc;
|
|
|
|
#define APP_USBD_CORE_CLASS_INSTANCE_CONFIG ()
|
|
|
|
|
|
/*lint -u -save -e26 -e40 -e64 -e123 -e505 -e651*/
|
|
|
|
/**
|
|
* @brief Core instance
|
|
*
|
|
* Create instance that would be connected into endpoints in USBD library.
|
|
*/
|
|
APP_USBD_CLASS_INST_GLOBAL_DEF(
|
|
app_usbd_core_inst,
|
|
app_usbd_core,
|
|
&m_core_methods,
|
|
APP_USBD_CORE_CLASS_CONFIGURATION,
|
|
() );
|
|
/*lint -restore*/
|
|
|
|
|
|
|
|
/**
|
|
* @brief Check current USBD power connection status
|
|
*
|
|
*/
|
|
static inline bool usbd_core_power_is_detected(void)
|
|
{
|
|
return 0 != ( (NRF_POWER->USBREGSTATUS) & POWER_USBREGSTATUS_VBUSDETECT_Msk);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Safely call EP0 handler
|
|
*
|
|
* Function calls EP0 handler only if its pointer is non-zero.
|
|
*
|
|
* @param status Status to send as a handler parameter.
|
|
*/
|
|
static inline ret_code_t usbd_core_ep0_handler_call_and_clear(nrf_drv_usbd_ep_status_t status)
|
|
{
|
|
app_usbd_core_setup_data_handler_t handler = m_ep0_handler_desc.handler;
|
|
if (NULL != handler)
|
|
{
|
|
m_ep0_handler_desc.handler = NULL;
|
|
return handler(status, m_ep0_handler_desc.p_context);
|
|
}
|
|
|
|
return NRF_ERROR_NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if EP0 handler is configured
|
|
*
|
|
* EP0 handler is configured is any instance that has processed SETUP command
|
|
* expects some incoming / outgoing data.
|
|
*
|
|
* EP0 handler should be cleared automatically just before it is called
|
|
* (see @ref usbd_core_ep0_handler_call_and_clear).
|
|
* If instance requires more data - it has to setup EP0 handler once more time
|
|
* (see @ref app_usbd_core_setup_data_handler_set).
|
|
*
|
|
* This function adds small layer of abstraction for checking if EP0 handler
|
|
* is already configured.
|
|
*
|
|
* @retval true EP0 handler is set
|
|
* @retval false EP0 handler is cleared
|
|
*/
|
|
static inline bool usb_core_ep0_handler_check(void)
|
|
{
|
|
return (NULL != m_ep0_handler_desc.handler);
|
|
}
|
|
|
|
/**
|
|
* @brief Empty data handler
|
|
*
|
|
* Data handler used only to mark that there is requested data during SETUP.
|
|
*
|
|
* @return Always NRF_SUCCESS
|
|
* @sa setup_empty_data_handler_desc
|
|
*/
|
|
static ret_code_t setup_data_handler_empty(nrf_drv_usbd_ep_status_t status, void * p_contex)
|
|
{
|
|
UNUSED_PARAMETER(status);
|
|
UNUSED_PARAMETER(p_contex);
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @todo RK Documentation
|
|
*/
|
|
static const app_usbd_core_setup_data_handler_desc_t m_setup_data_handler_empty_desc =
|
|
{
|
|
.handler = setup_data_handler_empty,
|
|
.p_context = NULL
|
|
};
|
|
|
|
/**
|
|
* @brief Special feeder to generate ZLP
|
|
*
|
|
* This feeder includes ZLP on the end of the transfer if required.
|
|
* The context should be a pointer to the boolean value: true if it ZLP is required,
|
|
* false if not.
|
|
*/
|
|
static bool app_usbd_core_zlp_feeder(nrf_drv_usbd_transfer_t * p_next, void * p_context)
|
|
{
|
|
bool * p_generate = p_context;
|
|
if(*p_generate)
|
|
{
|
|
p_next->p_data.tx = NULL;
|
|
p_next->size = 0;
|
|
*p_generate = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @todo RK Documentation
|
|
*
|
|
* @note This variable is used only for ZLP generation in SETUP response
|
|
*/
|
|
static bool m_setup_zlp_required;
|
|
|
|
/**
|
|
* @todo RK Documentation
|
|
*
|
|
* @note This variable is used only for ZLP generation in SETUP response
|
|
*/
|
|
static const nrf_drv_usbd_transfer_handler_desc_t m_setup_zlp_desc =
|
|
{
|
|
.handler = app_usbd_core_zlp_feeder,
|
|
.p_context = &m_setup_zlp_required
|
|
};
|
|
|
|
/**
|
|
* @brief Structure used as a context for descriptor feeder
|
|
*
|
|
* Structure with all the data required to process instances to generate descriptor
|
|
* data chunk.
|
|
*/
|
|
typedef struct
|
|
{
|
|
app_usbd_class_inst_t const * p_cinst; //!< The class instance that is currently processed
|
|
const uint8_t * p_desc; //!< Pointer at current descriptor or NULL if finished.
|
|
/**<
|
|
* If we get NULL on transfer function enter it means that ZLP is required.
|
|
* Or it is time to finish the transfer (depending on @c total_left).
|
|
*/
|
|
size_t desc_left; //!< Number of bytes left in the current class descriptor to send
|
|
size_t total_left; //!< Number of bytes left that was requested by the host
|
|
uint8_t * p_trans_buff; //!< Currently used transfer buffer
|
|
/**<
|
|
* This member is used only when function is called first time,
|
|
* from function that prepares first transfer.
|
|
* If the buffer was partially filled the @c max_size member would contain
|
|
* number of bytes left to fill.
|
|
*
|
|
* This member is automatically cleared inside @ref usbd_descriptor_feeder.
|
|
*/
|
|
size_t max_size; //!< Size in the buffer left.
|
|
/**
|
|
* Valid only if @c p_trans_buff is not NULL.
|
|
*/
|
|
} app_usbd_core_descriptor_conf_feed_data_t;
|
|
|
|
/**
|
|
* @brief Default data used by the feeder
|
|
*
|
|
*
|
|
*/
|
|
static app_usbd_core_descriptor_conf_feed_data_t m_descriptor_conf_feed_data;
|
|
|
|
/**
|
|
* @brief Descriptor feeder
|
|
*
|
|
* Descriptor feeder is used as an callback function when descriptors are
|
|
* transfered and buffer is ready for next data.
|
|
* It prepares next chunk of data to be sent.
|
|
*
|
|
* @param p_next See @ref nrf_drv_usbd_next_transfer_handler_t documentation.
|
|
* @param p_context Pointer to @ref app_usbd_core_descriptor_feed_data_t data type.
|
|
*
|
|
* @return See @ref nrf_drv_usbd_next_transfer_handler_t documentation.
|
|
*/
|
|
static bool usbd_descriptor_conf_feeder(nrf_drv_usbd_transfer_t * p_next, void * p_context)
|
|
{
|
|
app_usbd_core_descriptor_conf_feed_data_t * p_data = p_context;
|
|
if (NULL == p_data->p_cinst)
|
|
{
|
|
if (0 != p_data->total_left)
|
|
{
|
|
p_data->total_left = 0;
|
|
/* ZLP request */
|
|
p_next->p_data.tx = NULL;
|
|
p_next->size = 0;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t max_size; /* Buffer size */
|
|
uint8_t * p_trans_buff;
|
|
size_t size = 0; /* Currently added number of bytes */
|
|
size_t tx_size; /* Number of bytes aviable for current transfer */
|
|
|
|
if (NULL == p_data->p_trans_buff)
|
|
{
|
|
p_trans_buff = app_usbd_core_setup_transfer_buff_get(&max_size);
|
|
}
|
|
else
|
|
{
|
|
p_trans_buff = p_data->p_trans_buff;
|
|
max_size = p_data->max_size;
|
|
p_data->p_trans_buff = NULL;
|
|
}
|
|
|
|
tx_size = MIN(max_size, p_data->total_left);
|
|
while (0 != tx_size)
|
|
{
|
|
/* Process transfer */
|
|
if (0 < p_data->desc_left)
|
|
{
|
|
size_t to_copy = MIN(tx_size, p_data->desc_left);
|
|
memcpy(p_trans_buff+size, p_data->p_desc, to_copy);
|
|
p_data->desc_left -= to_copy;
|
|
p_data->total_left -= to_copy;
|
|
tx_size -= to_copy;
|
|
size += to_copy;
|
|
p_data->p_desc += to_copy;
|
|
}
|
|
if (0 == p_data->desc_left)
|
|
{
|
|
/* Get next descriptor */
|
|
p_data->p_cinst = app_usbd_class_next_get(p_data->p_cinst);
|
|
if (NULL == p_data->p_cinst)
|
|
{
|
|
/* No more data - check if ZLP is required */
|
|
if (size > 0)
|
|
{
|
|
if (0 != (size % nrf_drv_usbd_ep_max_packet_size_get(NRF_DRV_USBD_EPIN0)))
|
|
{
|
|
/* No ZLP required */
|
|
p_data->total_left = 0;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Prepare next descriptor */
|
|
p_data->p_desc =
|
|
p_data->p_cinst->p_class_methods->get_descriptors(p_data->p_cinst,
|
|
&p_data->desc_left);
|
|
}
|
|
}
|
|
}
|
|
p_next->p_data.tx = p_trans_buff;
|
|
p_next->size = size;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @todo RK Documentation
|
|
*/
|
|
static const nrf_drv_usbd_transfer_handler_desc_t usbd_descriptor_feeder_desc =
|
|
{
|
|
.handler = usbd_descriptor_conf_feeder,
|
|
.p_context = &m_descriptor_conf_feed_data
|
|
};
|
|
|
|
static ret_code_t setup_req_get_status(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
size_t max_size;
|
|
uint8_t * p_trans_buff = app_usbd_core_setup_transfer_buff_get(&max_size);
|
|
ASSERT(sizeof(uint16_t) <= max_size);
|
|
|
|
memset(p_trans_buff, 0, sizeof(uint16_t));
|
|
if (m_descriptors.configuration->bmAttributes &
|
|
APP_USBD_DESCRIPTOR_CONFIGURATION_ATTRIBUTE_SELF_POWERED_MASK)
|
|
{
|
|
SET_BIT(p_trans_buff[0], 0);
|
|
}
|
|
if (IS_SET(m_device_features_state, APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP))
|
|
{
|
|
SET_BIT(p_trans_buff[0], 1);
|
|
}
|
|
return app_usbd_core_setup_rsp(&(p_setup_ev->setup), p_trans_buff, sizeof(uint16_t));
|
|
}
|
|
|
|
static ret_code_t setup_req_get_descriptor(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
switch (p_setup_ev->setup.wValue.hb)
|
|
{
|
|
case APP_USBD_DESCRIPTOR_DEVICE:
|
|
{
|
|
return app_usbd_core_setup_rsp(&(p_setup_ev->setup),
|
|
&m_descriptors.device,
|
|
sizeof(m_descriptors.device));
|
|
}
|
|
case APP_USBD_DESCRIPTOR_CONFIGURATION:
|
|
{
|
|
/* The size equals the size of configuration descriptor and all classes descriptors */
|
|
size_t size = sizeof(app_usbd_descriptor_configuration_t);
|
|
uint8_t iface_count = 0;
|
|
|
|
/* Iterate over all registered classes count descriptors and total size */
|
|
app_usbd_class_inst_t const * p_class;
|
|
for (p_class = app_usbd_class_first_get(); p_class != NULL;
|
|
p_class = app_usbd_class_next_get(p_class))
|
|
{
|
|
ASSERT(NULL != (p_class->p_class_methods));
|
|
ASSERT(NULL != (p_class->p_class_methods->get_descriptors));
|
|
size_t dsc_size;
|
|
const void * dsc = p_class->p_class_methods->get_descriptors(p_class, &dsc_size);
|
|
UNUSED_VARIABLE(dsc);
|
|
size += dsc_size;
|
|
iface_count += app_usbd_class_iface_count_get(p_class);
|
|
}
|
|
|
|
m_descriptors.configuration->bNumInterfaces = iface_count;
|
|
m_descriptors.configuration->wTotalLength = size;
|
|
|
|
if (m_rwu_counter)
|
|
{
|
|
m_descriptors.configuration->bmAttributes |=
|
|
APP_USBD_DESCRIPTOR_CONFIGURATION_ATTRIBUTE_REMOTE_WAKEUP_MASK;
|
|
}
|
|
|
|
/* Access transmission buffer */
|
|
size_t max_size;
|
|
uint8_t * p_trans_buff = app_usbd_core_setup_transfer_buff_get(&max_size);
|
|
/* Copy updated configuration descriptor */
|
|
ASSERT(sizeof(m_descriptors.configuration) <= max_size);
|
|
memcpy(p_trans_buff,
|
|
m_descriptors.configuration,
|
|
sizeof(app_usbd_descriptor_configuration_t));
|
|
|
|
/* Fill the structure that allows iteration through all classes */
|
|
memset(&m_descriptor_conf_feed_data, 0, sizeof(m_descriptor_conf_feed_data));
|
|
|
|
nrf_drv_usbd_transfer_t first_tx;
|
|
size_t tx_size = MIN(size, p_setup_ev->setup.wLength.w);
|
|
if (tx_size > sizeof(app_usbd_descriptor_configuration_t))
|
|
{
|
|
p_class = app_usbd_class_first_get();
|
|
m_descriptor_conf_feed_data.p_cinst = p_class;
|
|
|
|
ASSERT(NULL != (p_class->p_class_methods));
|
|
ASSERT(NULL != (p_class->p_class_methods->get_descriptors));
|
|
|
|
m_descriptor_conf_feed_data.p_desc =
|
|
p_class->p_class_methods->get_descriptors(
|
|
p_class,
|
|
&(m_descriptor_conf_feed_data.desc_left));
|
|
|
|
size_t dsc_size = sizeof(app_usbd_descriptor_configuration_t);
|
|
|
|
m_descriptor_conf_feed_data.total_left = tx_size - dsc_size;
|
|
m_descriptor_conf_feed_data.p_trans_buff = p_trans_buff + dsc_size;
|
|
m_descriptor_conf_feed_data.max_size = max_size - dsc_size;
|
|
|
|
bool tx_ready = usbd_descriptor_conf_feeder(&first_tx,
|
|
&m_descriptor_conf_feed_data);
|
|
ASSERT(tx_ready);
|
|
UNUSED_VARIABLE(tx_ready);
|
|
|
|
first_tx.p_data.tx = p_trans_buff;
|
|
first_tx.size += sizeof(app_usbd_descriptor_configuration_t);
|
|
ASSERT(first_tx.size <= max_size);
|
|
}
|
|
else
|
|
{
|
|
m_descriptor_conf_feed_data.p_cinst = NULL;
|
|
m_descriptor_conf_feed_data.total_left = 0;
|
|
|
|
first_tx.p_data.tx = p_trans_buff;
|
|
first_tx.size = tx_size;
|
|
}
|
|
|
|
/* Start first transfer */
|
|
ret_code_t ret;
|
|
CRITICAL_REGION_ENTER();
|
|
ret = app_usbd_core_setup_data_transfer(NRF_DRV_USBD_EPIN0,
|
|
&first_tx,
|
|
&usbd_descriptor_feeder_desc);
|
|
|
|
if (NRF_SUCCESS == ret)
|
|
{
|
|
ret = app_usbd_core_setup_data_handler_set(NRF_DRV_USBD_EPIN0,
|
|
&m_setup_data_handler_empty_desc);
|
|
}
|
|
CRITICAL_REGION_EXIT();
|
|
|
|
return ret;
|
|
}
|
|
case APP_USBD_DESCRIPTOR_STRING:
|
|
{
|
|
app_usbd_string_desc_idx_t id =
|
|
(app_usbd_string_desc_idx_t)(p_setup_ev->setup.wValue.lb);
|
|
uint16_t langid = p_setup_ev->setup.wIndex.w;
|
|
uint16_t const * p_string_dsc = app_usbd_string_desc_get(id, langid);
|
|
if (p_string_dsc == NULL)
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return app_usbd_core_setup_rsp(&p_setup_ev->setup,
|
|
p_string_dsc,
|
|
app_usbd_string_desc_length(p_string_dsc));
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
static ret_code_t setup_req_get_configuration(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
size_t max_size;
|
|
uint8_t * p_trans_buff = app_usbd_core_setup_transfer_buff_get(&max_size);
|
|
if (APP_USB_STATE_BASE(app_usbd_state) == APP_USBD_STATE_Configured)
|
|
{
|
|
p_trans_buff[0] = 1;
|
|
}
|
|
else if (APP_USB_STATE_BASE(app_usbd_state) == APP_USBD_STATE_Addressed)
|
|
{
|
|
p_trans_buff[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return app_usbd_core_setup_rsp(&p_setup_ev->setup, p_trans_buff, sizeof(p_trans_buff[0]));
|
|
}
|
|
|
|
/**
|
|
* @brief Internal SETUP standard IN request handler
|
|
* @param[in] p_inst Instance of the class
|
|
* @param[in] p_setup_ev Setup request
|
|
* @return Standard error code
|
|
* @retval NRF_SUCCESS if request handled correctly
|
|
* @retval NRF_ERROR_NOT_SUPPORTED if request is not supported
|
|
*/
|
|
static ret_code_t setup_req_std_in(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
switch (p_setup_ev->setup.bmRequest)
|
|
{
|
|
case APP_USBD_SETUP_STDREQ_GET_STATUS:
|
|
{
|
|
return setup_req_get_status(p_inst, p_setup_ev);
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_GET_DESCRIPTOR:
|
|
{
|
|
return setup_req_get_descriptor(p_inst, p_setup_ev);
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_GET_CONFIGURATION:
|
|
{
|
|
return setup_req_get_configuration(p_inst, p_setup_ev);
|
|
}
|
|
default:
|
|
/*Not supported*/
|
|
break;
|
|
}
|
|
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
static ret_code_t setup_req_std_set_configuration(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
if (!((app_usbd_state == APP_USBD_STATE_Configured) ||
|
|
(app_usbd_state == APP_USBD_STATE_Addressed)))
|
|
{
|
|
return NRF_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
app_usbd_state_t new_state = app_usbd_state;
|
|
|
|
if (p_setup_ev->setup.wValue.lb == 0)
|
|
{
|
|
new_state = APP_USBD_STATE_Addressed;
|
|
}
|
|
else if (p_setup_ev->setup.wValue.lb == 1)
|
|
{
|
|
new_state = APP_USBD_STATE_Configured;
|
|
/*Clear all bulk/interrupt endpoint status and set toggle to DATA0*/
|
|
|
|
app_usbd_class_inst_t const * p_inst_it = app_usbd_class_first_get();
|
|
while (p_inst_it != NULL)
|
|
{
|
|
uint8_t iface_count = app_usbd_class_iface_count_get(p_inst_it);
|
|
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_it, i);
|
|
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);
|
|
}
|
|
|
|
}
|
|
}
|
|
p_inst_it = p_inst_it->p_data->p_next;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/*In this driver only one configuration is supported.*/
|
|
return NRF_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
|
|
|
|
if (app_usbd_state != new_state)
|
|
{
|
|
app_usbd_state = new_state;
|
|
/** @todo RK A way to notify class that state was changed */
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Internal SETUP standard OUT request handler
|
|
* @param[in] p_inst Instance of the class
|
|
* @param[in] p_setup_ev Setup request
|
|
* @return Standard error code
|
|
* @retval NRF_SUCCESS if request handled correctly
|
|
* @retval NRF_ERROR_NOT_SUPPORTED if request is not supported
|
|
*/
|
|
static ret_code_t setup_req_std_out(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
switch (p_setup_ev->setup.bmRequest)
|
|
{
|
|
case APP_USBD_SETUP_STDREQ_SET_ADDRESS:
|
|
{
|
|
if ((app_usbd_state != APP_USBD_STATE_Default) &&
|
|
(app_usbd_state != APP_USBD_STATE_Addressed) &&
|
|
(app_usbd_state != APP_USBD_STATE_Configured))
|
|
{
|
|
return NRF_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
app_usbd_state = APP_USBD_STATE_Addressed;
|
|
return NRF_SUCCESS;
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_SET_FEATURE:
|
|
{
|
|
if (p_setup_ev->setup.wValue.w != APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP)
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
SET_BIT(m_device_features_state, APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP);
|
|
return NRF_SUCCESS;
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_CLEAR_FEATURE:
|
|
{
|
|
if (p_setup_ev->setup.wValue.w != APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP)
|
|
{
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
CLR_BIT(m_device_features_state, APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP);
|
|
return NRF_SUCCESS;
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_SET_CONFIGURATION:
|
|
{
|
|
return setup_req_std_set_configuration(p_inst, p_setup_ev);
|
|
}
|
|
case APP_USBD_SETUP_STDREQ_SET_DESCRIPTOR:
|
|
{
|
|
/*Not supported yet.*/
|
|
break;
|
|
}
|
|
default:
|
|
/*Not supported*/
|
|
break;
|
|
}
|
|
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Internal SETUP event handler
|
|
* @param[in] p_inst Instance of the class
|
|
* @param[in] p_setup_ev Setup request
|
|
* @return Standard error code
|
|
* @retval NRF_SUCCESS if request handled correctly
|
|
* @retval NRF_ERROR_NOT_SUPPORTED if request is not supported
|
|
*/
|
|
static ret_code_t setup_device_event_handler(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_setup_evt_t const * const p_setup_ev)
|
|
{
|
|
ASSERT(p_inst != NULL);
|
|
ASSERT(p_setup_ev != NULL);
|
|
|
|
if (app_usbd_setup_req_dir(p_setup_ev->setup.bmRequestType) == APP_USBD_SETUP_REQDIR_IN)
|
|
{
|
|
switch (app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType))
|
|
{
|
|
case APP_USBD_SETUP_REQTYPE_STD:
|
|
return setup_req_std_in(p_inst, p_setup_ev);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else /*APP_USBD_SETUP_REQDIR_OUT*/
|
|
{
|
|
switch (app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType))
|
|
{
|
|
case APP_USBD_SETUP_REQTYPE_STD:
|
|
return setup_req_std_out(p_inst, p_setup_ev);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NRF_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Process SETUP command
|
|
*
|
|
* Auxiliary function for SETUP command processing
|
|
*/
|
|
static inline ret_code_t app_usbd_core_setup_req_handler(app_usbd_class_inst_t const * const p_inst,
|
|
nrf_drv_usbd_evt_t const * const p_event)
|
|
{
|
|
app_usbd_setup_evt_t setup_ev;
|
|
ret_code_t ret = NRF_ERROR_INTERNAL; /* Final result of request processing function */
|
|
|
|
/* This handler have to be cleared when SETUP is entered */
|
|
// ASSERT(!usb_core_ep0_handler_check());
|
|
|
|
setup_ev.type = APP_USBD_EVT_DRV_SETUP;
|
|
nrf_drv_usbd_setup_get((nrf_drv_usbd_setup_t *)&(setup_ev.setup));
|
|
|
|
switch (app_usbd_setup_req_rec(setup_ev.setup.bmRequestType))
|
|
{
|
|
case APP_USBD_SETUP_REQREC_DEVICE:
|
|
{
|
|
/* Endpoint 0 has core instance (that process device requests) connected */
|
|
ret = setup_device_event_handler(p_inst, &setup_ev);
|
|
break;
|
|
}
|
|
case APP_USBD_SETUP_REQREC_INTERFACE:
|
|
{
|
|
ret = app_usbd_iface_call(
|
|
setup_ev.setup.wIndex.lb,
|
|
(app_usbd_complex_evt_t const *)&setup_ev);
|
|
break;
|
|
}
|
|
case APP_USBD_SETUP_REQREC_ENDPOINT:
|
|
{
|
|
nrf_drv_usbd_ep_t ep = (nrf_drv_usbd_ep_t)setup_ev.setup.wIndex.w;
|
|
if (ep == NRF_DRV_USBD_EPOUT0)
|
|
{
|
|
app_usbd_class_inst_t const * const p_core_inst = app_usbd_core_instance_access();
|
|
ret = app_usbd_endpoint_std_req_handle(p_core_inst, &setup_ev);
|
|
break;
|
|
}
|
|
|
|
ret = app_usbd_ep_call(
|
|
(nrf_drv_usbd_ep_t)(setup_ev.setup.wIndex.lb),
|
|
(app_usbd_complex_evt_t const *)&setup_ev);
|
|
break;
|
|
}
|
|
case APP_USBD_SETUP_REQREC_OTHER:
|
|
{
|
|
/* Try to process via every instance */
|
|
ret = app_usbd_all_until_served_call((app_usbd_complex_evt_t *)&setup_ev);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Processing result */
|
|
if (ret == NRF_SUCCESS)
|
|
{
|
|
if (usb_core_ep0_handler_check())
|
|
{
|
|
/* Request processed successfully and requires SETUP data */
|
|
nrf_drv_usbd_setup_data_clear();
|
|
}
|
|
else
|
|
{
|
|
/* Request processed successfully */
|
|
nrf_drv_usbd_setup_clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Request finished with error */
|
|
nrf_drv_usbd_setup_stall();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Event handler for core module
|
|
*
|
|
* The event handler that would process all events directed to device.
|
|
*
|
|
*/
|
|
static ret_code_t app_usbd_core_event_handler(app_usbd_class_inst_t const * const p_inst,
|
|
app_usbd_complex_evt_t const * const p_event)
|
|
{
|
|
ret_code_t ret = NRF_ERROR_NOT_SUPPORTED;
|
|
switch(p_event->type)
|
|
{
|
|
case APP_USBD_EVT_DRV_RESET:
|
|
{
|
|
app_usbd_state = APP_USBD_STATE_Default;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_DRV_SUSPEND:
|
|
{
|
|
ASSERT(app_usbd_state >= APP_USBD_STATE_Unattached);
|
|
app_usbd_state |= APP_USBD_STATE_SuspendedMask;
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_DRV_RESUME:
|
|
{
|
|
if (nrf_atomic_flag_clear_fetch(&m_rwu_pending) != 0)
|
|
{
|
|
nrf_usbd_task_trigger(NRF_USBD_TASK_NODRIVEDPDM);
|
|
}
|
|
|
|
ASSERT(APP_USB_STATE_BASE(app_usbd_state) >= APP_USBD_STATE_Unattached);
|
|
app_usbd_state &= (app_usbd_state_t)(~APP_USBD_STATE_SuspendedMask);
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_DRV_SETUP:
|
|
{
|
|
ret = app_usbd_core_setup_req_handler(p_inst, &(p_event->drv_evt));
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_INST_APPEND:
|
|
{
|
|
ASSERT(app_usbd_state == APP_USBD_STATE_Disabled);
|
|
app_usbd_state = APP_USBD_STATE_Unattached;
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_INST_REMOVE:
|
|
{
|
|
ASSERT(app_usbd_state == APP_USBD_STATE_Unattached);
|
|
app_usbd_state = APP_USBD_STATE_Disabled;
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_START:
|
|
{
|
|
app_usbd_state = APP_USBD_STATE_Powered;
|
|
if (usbd_core_power_is_detected())
|
|
{
|
|
app_usbd_state = APP_USBD_STATE_Default;
|
|
}
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
case APP_USBD_EVT_STOP:
|
|
{
|
|
ASSERT(APP_USB_STATE_BASE(app_usbd_state) > APP_USBD_STATE_Powered);
|
|
if (app_usbd_state & APP_USBD_STATE_SuspendedMask)
|
|
{
|
|
app_usbd_state = APP_USBD_STATE_SuspendedPowered;
|
|
}
|
|
else
|
|
{
|
|
app_usbd_state = APP_USBD_STATE_Powered;
|
|
}
|
|
ret = NRF_SUCCESS;
|
|
break;
|
|
}
|
|
/* Data transfer on endpoint 0 */
|
|
case APP_USBD_EVT_DRV_EPTRANSFER:
|
|
{
|
|
/* This EPTRANSFER event have to be called only for EP0 */
|
|
ASSERT((p_event->drv_evt.data.eptransfer.ep == NRF_DRV_USBD_EPOUT0) ||
|
|
(p_event->drv_evt.data.eptransfer.ep == NRF_DRV_USBD_EPIN0));
|
|
ret = usbd_core_ep0_handler_call_and_clear(p_event->drv_evt.data.eptransfer.status);
|
|
/* Processing result */
|
|
if(ret == NRF_SUCCESS)
|
|
{
|
|
if(usb_core_ep0_handler_check())
|
|
{
|
|
/* Request processed successfully and requires SETUP data */
|
|
nrf_drv_usbd_setup_data_clear();
|
|
}
|
|
else
|
|
{
|
|
/* Request processed successfully */
|
|
nrf_drv_usbd_setup_clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Request finished with error */
|
|
nrf_drv_usbd_setup_stall();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret_code_t app_usbd_core_setup_rsp(app_usbd_setup_t const * p_setup,
|
|
void const * p_data,
|
|
size_t size)
|
|
{
|
|
size_t req_size = p_setup->wLength.w;
|
|
size_t tx_size = MIN(req_size, size);
|
|
bool zlp_required = (size < req_size) &&
|
|
(0 == (size % nrf_drv_usbd_ep_max_packet_size_get(NRF_DRV_USBD_EPIN0)));
|
|
|
|
m_setup_zlp_required = zlp_required;
|
|
|
|
NRF_DRV_USBD_TRANSFER_IN(transfer, p_data, tx_size);
|
|
|
|
ret_code_t ret;
|
|
CRITICAL_REGION_ENTER();
|
|
ret = app_usbd_core_setup_data_transfer(NRF_DRV_USBD_EPIN0,
|
|
&transfer,
|
|
&m_setup_zlp_desc);
|
|
if (NRF_SUCCESS == ret)
|
|
{
|
|
ret = app_usbd_core_setup_data_handler_set(NRF_DRV_USBD_EPIN0,
|
|
&m_setup_data_handler_empty_desc);
|
|
}
|
|
CRITICAL_REGION_EXIT();
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret_code_t app_usbd_core_setup_data_handler_set(
|
|
nrf_drv_usbd_ep_t ep,
|
|
app_usbd_core_setup_data_handler_desc_t const * const p_handler_desc)
|
|
{
|
|
if (nrf_drv_usbd_last_setup_dir_get() != ep)
|
|
{
|
|
return NRF_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
m_ep0_handler_desc = *p_handler_desc;
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
ret_code_t app_usbd_core_ep_transfer(
|
|
nrf_drv_usbd_ep_t ep,
|
|
nrf_drv_usbd_transfer_t const * const p_transfer,
|
|
nrf_drv_usbd_transfer_handler_desc_t const * const p_handler)
|
|
{
|
|
if (APP_USB_STATE_BASE(app_usbd_state) != APP_USBD_STATE_Configured)
|
|
{
|
|
return NRF_ERROR_INVALID_STATE;
|
|
}
|
|
return nrf_drv_usbd_ep_transfer(ep, p_transfer, p_handler);
|
|
}
|
|
|
|
ret_code_t app_usbd_core_setup_data_transfer(
|
|
nrf_drv_usbd_ep_t ep,
|
|
nrf_drv_usbd_transfer_t const * const p_transfer,
|
|
nrf_drv_usbd_transfer_handler_desc_t const * const p_handler)
|
|
{
|
|
ASSERT(0 == NRF_USBD_EP_NR_GET(ep));
|
|
|
|
if (!((APP_USB_STATE_BASE(app_usbd_state) == APP_USBD_STATE_Configured) ||
|
|
(APP_USB_STATE_BASE(app_usbd_state) == APP_USBD_STATE_Addressed ) ||
|
|
(APP_USB_STATE_BASE(app_usbd_state) == APP_USBD_STATE_Default ) ))
|
|
{
|
|
return NRF_ERROR_INVALID_STATE;
|
|
}
|
|
return nrf_drv_usbd_ep_transfer(ep, p_transfer, p_handler);
|
|
}
|
|
|
|
void * app_usbd_core_setup_transfer_buff_get(size_t * p_size)
|
|
{
|
|
if (p_size != NULL)
|
|
*p_size = sizeof(m_setup_transfer_buff);
|
|
|
|
return m_setup_transfer_buff;
|
|
}
|
|
|
|
app_usbd_state_t app_usbd_core_state_get(void)
|
|
{
|
|
return app_usbd_state;
|
|
}
|
|
|
|
void app_usbd_core_class_rwu_register(app_usbd_class_inst_t const * const p_inst)
|
|
{
|
|
ASSERT(p_inst != NULL);
|
|
++m_rwu_counter;
|
|
/*Overflow check*/
|
|
ASSERT(m_rwu_counter != 0);
|
|
}
|
|
|
|
void app_usbd_core_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_counter != 0);
|
|
--m_rwu_counter;
|
|
}
|
|
|
|
void app_usbd_core_class_rwu_pend(void)
|
|
{
|
|
if (m_rwu_counter == 0)
|
|
{
|
|
/* No class registered on remote wake-up */
|
|
return;
|
|
}
|
|
|
|
if (IS_SET(m_device_features_state, APP_USBD_SETUP_STDFEATURE_DEVICE_REMOTE_WAKEUP) == 0)
|
|
{
|
|
/* Feature remote wake-up hasn't been set by host*/
|
|
return;
|
|
}
|
|
|
|
if (nrf_atomic_flag_set_fetch(&m_rwu_pending) != 0)
|
|
{
|
|
/* Remote wake-up pending */
|
|
return;
|
|
}
|
|
|
|
nrf_usbd_dpdmvalue_set(NRF_USBD_DPDMVALUE_RESUME);
|
|
nrf_usbd_task_trigger(NRF_USBD_TASK_DRIVEDPDM);
|
|
}
|
|
|
|
/** @} */
|
|
#endif // APP_USBD_ENABLED
|