/** * 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 #include #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__ */