| /** |
| * Copyright (c) 2017 - 2019, Nordic Semiconductor ASA |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form, except as embedded into a Nordic |
| * Semiconductor ASA integrated circuit in a product or a software update for |
| * such product, must reproduce the above copyright notice, this list of |
| * conditions and the following disclaimer in the documentation and/or other |
| * materials provided with the distribution. |
| * |
| * 3. Neither the name of Nordic Semiconductor ASA nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * 4. This software, with or without modification, must only be used with a |
| * Nordic Semiconductor ASA integrated circuit. |
| * |
| * 5. Any software provided in binary form under this license must not be reverse |
| * engineered, decompiled, modified and/or disassembled. |
| * |
| * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #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__ */ |