/* * Copyright (C) 2021 BlueKitchen GmbH * * 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 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 the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN * GMBH 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. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ /** * @title HID Service Client * */ #ifndef HIDS_CLIENT_H #define HIDS_CLIENT_H #include #include "btstack_defines.h" #include "btstack_hid.h" #include "bluetooth.h" #include "btstack_linked_list.h" #include "ble/gatt_client.h" #if defined __cplusplus extern "C" { #endif /** * @text The HID Service Client is used on the HID Host to receive reports and other HID data. */ #ifndef MAX_NUM_HID_SERVICES #define MAX_NUM_HID_SERVICES 3 #endif #define HIDS_CLIENT_INVALID_REPORT_INDEX 0xFF #define HIDS_CLIENT_NUM_REPORTS 15 typedef enum { HIDS_CLIENT_STATE_IDLE, // get all HID services HIDS_CLIENT_STATE_W2_QUERY_SERVICE, HIDS_CLIENT_STATE_W4_SERVICE_RESULT, // for each service, discover all characteristics HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC, HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT, // called if BOOT mode HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE, // for each REPORT_MAP characteristic, read HID Descriptor (Report Map Characteristic Value) HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR, HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR, // for REPORT_MAP characteristic, discover descriptor HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS, HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT, // for REPORT_MAP characteristic, read External Report Reference Characteristic Descriptor HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID, HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID, // for every external report reference uuid, discover external Report characteristic HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC, HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT, // for each Report characteristics, discover Report characteristic descriptor HIDS_CLIENT_STATE_W2_FIND_REPORT, HIDS_CLIENT_STATE_W4_REPORT_FOUND, // then read value of Report characteristic descriptor to get report ID and type HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE, HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE, HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS, HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED, HIDS_CLIENT_STATE_CONNECTED, HIDS_CLIENT_W2_SEND_WRITE_REPORT, HIDS_CLIENT_W4_WRITE_REPORT_DONE, HIDS_CLIENT_W2_SEND_GET_REPORT, HIDS_CLIENT_W4_GET_REPORT_RESULT, HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC, HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT, HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE, HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS, HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED, #ifdef ENABLE_TESTING_SUPPORT HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION, HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT, #endif } hid_service_client_state_t; typedef struct { // GATT cache //reuse as descriptor_handle uint16_t value_handle; uint16_t end_handle; uint16_t properties; // UUID of external Report characteristic, stored in Report Map descriptor EXTERNAL_REPORT_REFERENCE uint16_t external_report_reference_uuid; #ifdef ENABLE_TESTING_SUPPORT uint16_t ccc_handle; #endif // service mapping uint8_t service_index; uint8_t report_id; hid_report_type_t report_type; uint8_t boot_report; gatt_client_notification_t notification_listener; } hids_client_report_t; typedef struct { hid_protocol_mode_t protocol_mode; uint16_t start_handle; uint16_t end_handle; uint16_t report_map_value_handle; uint16_t report_map_end_handle; uint16_t hid_information_value_handle; uint16_t control_point_value_handle; uint16_t protocol_mode_value_handle; // descriptor storage uint16_t hid_descriptor_offset; uint16_t hid_descriptor_len; uint16_t hid_descriptor_max_len; uint8_t hid_descriptor_status; // ERROR_CODE_SUCCESS if descriptor available, // ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if not, and // ERROR_CODE_MEMORY_CAPACITY_EXCEEDED if descriptor is larger then the available space } hid_service_t; typedef struct { btstack_linked_item_t item; hci_con_handle_t con_handle; uint16_t cid; hid_service_client_state_t state; btstack_packet_handler_t client_handler; uint8_t num_instances; hid_service_t services[MAX_NUM_HID_SERVICES]; // used for discovering characteristics uint8_t service_index; hid_protocol_mode_t required_protocol_mode; // send report hids_client_report_t reports[HIDS_CLIENT_NUM_REPORTS]; uint8_t num_reports; hids_client_report_t external_reports[HIDS_CLIENT_NUM_REPORTS]; uint8_t num_external_reports; btstack_context_callback_registration_t write_without_response_request; // index used for report and report map search uint8_t report_index; uint16_t report_len; const uint8_t * report; // used to write control_point and protocol_mode uint16_t handle; uint8_t value; } hids_client_t; /* API_START */ /** * @brief Initialize HID Service Client. The HID Descriptor storage is shared between all connections. * * @param hid_descriptor_storage * @param hid_descriptor_storage_len */ void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len); /** * @brief Connect to HID Services of remote device. Event GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED will be emitted * after all remote HID services and characteristics are found, and notifications for all input reports are enabled. * Status code can be ERROR_CODE_SUCCES if at least one HID service is found, otherwise either ATT errors or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no service or report map or report are found. * It also contains the number of individual HIDS Services. * * Connection state is stored in hids_client_t objects provided via memory pool * Max number of connections is limited by MAX_NR_HIDS_CLIENTS unless HAVE_MALLOC is used * * @param con_handle * @param packet_handler * @param protocol_mode see hid_protocol_mode_t in btstack_hid.h * @param hids_cid (out) to use for other functions * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_COMMAND_DISALLOWED if there is already a client * associated with con_handle, or BTSTACK_MEMORY_ALLOC_FAILED. */ uint8_t hids_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, hid_protocol_mode_t protocol_mode, uint16_t * hids_cid); /** * @brief Send HID report. Event GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN is emitted. * * @param hids_cid * @param report_id * @param report_type * @param report * @param report_len * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found, or * ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE if report length exceeds MTU. */ uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len); /** * @brief Get HID report. Event GATTSERVICE_SUBEVENT_HID_REPORT is emitted. * * @param hids_cid * @param report_id * @param report_type * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found, or * ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE if report length exceeds MTU. */ uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type); /** * @brief Get HID Information. Event GATTSERVICE_SUBEVENT_HID_INFORMATION is emitted. * * @param hids_cid * @param service_index * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found. */ uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index); /** * @brief Get Protocol Mode. Event GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE is emitted. * * @param hids_cid * @param service_index * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found. */ uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index); /** * @brief Set Protocol Mode. * * @param hids_cid * @param service_index * @param protocol_mode * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found. */ uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode); /** * @brief Send Suspend to remote HID service. * * @param hids_cid * @param service_index * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found. */ uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index); /** * @brief Send Exit Suspend to remote HID service. * * @param hids_cid * @param service_index * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found. */ uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index); /** * @brief Enable all notifications. Event GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION reports current configuration. * * @param hids_cid * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, or * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state. */ uint8_t hids_client_enable_notifications(uint16_t hids_cid); /** * @brief Disable all notifications. Event GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION reports current configuration. * * @param hids_cid * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, or * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state. */ uint8_t hids_client_disable_notifications(uint16_t hids_cid); /** * @brief Disconnect from HID Service. * * @param hids_cid * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER */ uint8_t hids_client_disconnect(uint16_t hids_cid); /* * @brief Get descriptor data. For services in boot mode without a Report Map, a default HID Descriptor for Keyboard/Mouse is provided. * * @param hid_cid * @return data */ const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index); /* * @brief Get descriptor length * * @param hid_cid * @return length */ uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index); /** * @brief De-initialize HID Service Client. * */ void hids_client_deinit(void); /* API_END */ #if defined __cplusplus } #endif #endif