/** | |
* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, this | |
* list of conditions and the following disclaimer. | |
* | |
* 2. Redistributions in binary form, except as embedded into a Nordic | |
* Semiconductor ASA integrated circuit in a product or a software update for | |
* such product, must reproduce the above copyright notice, this list of | |
* conditions and the following disclaimer in the documentation and/or other | |
* materials provided with the distribution. | |
* | |
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its | |
* contributors may be used to endorse or promote products derived from this | |
* software without specific prior written permission. | |
* | |
* 4. This software, with or without modification, must only be used with a | |
* Nordic Semiconductor ASA integrated circuit. | |
* | |
* 5. Any software provided in binary form under this license must not be reverse | |
* engineered, decompiled, modified and/or disassembled. | |
* | |
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS | |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE | |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | |
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
*/ | |
#ifndef APP_USBD_CLASS_BASE_H__ | |
#define APP_USBD_CLASS_BASE_H__ | |
#include <stdint.h> | |
#include <stddef.h> | |
#include "app_usbd_types.h" | |
#include "nrf_drv_usbd.h" | |
#include "nrf_assert.h" | |
#include "app_util.h" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/** | |
* @defgroup app_usbd_class_base USBD Class Base module | |
* @ingroup app_usbd | |
* | |
* @brief @tagAPI52840 The base for any class instance is defined in this module. | |
* | |
* @details Any class instance must start from base class instance structure. | |
* This makes them compatible with USBD library independently of the | |
* implementation details. | |
* @{ | |
*/ | |
/** | |
* @brief Endpoint configuration. | |
*/ | |
typedef struct | |
{ | |
nrf_drv_usbd_ep_t address; //!< Endpoint address | |
} app_usbd_class_ep_conf_t; | |
/** | |
* @brief Interface configuration. | |
*/ | |
typedef struct | |
{ | |
uint8_t number; //!< Interface number | |
uint8_t ep_cnt; //!< Endpoint number | |
uint8_t ep_offset; //!< Offset of the first endpoint | |
/**< Offset in bytes of the first endpoint. | |
* The offset is calculated from the address of this interface structure | |
*/ | |
} app_usbd_class_iface_conf_t; | |
/** | |
* @brief Instance variable data. | |
*/ | |
typedef struct | |
{ | |
app_usbd_class_inst_t const * p_next; //!< Pointer to the next instance | |
app_usbd_class_inst_t const * p_sof_next; //!< Pointer to the next SOF event requiring instance | |
app_usbd_sof_interrupt_handler_t sof_handler; //!< Instance specific SOF interrupt handler | |
} app_usbd_class_data_t; | |
/** | |
* @brief Class descriptor context. | |
*/ | |
typedef struct | |
{ | |
uint32_t line; //!< Number of line to resume writing descriptors from | |
uint8_t data_buffer; //!< Data from last call of feeder | |
} app_usbd_class_descriptor_ctx_t; | |
/** | |
* @brief Class descriptor state. | |
*/ | |
typedef struct | |
{ | |
uint8_t * p_buffer; //!< Pointer to buffer | |
uint32_t current_size; //!< Current size of descriptor | |
uint32_t maximum_size; //!< Maximum size of descriptor | |
app_usbd_class_descriptor_ctx_t * p_context; //!< Pointer to context | |
} app_usbd_class_descriptor_state_t; | |
/** | |
* @brief Class interface function set. | |
* */ | |
typedef struct { | |
/** | |
* @brief Instance callback function. | |
* | |
* The function used by every class instance. | |
* @param[in,out] p_inst Instance of the class. | |
* @param[in] p_event Event to process. | |
* | |
* @return Standard error code. | |
* | |
* @note If given event is not supported by class, return @ref NRF_ERROR_NOT_SUPPORTED | |
*/ | |
ret_code_t (* event_handler)(app_usbd_class_inst_t const * const p_inst, | |
app_usbd_complex_evt_t const * const p_event); | |
/** | |
* @brief Instance feed descriptors. | |
* | |
* Feeds whole descriptor of the instance. | |
* @param[in] p_ctx Class descriptor context. | |
* @param[in,out] p_inst Instance of the class. | |
* @param[out] p_buff Buffer for descriptor. | |
* @param[in] max_size Requested size of the descriptor. | |
* | |
* @return True if not finished feeding the descriptor, false if done. | |
*/ | |
bool (* feed_descriptors)(app_usbd_class_descriptor_ctx_t * p_ctx, | |
app_usbd_class_inst_t const * p_inst, | |
uint8_t * p_buff, | |
size_t max_size); | |
/** | |
* @brief Select interface | |
* | |
* Function called when class interface has to be selected. | |
* | |
* This function would be called for every interface when: | |
* - SET_INTERFACE command is processed by the default handler | |
* - SET_CONFIG(1) command is processed by the default handler | |
* | |
* @note Remember to disable all the endpoints that are not used | |
* in the selected configuration. | |
* @note If this function pointer is NULL default procedure would | |
* just enable all the interface endpoints and selecting | |
* alternate configurations other than 0 would generate error. | |
* @note Calling the function with alternate setting 0 has to always succeed. | |
* | |
* @param[in,out] p_inst Instance of the class | |
* @param[in] iface_idx Index of the interface inside class structure | |
* @param[in] alternate Alternate setting that should be selected | |
* | |
* @return Function has to return @ref NRF_SUCCESS when it has successfully proceed | |
* interface selection. | |
* If it returns @ref NRF_ERROR_NOT_SUPPORTED, default function would be used | |
* to proceed the request - just like there would be NULL pointer in this field. | |
* Any other kind of error would make library to STALL the request. | |
*/ | |
ret_code_t (* iface_select)(app_usbd_class_inst_t const * const p_inst, | |
uint8_t iface_idx, | |
uint8_t alternate); | |
/** | |
* @brief Deselect interface. | |
* | |
* Function called when the class interface has to be deselected. | |
* | |
* This function would be called for every interface when: | |
* - Library start internal event is processed by the default handler | |
* - RESET event is processed by the default handler | |
* - SET_ADDRESS is processed by the default handler | |
* - SET_CONFIG(0) is processed by the default handler | |
* | |
* @note Just after this function is called all the interface | |
* endpoints would be disabled. | |
* This function does not has to take care about it. | |
* @note If this function pointer is NULL default procedure would | |
* just disable all the interface endpoints. | |
* | |
* @param[in,out] p_inst Instance of the class. | |
* @param[in] iface_idx Index of the interface inside class structure. | |
*/ | |
void (* iface_deselect)(app_usbd_class_inst_t const * const p_inst, uint8_t iface_idx); | |
/** | |
* @brief Get current interface. | |
* | |
* Function called when class interface has to return its alternate settings | |
* in reaction on GET_INTERFACE command. | |
* It should be defined in a pair with @ref app_usbd_class_methods_t::iface_select. | |
* | |
* @param[in] p_inst Instance of the class. | |
* @param[in] iface_idx Index of the interface inside class structure. | |
* | |
* @return Current alternate setting of the selected interface. | |
* | |
* @note For the classes that support this function, when an interface that has not alternate | |
* configurations has been selected this function has to return 0 - default alternate setting. | |
* | |
* @note If this function pointer it NULL default procedure would return alternate interface | |
* value 0. | |
*/ | |
uint8_t (* iface_selection_get)(app_usbd_class_inst_t const * const p_inst, uint8_t iface_idx); | |
} app_usbd_class_methods_t; | |
/** | |
* @brief The instance structure itself. | |
* | |
* The structure of base class instance. | |
*/ | |
struct app_usbd_class_inst_s | |
{ | |
app_usbd_class_data_t * p_data; //!< Pointer to non-constant data | |
app_usbd_class_methods_t const * p_class_methods; //!< Class interface methods | |
struct | |
{ | |
uint8_t cnt; //!< Number of defined interfaces | |
uint8_t config[]; //!< Interface configuration data followed by endpoint data | |
} iface; //!< Interface structure | |
}; | |
/** | |
* @brief Get total number of interfaces. | |
* | |
* | |
*/ | |
static inline uint8_t app_usbd_class_iface_count_get(app_usbd_class_inst_t const * const p_inst) | |
{ | |
return p_inst->iface.cnt; | |
} | |
/** | |
* @brief Interface accessing function. | |
* | |
* Get interface pointer. | |
* Interfaces create continuous array in the memory so it is possible to get | |
* interface with index 0 and then just iterate to the next one. | |
* | |
* @param p_inst Pointer to the class instance | |
* @param iface_idx Index of the instance to get. | |
* This is not the interface identifier. | |
* Technically it is the index of the interface in the class description array. | |
* @return Pointer to the interface configuration parameters or NULL if given index is out of interface scope for given class. | |
*/ | |
static inline app_usbd_class_iface_conf_t const * app_usbd_class_iface_get( | |
app_usbd_class_inst_t const * const p_inst, | |
uint8_t iface_idx) | |
{ | |
ASSERT(NULL != p_inst); | |
if (iface_idx >= (app_usbd_class_iface_count_get(p_inst))) | |
{ | |
return NULL; | |
} | |
app_usbd_class_iface_conf_t const * p_interface = | |
(app_usbd_class_iface_conf_t const * )(p_inst->iface.config); | |
return &(p_interface[iface_idx]); | |
} | |
/** | |
* @brief Get interface number. | |
* | |
* @param p_iface Pointer to interface structure. | |
* | |
* @return Interface number from interface configuration structure. | |
*/ | |
static inline uint8_t app_usbd_class_iface_number_get( | |
app_usbd_class_iface_conf_t const * const p_iface) | |
{ | |
return p_iface->number; | |
} | |
/** | |
* @brief Get number of endpoints in interface. | |
* | |
* @param p_iface Pointer to interface structure. | |
* | |
* @return Number of endpoints used by given interface. | |
*/ | |
static inline uint8_t app_usbd_class_iface_ep_count_get( | |
app_usbd_class_iface_conf_t const * const p_iface) | |
{ | |
return p_iface->ep_cnt; | |
} | |
/** | |
* @brief Interface Endpoint accessing function. | |
* | |
* @param p_iface Interface configuration pointer. | |
* @param ep_idx Endpoint index. | |
* | |
* @return Endpoint information structure pointer or NULL if given index is outside of endpoints for selected interface. | |
* | |
* @sa app_usbd_class_iface_get | |
*/ | |
static inline app_usbd_class_ep_conf_t const * app_usbd_class_iface_ep_get( | |
app_usbd_class_iface_conf_t const * const p_iface, | |
uint8_t ep_idx) | |
{ | |
ASSERT(NULL != p_iface); | |
if (ep_idx >= p_iface->ep_cnt) | |
{ | |
return NULL; | |
} | |
app_usbd_class_ep_conf_t const * p_ep = | |
(app_usbd_class_ep_conf_t const * )(((uint8_t const *)p_iface) + p_iface->ep_offset); | |
return &(p_ep[ep_idx]); | |
} | |
/** | |
* @brief Translate endpoint address to class index. | |
* | |
* @param p_iface Interface configuration pointer. | |
* @param ep_address Endpoint address. | |
* | |
* @return Endpoint index or number of endpoints if not found. | |
* | |
*/ | |
static inline uint8_t app_usbd_class_iface_ep_idx_get( | |
app_usbd_class_iface_conf_t const * const p_iface, | |
nrf_drv_usbd_ep_t ep_address) | |
{ | |
ASSERT(NULL != p_iface); | |
app_usbd_class_ep_conf_t const * p_ep = | |
(app_usbd_class_ep_conf_t const * )(((uint8_t const *)p_iface) + p_iface->ep_offset); | |
uint8_t i; | |
for (i = 0; i < p_iface->ep_cnt; ++i) | |
{ | |
if (ep_address == p_ep[i].address) | |
{ | |
break; | |
} | |
} | |
return i; | |
} | |
/** | |
* @brief Get the selected endpoint address. | |
* | |
* @param p_ep Endpoint configuration structure. | |
* | |
* @return Endpoint address | |
*/ | |
static inline nrf_drv_usbd_ep_t app_usbd_class_ep_address_get(app_usbd_class_ep_conf_t const * p_ep) | |
{ | |
return (nrf_drv_usbd_ep_t)p_ep->address; | |
} | |
/** | |
* @brief Get the pointer to the writable instance data. | |
* | |
* @param p_inst Instance pointer. | |
* @return Pointer to writable instance data. | |
*/ | |
static inline app_usbd_class_data_t * app_usbd_class_data_access( | |
app_usbd_class_inst_t const * const p_inst) | |
{ | |
return p_inst->p_data; | |
} | |
/** | |
* @name Internal macros for argument mapping | |
* | |
* Functions to be used as a mapping macro for @ref MACRO_MAP, @ref MACRO_MAP_FOR or @ref MACRO_MAP_FOR_PARAM | |
* @{ | |
*/ | |
/** | |
* @brief Count the number of endpoints in given configuration. | |
* | |
* Config should be given as a interface configuration in a brackets: | |
* @code | |
* (interface_nr, ep1, ep2, ep3) | |
* @endcode | |
* Number of endpoints may vary from 0 to a few (technically up to 16, but it seems not to make sense to use more than 4). | |
* Interface number is always present. | |
* | |
* @param iface_config Single interface configuration (in brackets). | |
* | |
* @return Number of endpoints in interface. This is computed value - can be used by compiler but not by preprocessor. | |
*/ | |
#define APP_USBD_CLASS_CONF_IFACE_EP_COUNT_(iface_config) \ | |
(NUM_VA_ARGS(BRACKET_EXTRACT(iface_config)) - 1) | |
/** | |
* @brief Adds the number of endpoints in given config to the current value. | |
* | |
* This is basically @ref APP_USBD_CLASS_CONF_IFACE_EP_COUNT_ with plus sign added. | |
* | |
* @param iface_config See parameters documentation in @ref APP_USBD_CLASS_CONF_IFACE_EP_COUNT_ | |
* | |
* @return Plus sign followed by number of endpoints in interface. | |
* | |
* @sa APP_USBD_CLASS_CONF_IFACE_EP_COUNT_ | |
*/ | |
#define APP_USBD_CLASS_CONF_IFACE_EP_PLUS_COUNT_(iface_config) \ | |
+ APP_USBD_CLASS_CONF_IFACE_EP_COUNT_(iface_config) | |
/** | |
* @brief Create variable for endpoint. | |
*/ | |
/** | |
* @brief Extract endpoints given interface configuration. | |
* | |
* This macro gets single endpoint configuration and extracts all the endpoints. | |
* It also adds comma on the end of extracted endpoints. | |
* This way when this macro is called few times it generates nice list of all endpoints | |
* that may be used to array initialization. | |
* | |
* @param iface_config Single interface configuration in brackets. | |
* The format should be similar like described in @ref APP_USBD_CLASS_CONF_IFACE_EP_COUNT_. | |
*/ | |
#define APP_USBD_CLASS_IFACE_EP_EXTRACT_(iface_config) \ | |
CONCAT_2(APP_USBD_CLASS_IFACE_EP_EXTRACT_, \ | |
NUM_VA_ARGS_IS_MORE_THAN_1(BRACKET_EXTRACT(iface_config))) \ | |
(BRACKET_EXTRACT(iface_config)) | |
/** | |
* @brief Auxiliary macro for @ref APP_USBD_CLASS_IFACE_EP_EXTRACT_ | |
* | |
* This macro is called when interface has no endpoints. | |
*/ | |
#define APP_USBD_CLASS_IFACE_EP_EXTRACT_0(iface_nr) | |
/** | |
* @brief Auxiliary macro for @ref APP_USBD_CLASS_IFACE_EP_EXTRACT_ | |
* | |
* This macro is called when interface has at least one endpoint. | |
*/ | |
#define APP_USBD_CLASS_IFACE_EP_EXTRACT_1(...) \ | |
APP_USBD_CLASS_IFACE_EP_EXTRACT_1_(__VA_ARGS__) | |
#define APP_USBD_CLASS_IFACE_EP_EXTRACT_1_(iface_nr, ...) \ | |
MACRO_MAP_REC(APP_USBD_CLASS_IFACE_EP_EXTRACT_1__, __VA_ARGS__) | |
#define APP_USBD_CLASS_IFACE_EP_EXTRACT_1__(ep) \ | |
{(nrf_drv_usbd_ep_t) (ep)}, | |
/** | |
* @brief Generate configuration for single interface. | |
* | |
* This macro extract configuration for single interface. | |
* The configuration is inside curly brackets and comma is added on the end. | |
* This mean it can be directly used to init array of interface configurations. | |
* | |
* @param iface_config Single interface configuration. | |
* @param N Currently processed configuration. | |
* @param iface_configs All interfaces configuration in brackets. | |
*/ | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_(iface_config, N, iface_configs) \ | |
CONCAT_2(APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_, \ | |
NUM_VA_ARGS_IS_MORE_THAN_1(BRACKET_EXTRACT(iface_config))) \ | |
(N, iface_configs, BRACKET_EXTRACT(iface_config)) | |
/** | |
* @brief Macro used when there was an error extracting number of configs. | |
* | |
* Throws a syntax error. | |
*/ | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_x(iface_config, N, iface_configs) \ | |
[N] = !!!iface_config!!! | |
/** | |
* @brief Auxiliary macro for @ref APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_ | |
* | |
* This macro is called when interface has no endpoints. | |
*/ | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_0(N, iface_configs, iface_nr) \ | |
APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_0_(N, iface_configs, iface_nr) | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_0_(N, iface_configs, iface_nr) \ | |
{ .number = iface_nr, .ep_cnt = 0, .ep_offset = 0 }, | |
/** | |
* @brief Auxiliary macro for @ref APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_ | |
* | |
* This macro is called when interface has at last one endpoint. | |
*/ | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_1(N, iface_configs, ...) \ | |
APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_1_(N, iface_configs, __VA_ARGS__) | |
#define APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_1_(N, iface_configs, iface_nr, ...) \ | |
{ .number = iface_nr, .ep_cnt = NUM_VA_ARGS(__VA_ARGS__), \ | |
.ep_offset = APP_USBD_CLASS_CONF_TOTAL_EP_COUNT_N(N, iface_configs) * \ | |
sizeof(app_usbd_class_ep_conf_t) \ | |
+ ((NUM_VA_ARGS(BRACKET_EXTRACT(iface_configs)) - N) * \ | |
sizeof(app_usbd_class_iface_conf_t)) \ | |
}, | |
/** @} */ | |
/** | |
* @name Macros that uses mapping macros internally | |
* | |
* Auxiliary macros that uses mapping macros to make some calculations or realize other functionality. | |
* Mapped here for easier unit testing and to hide complex mapping functions calling. | |
* @{ | |
*/ | |
/** | |
* @brief Count total number of endpoints. | |
* | |
* @param iface_configs List of interface configurations like explained | |
* in documentation for @ref APP_USBD_CLASS_INSTANCE_TYPEDEF | |
* | |
* @return The equation to calculate the number of endpoints by compiler. | |
*/ | |
#define APP_USBD_CLASS_CONF_TOTAL_EP_COUNT(iface_configs) \ | |
(0 MACRO_MAP(APP_USBD_CLASS_CONF_IFACE_EP_PLUS_COUNT_, BRACKET_EXTRACT(iface_configs))) | |
/** | |
* @brief Count total number of endpoint up-to interface index. | |
* | |
* The version of @ref APP_USBD_CLASS_CONF_TOTAL_EP_COUNT macro which takes the | |
* number of interfaces to analyze. | |
* | |
* @param N Number of interfaces to analyze. | |
* @param iface_configs List of interface configurations like explained | |
* in documentation for @ref APP_USBD_CLASS_INSTANCE_TYPEDEF | |
* | |
* @return The equation to calculate the number of endpoints by compiler. | |
*/ | |
#define APP_USBD_CLASS_CONF_TOTAL_EP_COUNT_N(N, iface_configs) \ | |
(0 MACRO_MAP_N(N, APP_USBD_CLASS_CONF_IFACE_EP_PLUS_COUNT_, BRACKET_EXTRACT(iface_configs))) | |
/** | |
* @brief Extract configurations for interfaces. | |
* | |
* This macro extracts the configurations for every interface. | |
* Basically uses the @ref APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_ macro on every | |
* configuration found. | |
* | |
* This should generate interface configuration initialization data | |
* in comma separated initializers in curly braces. | |
* | |
* @param iface_configs List of interface configurations like explained | |
* in documentation for @ref APP_USBD_CLASS_INSTANCE_TYPEDEF | |
* | |
* @return Comma separated initialization data for all interfaces. | |
*/ | |
/*lint -emacro( (40), APP_USBD_CLASS_IFACES_CONFIG_EXTRACT) */ | |
#define APP_USBD_CLASS_IFACES_CONFIG_EXTRACT(iface_configs) \ | |
MACRO_MAP_FOR_PARAM(iface_configs, \ | |
APP_USBD_CLASS_IFACE_CONFIG_EXTRACT_, \ | |
BRACKET_EXTRACT(iface_configs)) | |
/** | |
* @brief Extract all endpoints. | |
* | |
* Macro that extracts all endpoints from every interface. | |
* | |
* @param iface_configs List of interface configurations like explained | |
* in documentation for @ref APP_USBD_CLASS_INSTANCE_TYPEDEF | |
* | |
* @return Comma separated list of endpoints. | |
*/ | |
/*lint -emacro( (40), APP_USBD_CLASS_IFACES_EP_EXTRACT) */ | |
#define APP_USBD_CLASS_IFACES_EP_EXTRACT(iface_configs) \ | |
MACRO_MAP(APP_USBD_CLASS_IFACE_EP_EXTRACT_, BRACKET_EXTRACT(iface_configs)) | |
/** @} */ | |
/** | |
* @brief USBD instance of class mnemonic. | |
* | |
* Macro that generates mnemonic for the name of the structure that describes instance for selected class. | |
* | |
* @param type_name The name of the instance without _t postfix. | |
* | |
* @return The name with the right postfix to create the name for the type for the class. | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_TYPE(type_name) CONCAT_2(type_name, _t) | |
/** | |
* @brief USBD data for instance class mnemonic. | |
* | |
* The mnemonic of the variable type that holds writable part of the class instance. | |
* | |
* @param type_name The name of the instance without _t postfix. | |
* | |
* @return The name with the right postfix to create the name for the data type for the class. | |
*/ | |
#define APP_USBD_CLASS_DATA_TYPE(type_name) CONCAT_2(type_name, _data_t) | |
/** | |
* @brief Declare class specific member of class instance. | |
* | |
* @param type Type of the attached class configuration. | |
* | |
* @sa APP_USBD_CLASS_INSTANCE_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC(type) type class_part; | |
/** | |
* @brief Used if there is no class specific configuration. | |
* | |
* This constant can be used if there is no specific configuration inside created instance. | |
* | |
* @sa APP_USBD_CLASS_INSTANCE_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE | |
/** | |
* @brief Declare class specific member of class data. | |
* | |
* @param type Type of the attached class data. | |
* | |
* @sa APP_USBD_CLASS_DATA_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_DATA_SPECIFIC_DEC(type) APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC(type) | |
/** | |
* @brief Used if there is no class specific data. | |
* | |
* This constant can be used if there is no specific writable data inside created instance. | |
* | |
* @sa APP_USBD_CLASS_DATA_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_DATA_SPECIFIC_DEC_NONE APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE | |
/** | |
* @brief Instance structure declaration. | |
* | |
* The macro that declares a variable type that would be used to store given class instance. | |
* Class instance stores all the data from @ref app_usbd_class_inst_t and overlaid data for specified class. | |
* | |
* The structure of interface configuration data: | |
* @code | |
* ( | |
* (iface1_nr, (ep1, ep2, ep3)), | |
(iface2_nr), | |
(iface3_nr, (ep4)) | |
* ) | |
* @endcode | |
* | |
* @param type_name The name of the instance without _t postfix. | |
* @param interfaces_configs List of interface configurations like explained above. | |
* @param class_config_dec Result of the macro | |
* @ref APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC or | |
* @ref APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE | |
* | |
* @return The definition of the structure type that holds all the required data. | |
* | |
* @note It should not be used directly in the final application. See @ref APP_USBD_CLASS_DATA_TYPEDEF instead. | |
* | |
* @note APP_USBD_CLASS_DATA_TYPEDEF has to be called first for the compilation to success. | |
* | |
* @sa APP_USBD_CLASS_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_TYPEDEF(type_name, interfaces_configs, class_config_dec) \ | |
typedef union CONCAT_2(type_name, _u) \ | |
{ \ | |
app_usbd_class_inst_t base; \ | |
struct \ | |
{ \ | |
APP_USBD_CLASS_DATA_TYPE(type_name) * p_data; \ | |
app_usbd_class_methods_t const * p_class_methods; \ | |
struct \ | |
{ \ | |
uint8_t cnt; \ | |
app_usbd_class_iface_conf_t \ | |
config[NUM_VA_ARGS(BRACKET_EXTRACT(interfaces_configs))]; \ | |
app_usbd_class_ep_conf_t \ | |
ep[APP_USBD_CLASS_CONF_TOTAL_EP_COUNT(interfaces_configs)]; \ | |
} iface; \ | |
class_config_dec \ | |
} specific; \ | |
} APP_USBD_CLASS_INSTANCE_TYPE(type_name) | |
/** | |
* @brief Same as @ref APP_USBD_CLASS_INSTANCE_TYPEDEF but for class with EP0 only. | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_NO_EP_TYPEDEF(type_name, interfaces_configs, class_config_dec) \ | |
typedef union CONCAT_2(type_name, _u) \ | |
{ \ | |
app_usbd_class_inst_t base; \ | |
struct \ | |
{ \ | |
APP_USBD_CLASS_DATA_TYPE(type_name) * p_data; \ | |
app_usbd_class_methods_t const * p_class_methods; \ | |
struct \ | |
{ \ | |
uint8_t cnt; \ | |
app_usbd_class_iface_conf_t \ | |
config[NUM_VA_ARGS(BRACKET_EXTRACT(interfaces_configs))]; \ | |
} iface; \ | |
class_config_dec \ | |
} specific; \ | |
} APP_USBD_CLASS_INSTANCE_TYPE(type_name) | |
/** | |
* @brief Writable data structure declaration. | |
* | |
* The macro that declares a variable type that would be used to store given class writable data. | |
* Writable data contains base part of the type @ref app_usbd_class_data_t followed by | |
* class specific data. | |
* | |
* @param type_name The name of the type without _t postfix. | |
* @param class_data_dec Result of the macro | |
* @ref APP_USBD_CLASS_DATA_SPECIFIC_DEC or | |
* @ref APP_USBD_CLASS_DATA_SPECIFIC_DEC_NONE | |
* | |
* @return The definition of the structure type that holds all the required writable data. | |
* | |
* @note It should not be used directly in the final application. See @ref APP_USBD_CLASS_DATA_TYPEDEF instead. | |
* | |
* @sa APP_USBD_CLASS_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_DATA_TYPEDEF(type_name, class_data_dec) \ | |
typedef struct \ | |
{ \ | |
app_usbd_class_data_t base; \ | |
class_data_dec \ | |
}APP_USBD_CLASS_DATA_TYPE(type_name) | |
/** | |
* @brief Declare all data types required by the class instance. | |
* | |
* Macro that declares data type first and then instance type. | |
* | |
* @param type_name The name of the type without _t postfix. | |
* @param interface_configs List of interface configurations like in @ref APP_USBD_CLASS_INSTANCE_TYPEDEF. | |
* @param class_config_dec Result of the macro | |
* @ref APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC or | |
* @ref APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE | |
* @param class_data_dec Result of the macro | |
* @ref APP_USBD_CLASS_DATA_SPECIFIC_DEC or | |
* @ref APP_USBD_CLASS_DATA_SPECIFIC_DEC_NONE | |
* | |
* @return Declaration of the data type for the instance and instance itself. | |
* | |
* @sa APP_USBD_CLASS_DATA_TYPEDEF | |
* @sa APP_USBD_CLASS_INSTANCE_TYPEDEF | |
*/ | |
#define APP_USBD_CLASS_TYPEDEF(type_name, interface_configs, class_config_dec, class_data_dec) \ | |
APP_USBD_CLASS_DATA_TYPEDEF(type_name, class_data_dec); \ | |
APP_USBD_CLASS_INSTANCE_TYPEDEF(type_name, interface_configs, class_config_dec) | |
/** | |
* @brief Same as @ref APP_USBD_CLASS_TYPEDEF but for class with EP0 only. | |
*/ | |
#define APP_USBD_CLASS_NO_EP_TYPEDEF(type_name, \ | |
interface_configs, \ | |
class_config_dec, \ | |
class_data_dec) \ | |
APP_USBD_CLASS_DATA_TYPEDEF(type_name, class_data_dec); \ | |
APP_USBD_CLASS_INSTANCE_NO_EP_TYPEDEF(type_name, interface_configs, class_config_dec) | |
/** | |
* @brief Forward declaration of type defined by @ref APP_USBD_CLASS_TYPEDEF | |
* | |
* @param type_name The name of the type without _t postfix. | |
* */ | |
#define APP_USBD_CLASS_FORWARD(type_name) union CONCAT_2(type_name, _u) | |
/** | |
* @brief Generate the initialization data for. | |
* | |
* Macro that generates the initialization data for instance. | |
* | |
* @param p_ram_data Pointer to writable instance data structure. | |
* @param class_methods Class methods. | |
* @param interfaces_configs Exactly the same interface config data that in @ref APP_USBD_CLASS_INSTANCE_TYPEDEF | |
* @param class_config_part Configuration part. The data should be inside brackets. | |
* Any data here would be removed from brackets and then put as an initialization | |
* data for class_part member of instance structure. | |
* | |
* @note It should not be used directly in the final application. See @ref APP_USBD_CLASS_INST_DEF instead. | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_INITVAL(p_ram_data, \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) \ | |
{ \ | |
.specific = { \ | |
.p_data = p_ram_data, \ | |
.p_class_methods = class_methods, \ | |
.iface = { \ | |
.cnt = NUM_VA_ARGS(BRACKET_EXTRACT(interfaces_configs)), \ | |
.config = { APP_USBD_CLASS_IFACES_CONFIG_EXTRACT(interfaces_configs) }, \ | |
.ep = { APP_USBD_CLASS_IFACES_EP_EXTRACT(interfaces_configs) } \ | |
}, \ | |
BRACKET_EXTRACT(class_config_part) \ | |
} \ | |
} | |
/** | |
* @brief Same as @ref APP_USBD_CLASS_INSTANCE_INITVAL but for class with EP0 only. | |
*/ | |
#define APP_USBD_CLASS_INSTANCE_NO_EP_INITVAL(p_ram_data, \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) \ | |
{ \ | |
.specific = { \ | |
.p_data = p_ram_data, \ | |
.p_class_methods = class_methods, \ | |
.iface = { \ | |
.cnt = NUM_VA_ARGS(BRACKET_EXTRACT(interfaces_configs)), \ | |
.config = { APP_USBD_CLASS_IFACES_CONFIG_EXTRACT(interfaces_configs) } \ | |
}, \ | |
BRACKET_EXTRACT(class_config_part) \ | |
} \ | |
} | |
/** | |
* @brief Define the base class instance. | |
* | |
* Macro that defines whole instance variable and fill it with initialization data. | |
* | |
* The tricky part is @c class_config_part. | |
* The configuration data here has to be placed inside brackets. | |
* Then any type of values can be used depending on the type used in @ref APP_USBD_CLASS_TYPEDEF. | |
* If instance does not has any specyfic data, use just empty bracket here. | |
* @code | |
* APP_USBD_CLASS_TYPEDEF( | |
* some_base_class, | |
* CLASS_BASE_CONFIGURATION, | |
* APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE, | |
* APP_USBD_CLASS_DATA_SPECIFIC_DEC_NONE | |
* ); | |
* APP_USBD_CLASS_INST_DEF( | |
* some_base_class_inst, | |
* some_base_class, | |
* base_class_event_handler, | |
* CLASS_BASE_CONFIGURATION, | |
* () // Empty configuration | |
* ); | |
* @endcode | |
* | |
* If the type of instance configuration is simple type, just provide initialization value: | |
* @code | |
* APP_USBD_CLASS_TYPEDEF( | |
* some_base_class, | |
* CLASS_BASE_CONFIGURATION, | |
* APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE, | |
* APP_USBD_CLASS_DATA_SPECIFIC_DEC(uint8_t) | |
* ); | |
* APP_USBD_CLASS_INST_DEF( | |
* some_base_class_inst, | |
* some_base_class, | |
* base_class_event_handler, | |
* CLASS_BASE_CONFIGURATION, | |
* (12) // Example values | |
* ); | |
* @endcode | |
* | |
* If the type of instance configuration is structure, provide initialization value for the whole structure: | |
* @code | |
* typedef structure | |
* { | |
* uint32_t p1; | |
* uint8_t p2; | |
* }my_config_t; | |
* | |
* APP_USBD_CLASS_TYPEDEF( | |
* some_base_class, | |
* CLASS_BASE_CONFIGURATION, | |
* APP_USBD_CLASS_INSTANCE_SPECIFIC_DEC_NONE, | |
* APP_USBD_CLASS_DATA_SPECIFIC_DEC(my_config_t) | |
* ); | |
* APP_USBD_CLASS_INST_DEF( | |
* some_base_class_inst, | |
* some_base_class, | |
* base_class_event_handler, | |
* CLASS_BASE_CONFIGURATION, | |
* ({12, 3}) // Example values | |
* ); | |
* @endcode | |
* | |
* @param instance_name The name of created instance variable. | |
* It would be constant variable and its type would be app_usbd_class_inst_t. | |
* @param type_name The name of the variable type. It has to be the same type that was passed to | |
* @ref APP_USBD_CLASS_TYPEDEF | |
* @param class_methods Class unified interface. | |
* @param interfaces_configs The same configuration data that the one passed to @ref APP_USBD_CLASS_TYPEDEF | |
* @param class_config_part Configuration data to the type that was declared by class_data_dec when calling | |
* @ref APP_USBD_CLASS_TYPEDEF. | |
* Configuration data has to be provided in brackets. | |
* It would be extracted from brackets and placed in initialization part of configuration structure. | |
* See detailed description of this macro for more informations. | |
*/ | |
#define APP_USBD_CLASS_INST_DEF(instance_name, \ | |
type_name, \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) \ | |
static APP_USBD_CLASS_DATA_TYPE(type_name) CONCAT_2(instance_name, _data); \ | |
static const APP_USBD_CLASS_INSTANCE_TYPE(type_name) instance_name = \ | |
APP_USBD_CLASS_INSTANCE_INITVAL( \ | |
&CONCAT_2(instance_name, _data), \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) | |
/** | |
* @brief Define the base class instance in global scope. | |
* | |
* This is the same macro like @ref APP_USBD_CLASS_INST_DEF but it creates the instance | |
* without static keyword. | |
* | |
* @param instance_name See documentation for @ref APP_USBD_CLASS_INST_DEF | |
* @param type_name See documentation for @ref APP_USBD_CLASS_INST_DEF | |
* @param class_methods See documentation for @ref APP_USBD_CLASS_INST_DEF | |
* @param interfaces_configs See documentation for @ref APP_USBD_CLASS_INST_DEF | |
* @param class_config_part See documentation for @ref APP_USBD_CLASS_INST_DEF | |
*/ | |
#define APP_USBD_CLASS_INST_GLOBAL_DEF(instance_name, \ | |
type_name, \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) \ | |
static APP_USBD_CLASS_DATA_TYPE(type_name) CONCAT_2(instance_name, _data); \ | |
const APP_USBD_CLASS_INSTANCE_TYPE(type_name) instance_name = \ | |
APP_USBD_CLASS_INSTANCE_INITVAL( \ | |
&CONCAT_2(instance_name, _data), \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) | |
/** | |
* @brief Same as @ref APP_USBD_CLASS_INST_GLOBAL_DEF but for class with EP0 only. | |
*/ | |
#define APP_USBD_CLASS_INST_NO_EP_GLOBAL_DEF(instance_name, \ | |
type_name, \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) \ | |
static APP_USBD_CLASS_DATA_TYPE(type_name) CONCAT_2(instance_name, _data); \ | |
const APP_USBD_CLASS_INSTANCE_TYPE(type_name) instance_name = \ | |
APP_USBD_CLASS_INSTANCE_NO_EP_INITVAL( \ | |
&CONCAT_2(instance_name, _data), \ | |
class_methods, \ | |
interfaces_configs, \ | |
class_config_part) | |
/** | |
* @brief Access class specific configuration. | |
* | |
* Macro that returns class specific configuration. | |
* | |
* @param[in] p_inst Instance pointer. | |
* | |
* @return A pointer for class specific part of the instance. | |
* | |
* @note If macro is used on the instance that has no class specific configuration | |
* an error would be generated during compilation. | |
*/ | |
#define APP_USBD_CLASS_GET_SPECIFIC_CONFIG(p_inst) (&((p_inst)->specific.class_part)) | |
/** | |
* @brief Access class specific data. | |
* | |
* @param[in] p_inst Instance pointer. | |
* | |
* @return A pointer for class specific part of writable data. | |
* | |
* @note If macro is used on the instance that has no class specific data | |
* an error would be generated during compilation. | |
*/ | |
#define APP_USBD_CLASS_GET_SPECIFIC_DATA(p_inst) (&(((p_inst)->specific.p_data)->class_part)) | |
/** | |
* @brief Macro to get base instance from class specific instance. | |
* | |
* This macro may be used on class specific instance to get base instance that | |
* can be processed by base instance access functions. | |
* Class specific instance can be just casted to class base instance, | |
* but then we would totally lost type safety. | |
* | |
* A little more safe is to use pointer to base member of class instance. | |
* This would generate an error when used on any variable that has no base member | |
* and would generate also error if this base member is wrong type. | |
*/ | |
#define APP_USBD_CLASS_BASE_INSTANCE(p_inst) (&((p_inst)->base)) | |
/*lint -emacro(142 438 616 646, APP_USBD_CLASS_DESCRIPTOR_INIT, APP_USBD_CLASS_DESCRIPTOR_BEGIN, APP_USBD_CLASS_DESCRIPTOR_YIELD, APP_USBD_CLASS_DESCRIPTOR_END, APP_USBD_CLASS_DESCRIPTOR_WRITE)*/ | |
/** | |
* @brief Initialize class descriptor. | |
* | |
* @param[in] p_ctx Class descriptor context. | |
*/ | |
#define APP_USBD_CLASS_DESCRIPTOR_INIT(p_ctx) \ | |
(p_ctx)->line = 0; | |
/** | |
* @brief Begin class descriptor. | |
* | |
* @param[in] p_ctx Class descriptor context. | |
* @param[in] p_buff Buffer to write into. | |
* @param[in] max_size Size of the buffer. | |
*/ | |
#define APP_USBD_CLASS_DESCRIPTOR_BEGIN(p_ctx, p_buff, max_size) \ | |
ASSERT((p_ctx) != NULL); \ | |
app_usbd_class_descriptor_state_t this_descriptor_feed; \ | |
this_descriptor_feed.p_buffer = (p_buff); \ | |
this_descriptor_feed.current_size = 0; \ | |
this_descriptor_feed.maximum_size = (max_size); \ | |
this_descriptor_feed.p_context = (p_ctx); \ | |
switch ((this_descriptor_feed.p_context)->line) \ | |
{ \ | |
case 0: \ | |
; | |
/** | |
* @brief Yield class descriptor | |
* | |
*/ | |
#define APP_USBD_CLASS_DESCRIPTOR_YIELD() \ | |
do \ | |
{ \ | |
(this_descriptor_feed.p_context)->line = __LINE__; \ | |
return true; \ | |
case __LINE__: \ | |
; \ | |
} while (0) | |
/*lint -emacro(438 527, APP_USBD_CLASS_DESCRIPTOR_END)*/ | |
/** | |
* @brief End class descriptor. | |
* | |
* This function has to be called at the end of class descriptor feeder function. | |
* No other operations in feeder function can be done after calling it. | |
*/ | |
#define APP_USBD_CLASS_DESCRIPTOR_END() \ | |
APP_USBD_CLASS_DESCRIPTOR_YIELD(); \ | |
} \ | |
(this_descriptor_feed.p_context)->line = 0; \ | |
return false; | |
/** | |
* @brief Write descriptor using protothreads. | |
* | |
* This function writes one byte to the buffer with offset. If buffer is full | |
* it yields. | |
* | |
* It is used by the class descriptor feeders internally. | |
* | |
* @ref APP_USBD_CLASS_DESCRIPTOR_BEGIN has to be called before using this function. | |
* @ref APP_USBD_CLASS_DESCRIPTOR_END has to be called after last use of this function. | |
* | |
* @param data Byte to be written to buffer. | |
*/ | |
#define APP_USBD_CLASS_DESCRIPTOR_WRITE(data) \ | |
do \ | |
{ \ | |
(this_descriptor_feed.p_context)->data_buffer = (data); \ | |
if (this_descriptor_feed.current_size >= this_descriptor_feed.maximum_size) \ | |
{ \ | |
APP_USBD_CLASS_DESCRIPTOR_YIELD(); \ | |
} \ | |
if(this_descriptor_feed.p_buffer != NULL) \ | |
{ \ | |
*(this_descriptor_feed.p_buffer + this_descriptor_feed.current_size) = \ | |
(this_descriptor_feed.p_context)->data_buffer; \ | |
} \ | |
this_descriptor_feed.current_size++; \ | |
} while(0); | |
/** @} */ | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* APP_USBD_CLASS_BASE_H__ */ |