/** * Copyright (c) 2017 - 2020, 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_HID_H__ #define APP_USBD_HID_H__ #include #include #include "sdk_common.h" #include "nrf_atomic.h" #include "app_usbd_hid_types.h" #include "app_usbd.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup app_usbd_hid USB HID class * @ingroup app_usbd * * @brief @tagAPI52840 Module with generic HID event data processing. * @{ */ #define APP_USBD_HID_IFACE_IDX 0 /**< @brief HID instance interface index. */ #define APP_USBD_HID_EPIN_IDX 0 /**< @brief HID instance endpoint IN index. */ #define APP_USBD_HID_EPOUT_IDX 1 /**< @brief HID instance endpoint OUT index.*/ /** * @brief HID context state flags. * * Bit numbers in @ref app_usbd_hid_ctx_t::state_flags. */ typedef enum { APP_USBD_HID_STATE_FLAG_APPENDED = 0, /**< State flag APPENDED. */ APP_USBD_HID_STATE_FLAG_STARTED = 1, /**< State flag STARTED. */ APP_USBD_HID_STATE_FLAG_SUSPENDED = 2, /**< State flag SUSPENDED. */ APP_USBD_HID_STATE_FLAG_TRANS_IN_PROGRESS = 3, /**< State flag TRANS_IN_PROGRESS. */ } app_usbd_hid_state_flag_t; /** * @brief Events passed to user event handler. * * @note Example prototype of user event handler: @code void hid_user_ev_handler(app_usbd_class_inst_t const * p_inst, app_usbd_hid_mouse_user_event_t event); @endcode */ typedef enum { APP_USBD_HID_USER_EVT_SET_BOOT_PROTO, /**< Event SET_BOOT_PROTOCOL. */ APP_USBD_HID_USER_EVT_SET_REPORT_PROTO, /**< Event SET_REPORT_PROTOCOL. */ APP_USBD_HID_USER_EVT_OUT_REPORT_READY, /**< Event OUT_REPORT_READY. */ APP_USBD_HID_USER_EVT_IN_REPORT_DONE, /**< Event IN_REPORT_DONE. */ APP_USBD_HID_USER_EVT_FEATURE_REPORT_READY, /**< Event FEATURE_REPORT_READY. */ } app_usbd_hid_user_event_t; /** * @brief User event handler. * * @param[in] p_inst Class instance. * @param[in] event User event. */ typedef void (*app_usbd_hid_user_ev_handler_t)(app_usbd_class_inst_t const * p_inst, app_usbd_hid_user_event_t event); /** * @brief Idle report handler. * * @param[in] p_inst Class instance. * @param[in] report_id Number of report ID that needs idle transfer. */ typedef ret_code_t (*app_usbd_hid_idle_handler_t)(app_usbd_class_inst_t const * p_inst, uint8_t report_id); /** * @brief HID unified interface * */ typedef struct { /** * @brief Function called on HID specific, GetReport request. * * This function should trigger data write to control pipe. * * @param[in] p_inst Class instance. * @param[in] p_setup_ev Setup event. * * @return Standard error code. */ ret_code_t (*on_get_report)(app_usbd_class_inst_t const * p_inst, app_usbd_setup_evt_t const * p_setup_ev); /** * @brief Function called on HID specific, SetReport request. * * This function should trigger data read from control pipe. This function is not required and * NULL could be pinned to this handler when output report is not defined in report descriptor. * * @param[in] p_inst Class instance. * @param[in] p_setup_ev Setup event. * * @return Standard error code. */ ret_code_t (*on_set_report)(app_usbd_class_inst_t const * p_inst, app_usbd_setup_evt_t const * p_setup_ev); /** * @brief Function called on IN endpoint transfer. * * This function should trigger next endpoint IN transfer if required. * * @param[in] p_inst Class instance. * * @return Standard error code. */ ret_code_t (*ep_transfer_in)(app_usbd_class_inst_t const * p_inst); /** * @brief Function called on OUT endpoint transfer. * * This function should should read data from OUT endpoint. This function is not required and * NULL could be pinned to this handler when class doesn't have OUT endpoint. * * @param[in] p_inst Class instance. * * @return Standard error code. */ ret_code_t (*ep_transfer_out)(app_usbd_class_inst_t const * p_inst); /** * @brief Function returns subclass descriptor count. * * @param[in] p_inst Class instance. * * @return Count of descriptors */ uint8_t (*subclass_count)(app_usbd_class_inst_t const * p_inst); /** * @brief Function returns subclass descriptor size. * * @param[in] p_inst Class instance. * @param[in] desc_num Index of the subclass descriptor * * @return Size of descriptor */ size_t (*subclass_length)(app_usbd_class_inst_t const * p_inst, uint8_t desc_num); /** * @brief Function returns pointer to subclass descriptor data. * * @param[in] p_inst Class instance. * @param[in] desc_num Index of the subclass descriptor * @param[in] cur_byte Index of required byte * * @return Pointer to requested byte in subclass descriptor */ const uint8_t * (*subclass_data)(app_usbd_class_inst_t const * p_inst, uint8_t desc_num, uint32_t cur_byte); /** * @brief Function called on idle transfer * * This function should trigger next idle transfer. * * @param[in,out] p_inst Instance of the class. * @param[in] report_id Number of report ID that needs idle transfer. * * @return Standard error code. */ ret_code_t (*on_idle)(app_usbd_class_inst_t const * p_inst, uint8_t report_id); ret_code_t (*set_idle_handler)(app_usbd_class_inst_t const * p_inst, app_usbd_hid_idle_handler_t handler); } app_usbd_hid_methods_t; /** * @brief HID report buffers. */ typedef struct { uint8_t * p_buff; size_t size; } app_usbd_hid_report_buffer_t; /** * @brief Define OUT report buffer structure @ref app_usbd_hid_report_buffer_t. * * @param name Instance name. * @param rep_size Output report size. */ #define APP_USBD_HID_GENERIC_GLOBAL_OUT_REP_DEF(name, rep_size) \ static uint8_t CONCAT_2(name, _buf)[(rep_size)]; \ const app_usbd_hid_report_buffer_t name = { \ .p_buff = CONCAT_2(name, _buf), \ .size = sizeof(CONCAT_2(name, _buf)), \ } /** * @brief Define FEATURE report buffer structure @ref app_usbd_hid_report_buffer_t. * * @param name Instance name. * @param rep_size Feature report size. */ #define APP_USBD_HID_GENERIC_GLOBAL_FEATURE_REP_DEF(name, rep_size) \ static uint8_t CONCAT_2(name, _feature_buf)[(rep_size)]; \ const app_usbd_hid_report_buffer_t name = { \ .p_buff = CONCAT_2(name, _feature_buf), \ .size = sizeof(CONCAT_2(name, _feature_buf)), \ } /** * @brief HID subclass descriptor. */ typedef struct { size_t size; app_usbd_descriptor_t type; uint8_t const * const p_data; } app_usbd_hid_subclass_desc_t; /** * @brief Initializer of HID report descriptor * * @param name Report descriptor name * @param ... Report descriptor data */ #define APP_USBD_HID_GENERIC_SUBCLASS_REPORT_DESC(name, ...) \ static uint8_t const CONCAT_2(name, _data)[] = \ __VA_ARGS__ \ ; \ static const app_usbd_hid_subclass_desc_t name = \ { \ sizeof(CONCAT_2(name, _data)), \ APP_USBD_DESCRIPTOR_REPORT, \ CONCAT_2(name,_data) \ } /** * @brief Initializer of HID physical descriptor * * @param name Physical descriptor name * @param ... Physical descriptor data */ #define APP_USBD_HID_GENERIC_SUBCLASS_PHYSICAL_DESC(name, ...) \ static uint8_t const CONCAT_2(name, _data)[] = \ __VA_ARGS__ \ ; \ static const app_usbd_hid_subclass_desc_t name = \ { \ sizeof(CONCAT_2(name, _data)), \ APP_USBD_DESCRIPTOR_PHYSICAL, \ CONCAT_2(name,_data) \ } /** * @brief USB HID instance. */ typedef struct { app_usbd_hid_subclass_desc_t const ** const p_subclass_desc; //!< HID subclass descriptors array. size_t subclass_desc_count; //!< HID subclass descriptors count. app_usbd_hid_subclass_t subclass_boot; //!< Boot device (see HID definition). app_usbd_hid_protocol_t protocol; //!< HID protocol (see HID definition). app_usbd_hid_report_buffer_t * p_rep_buffer_in; //!< Report buffer IN. app_usbd_hid_report_buffer_t const * p_rep_buffer_out; //!< Report buffer OUT (only one instance). app_usbd_hid_report_buffer_t const * p_rep_buffer_feature; //!< Report buffer FEATURE. app_usbd_hid_methods_t const * p_hid_methods; //!< Hid interface methods. app_usbd_hid_user_ev_handler_t user_event_handler; //!< User event handler. uint8_t * p_ep_interval; //!< Endpoint intervals. } app_usbd_hid_inst_t; /** * @brief USB HID instance initializer @ref app_usbd_hid_inst_t. * * @param subclass_dsc HID subclass descriptors. * @param sub_boot Subclass boot. (@ref app_usbd_hid_subclass_t) * @param protocl HID protocol. (@ref app_usbd_hid_protocol_t) * @param report_buff_in Input report buffer list. * @param report_buff_out Output report buffer. * @param report_buff_feature Feature report buffer. * @param user_ev_handler @ref app_usbd_hid_user_ev_handler_t. * @param hid_methods @ref app_usbd_hid_methods_t. * @param ep_list List of endpoints and intervals * */ #define APP_USBD_HID_INST_CONFIG(subclass_dsc, \ sub_boot, \ protocl, \ report_buff_in, \ report_buff_out, \ report_buff_feature, \ user_ev_handler, \ hid_methods, \ ep_list) \ { \ .p_subclass_desc = subclass_dsc, \ .subclass_desc_count = ARRAY_SIZE(subclass_dsc), \ .p_rep_buffer_in = report_buff_in, \ .p_rep_buffer_out = report_buff_out, \ .p_rep_buffer_feature = report_buff_feature, \ .user_event_handler = user_ev_handler, \ .p_hid_methods = hid_methods, \ .subclass_boot = sub_boot, \ .protocol = protocl, \ .p_ep_interval = ep_list \ } /** * @brief HID internal context. * */ typedef struct { nrf_atomic_u32_t state_flags; //!< HID state flags @ref app_usbd_hid_state_flag_t. nrf_atomic_flag_t access_lock; //!< Lock flag to internal data. uint8_t idle_rate[APP_USBD_HID_REPORT_IDLE_TABLE_SIZE]; //!< HID idle rate (4ms units). app_usbd_hid_protocol_select_t selected_protocol; //!< Currently selected HID protocol. app_usbd_hid_idle_handler_t idle_handler; //!< Idle report handler. uint32_t first_idle[APP_USBD_HID_REPORT_IDLE_TABLE_SIZE]; //!< Number of frame at first idle transaction. bool lock_idle[APP_USBD_HID_REPORT_IDLE_TABLE_SIZE]; //!< Lock flag to idle transactions. bool idle_on; //!< Idle transactions flag. bool idle_id_report; //!< Idle transactions with nonzero report id flag. } app_usbd_hid_ctx_t; /** * @brief Locks internal hid context. * * Simple semaphore functionality to prevent concurrent access from application and * interrupt to internal mouse data. * * @param[in] p_hid_ctx Internal hid context */ static inline void app_usbd_hid_access_lock(app_usbd_hid_ctx_t * p_hid_ctx) { UNUSED_RETURN_VALUE(nrf_atomic_flag_set(&p_hid_ctx->access_lock)); __DSB(); } /** * @brief Unlocks internal hid context. * * Simple semaphore functionality to prevent concurrent access from application and * interrupt to internal mouse data. * * @param[in] p_hid_ctx Internal hid context. */ static inline void app_usbd_hid_access_unlock(app_usbd_hid_ctx_t * p_hid_ctx) { UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_hid_ctx->access_lock)); __DSB(); } /** * @brief Tests whether internal lock is acquired. * * @param[in] p_hid_ctx Internal HID context. * * @retval true Locked. * @retval false Unlocked. */ static inline bool app_usbd_hid_access_lock_test(app_usbd_hid_ctx_t * p_hid_ctx) { return p_hid_ctx->access_lock != 0; } /** * @brief Set one of the HID internal state flags. * * @param[in] p_hid_ctx Internal HID context. * @param[in] flag Flag to set. */ static inline void app_usbd_hid_state_flag_set(app_usbd_hid_ctx_t * p_hid_ctx, app_usbd_hid_state_flag_t flag) { UNUSED_RETURN_VALUE(nrf_atomic_u32_or(&p_hid_ctx->state_flags, 1u << flag)); } /** * @brief Clear one of the HID internal state flags. * * @param[in] p_hid_ctx Internal HID context. * @param[in] flag Flag to clear. */ static inline void app_usbd_hid_state_flag_clr(app_usbd_hid_ctx_t * p_hid_ctx, app_usbd_hid_state_flag_t flag) { UNUSED_RETURN_VALUE(nrf_atomic_u32_and(&p_hid_ctx->state_flags, ~(1u << flag))); } /** * @brief Test one of the HID internal state flags. * * @param[in] p_hid_ctx Internal HID context. * @param[in] flag Flag to test. * * @retval true Flag is set. * @retval false Flag is not set. */ static inline bool app_usbd_hid_state_flag_test(app_usbd_hid_ctx_t * p_hid_ctx, app_usbd_hid_state_flag_t flag) { return ((p_hid_ctx->state_flags >> flag) & 1) == 1; } /** * @brief Checks whether HID endpoint transfer required. * * @param[in] p_hid_ctx Internal HID context. * * @retval true Input endpoint transfer required. * @retval false Transfer in progress or not allowed. */ static inline bool app_usbd_hid_trans_required(app_usbd_hid_ctx_t * p_hid_ctx) { if (app_usbd_hid_state_flag_test(p_hid_ctx, APP_USBD_HID_STATE_FLAG_SUSPENDED) != 0) { UNUSED_RETURN_VALUE(app_usbd_wakeup_req()); return false; } return app_usbd_hid_state_flag_test(p_hid_ctx, APP_USBD_HID_STATE_FLAG_TRANS_IN_PROGRESS) == 0; } /** * @brief Validates internal hid state. * * HID Mouse has to receive some USBD events before functions from this module could be used. * * @param[in] p_hid_ctx Internal hid context. * * @retval true State is valid. * @retval false State is invalid. */ static inline bool app_usbd_hid_state_valid(app_usbd_hid_ctx_t * p_hid_ctx) { /*Check whether internal flags allow to enable mouse*/ if ((app_usbd_hid_state_flag_test(p_hid_ctx, APP_USBD_HID_STATE_FLAG_APPENDED) == 0) || (app_usbd_hid_state_flag_test(p_hid_ctx, APP_USBD_HID_STATE_FLAG_STARTED) == 0)) { return false; } return true; } /** * @brief HID generic event handler. * * This handler should process every class event after specific class handler. * This approach allow to handle some events in the same way in all HID sub-classes. * * @param[in] p_inst Generic class instance. * @param[in] p_hinst HID class instance. * @param[in] p_hid_ctx HID context. * @param[in] p_event Complex event structure. * * @return Standard error code. */ ret_code_t app_usbd_hid_event_handler(app_usbd_class_inst_t const * p_inst, app_usbd_hid_inst_t const * p_hinst, app_usbd_hid_ctx_t * p_hid_ctx, app_usbd_complex_evt_t const * p_event); /** * @brief Returns IN report buffer. * * @param[in] p_hinst HID class instance. * * @return Report buffer handle or NULL if report doesn't exist. */ app_usbd_hid_report_buffer_t * app_usbd_hid_rep_buff_in_get(app_usbd_hid_inst_t const * p_hinst); /** * @brief Returns OUT report buffer. * * Output reports are handled in interrupt handler so only one buffer is required. Buffer returned by * this function has predefined size, which should be equal (maximum OUTPUT report size + 1). To receive * OUT report this function should be called on @ref APP_USBD_HID_USER_EVT_OUT_REPORT_READY event. * * @param[in] p_hinst HID class instance. * * @return Report buffer handle or NULL if report doesn't exist. */ static inline app_usbd_hid_report_buffer_t const * app_usbd_hid_rep_buff_out_get(app_usbd_hid_inst_t const * p_hinst) { ASSERT(p_hinst); return p_hinst->p_rep_buffer_out; } /** * @brief Returns FEATURE report buffer. * * @param[in] p_hinst HID class instance. * * @return Report buffer handle or NULL if report doesn't exist. */ app_usbd_hid_report_buffer_t const * app_usbd_hid_rep_buff_feature_get(app_usbd_hid_inst_t const * p_hinst); /** * @brief Returns HID selected protocol. * * @param[in] p_hid_ctx HID context. * * @return Currently selected protocol (@ref app_usbd_hid_protocol_select_t). */ static inline app_usbd_hid_protocol_select_t app_usbd_hid_selected_protocol_get(app_usbd_hid_ctx_t * p_hid_ctx) { ASSERT(p_hid_ctx); return p_hid_ctx->selected_protocol; } /** * @brief Auxiliary function to access to HID IN endpoint address. * * @param[in] p_inst Class instance data. * * @return IN endpoint address. */ static inline nrf_drv_usbd_ep_t app_usbd_hid_epin_addr_get(app_usbd_class_inst_t const * p_inst) { app_usbd_class_iface_conf_t const * class_iface; class_iface = app_usbd_class_iface_get(p_inst, APP_USBD_HID_IFACE_IDX); app_usbd_class_ep_conf_t const * ep_cfg; ep_cfg = app_usbd_class_iface_ep_get(class_iface, APP_USBD_HID_EPIN_IDX); return app_usbd_class_ep_address_get(ep_cfg); } /** * @brief Auxiliary function to access to HID generic OUT endpoint address. * * @param[in] p_inst Class instance data. * * @return OUT endpoint address. */ static inline nrf_drv_usbd_ep_t app_usbd_hid_epout_addr_get(app_usbd_class_inst_t const * p_inst) { app_usbd_class_iface_conf_t const * class_iface; class_iface = app_usbd_class_iface_get(p_inst, APP_USBD_HID_IFACE_IDX); app_usbd_class_ep_conf_t const * ep_cfg; ep_cfg = app_usbd_class_iface_ep_get(class_iface, APP_USBD_HID_EPOUT_IDX); return app_usbd_class_ep_address_get(ep_cfg); } /** @} */ #ifdef __cplusplus } #endif #endif /* APP_USBD_HID_H__ */