1a4bfc4feSMatthias Ringwald /* 2a4bfc4feSMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3a4bfc4feSMatthias Ringwald * 4a4bfc4feSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5a4bfc4feSMatthias Ringwald * modification, are permitted provided that the following conditions 6a4bfc4feSMatthias Ringwald * are met: 7a4bfc4feSMatthias Ringwald * 8a4bfc4feSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9a4bfc4feSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10a4bfc4feSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11a4bfc4feSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12a4bfc4feSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13a4bfc4feSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14a4bfc4feSMatthias Ringwald * contributors may be used to endorse or promote products derived 15a4bfc4feSMatthias Ringwald * from this software without specific prior written permission. 16a4bfc4feSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17a4bfc4feSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18a4bfc4feSMatthias Ringwald * monetary gain. 19a4bfc4feSMatthias Ringwald * 20a4bfc4feSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21a4bfc4feSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22a4bfc4feSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23a4bfc4feSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24a4bfc4feSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25a4bfc4feSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26a4bfc4feSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27a4bfc4feSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28a4bfc4feSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29a4bfc4feSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30a4bfc4feSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31a4bfc4feSMatthias Ringwald * SUCH DAMAGE. 32a4bfc4feSMatthias Ringwald * 33a4bfc4feSMatthias Ringwald * Please inquire about commercial licensing options at 34a4bfc4feSMatthias Ringwald * [email protected] 35a4bfc4feSMatthias Ringwald * 36a4bfc4feSMatthias Ringwald */ 37a4bfc4feSMatthias Ringwald 38523edd21SMatthias Ringwald #define __BTSTACK_FILE__ "hids_device.c" 39523edd21SMatthias Ringwald 40a4bfc4feSMatthias Ringwald /** 41a4bfc4feSMatthias Ringwald * Implementation of the GATT HIDS Device 42a4bfc4feSMatthias Ringwald * To use with your application, add '#import <hids.gatt>' to your .gatt file 43a4bfc4feSMatthias Ringwald */ 44a4bfc4feSMatthias Ringwald 45a4bfc4feSMatthias Ringwald #include "hids_device.h" 46a4bfc4feSMatthias Ringwald 4735b80bb0SMatthias Ringwald #include "ble/att_db.h" 4835b80bb0SMatthias Ringwald #include "ble/att_server.h" 49a4bfc4feSMatthias Ringwald #include "bluetooth_gatt.h" 50a4bfc4feSMatthias Ringwald #include "btstack_util.h" 51523edd21SMatthias Ringwald #include "btstack_debug.h" 52a4bfc4feSMatthias Ringwald 53*0235c9e5SMilanka Ringwald #define HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS 0x80 54*0235c9e5SMilanka Ringwald 55*0235c9e5SMilanka Ringwald typedef struct{ 56*0235c9e5SMilanka Ringwald uint16_t con_handle; 57*0235c9e5SMilanka Ringwald 58*0235c9e5SMilanka Ringwald uint8_t hid_country_code; 59*0235c9e5SMilanka Ringwald const uint8_t * hid_descriptor; 60*0235c9e5SMilanka Ringwald uint16_t hid_descriptor_size; 61*0235c9e5SMilanka Ringwald 62*0235c9e5SMilanka Ringwald uint16_t hid_report_map_handle; 63*0235c9e5SMilanka Ringwald uint16_t hid_protocol_mode; 64*0235c9e5SMilanka Ringwald uint16_t hid_protocol_mode_value_handle; 65*0235c9e5SMilanka Ringwald 66*0235c9e5SMilanka Ringwald uint16_t hid_boot_mouse_input_value_handle; 67*0235c9e5SMilanka Ringwald uint16_t hid_boot_mouse_input_client_configuration_handle; 68*0235c9e5SMilanka Ringwald uint16_t hid_boot_mouse_input_client_configuration_value; 69*0235c9e5SMilanka Ringwald 70*0235c9e5SMilanka Ringwald uint16_t hid_boot_keyboard_input_value_handle; 71*0235c9e5SMilanka Ringwald uint16_t hid_boot_keyboard_input_client_configuration_handle; 72*0235c9e5SMilanka Ringwald uint16_t hid_boot_keyboard_input_client_configuration_value; 73*0235c9e5SMilanka Ringwald 74*0235c9e5SMilanka Ringwald uint16_t hid_report_input_value_handle; 75*0235c9e5SMilanka Ringwald uint16_t hid_report_input_client_configuration_handle; 76*0235c9e5SMilanka Ringwald uint16_t hid_report_input_client_configuration_value; 77*0235c9e5SMilanka Ringwald 78*0235c9e5SMilanka Ringwald uint16_t hid_report_output_value_handle; 79*0235c9e5SMilanka Ringwald uint16_t hid_report_output_client_configuration_handle; 80*0235c9e5SMilanka Ringwald uint16_t hid_report_output_client_configuration_value; 81*0235c9e5SMilanka Ringwald 82*0235c9e5SMilanka Ringwald uint16_t hid_report_feature_value_handle; 83*0235c9e5SMilanka Ringwald uint16_t hid_report_feature_client_configuration_handle; 84*0235c9e5SMilanka Ringwald uint16_t hid_report_feature_client_configuration_value; 85*0235c9e5SMilanka Ringwald 86*0235c9e5SMilanka Ringwald uint16_t hid_control_point_value_handle; 87*0235c9e5SMilanka Ringwald // uint16_t hid_control_point_client_configuration_descriptor_handle; 88*0235c9e5SMilanka Ringwald uint8_t hid_control_point_suspend; 89*0235c9e5SMilanka Ringwald // btstack_context_callback_registration_t control_point_callback; 90*0235c9e5SMilanka Ringwald 91*0235c9e5SMilanka Ringwald btstack_context_callback_registration_t battery_callback; 92*0235c9e5SMilanka Ringwald } hids_device_t; 93*0235c9e5SMilanka Ringwald 94*0235c9e5SMilanka Ringwald static hids_device_t hids_device; 95*0235c9e5SMilanka Ringwald 96a4bfc4feSMatthias Ringwald static btstack_packet_handler_t packet_handler; 97a4bfc4feSMatthias Ringwald static att_service_handler_t hid_service; 98a4bfc4feSMatthias Ringwald 99*0235c9e5SMilanka Ringwald // TODO: store hids device connection into list 100*0235c9e5SMilanka Ringwald static hids_device_t * hids_device_get_instance_for_con_handle(uint16_t con_handle){ 101*0235c9e5SMilanka Ringwald UNUSED(con_handle); 102*0235c9e5SMilanka Ringwald return &hids_device; 103*0235c9e5SMilanka Ringwald } 104*0235c9e5SMilanka Ringwald 105*0235c9e5SMilanka Ringwald static hids_device_t * hids_device_create_instance(void){ 106*0235c9e5SMilanka Ringwald return &hids_device; 107*0235c9e5SMilanka Ringwald } 108*0235c9e5SMilanka Ringwald 109*0235c9e5SMilanka Ringwald // static int hids_device_delete_instance(void){ 110*0235c9e5SMilanka Ringwald // return 0; 111*0235c9e5SMilanka Ringwald // } 112*0235c9e5SMilanka Ringwald 113a4bfc4feSMatthias Ringwald static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value){ 114*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 115*0235c9e5SMilanka Ringwald if (!instance){ 116*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 117*0235c9e5SMilanka Ringwald return; 118*0235c9e5SMilanka Ringwald } 119*0235c9e5SMilanka Ringwald 120a4bfc4feSMatthias Ringwald if (!packet_handler) return; 121a4bfc4feSMatthias Ringwald uint8_t buffer[6]; 122a4bfc4feSMatthias Ringwald buffer[0] = HCI_EVENT_HIDS_META; 123a4bfc4feSMatthias Ringwald buffer[1] = 4; 124a4bfc4feSMatthias Ringwald buffer[2] = event; 125a4bfc4feSMatthias Ringwald little_endian_store_16(buffer, 3, (uint16_t) con_handle); 126a4bfc4feSMatthias Ringwald buffer[5] = value; 127a4bfc4feSMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 128a4bfc4feSMatthias Ringwald } 129a4bfc4feSMatthias Ringwald 130*0235c9e5SMilanka Ringwald static void hids_device_emit_event(uint8_t event, hci_con_handle_t con_handle){ 131*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 132*0235c9e5SMilanka Ringwald if (!instance){ 133*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 134*0235c9e5SMilanka Ringwald return; 135*0235c9e5SMilanka Ringwald } 136*0235c9e5SMilanka Ringwald 137*0235c9e5SMilanka Ringwald if (!packet_handler) return; 138*0235c9e5SMilanka Ringwald uint8_t buffer[5]; 139*0235c9e5SMilanka Ringwald buffer[0] = HCI_EVENT_HIDS_META; 140*0235c9e5SMilanka Ringwald buffer[1] = 4; 141*0235c9e5SMilanka Ringwald buffer[2] = event; 142*0235c9e5SMilanka Ringwald little_endian_store_16(buffer, 3, (uint16_t) con_handle); 143*0235c9e5SMilanka Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 144*0235c9e5SMilanka Ringwald } 145*0235c9e5SMilanka Ringwald 146a4bfc4feSMatthias Ringwald static void hids_device_can_send_now(void * context){ 147a4bfc4feSMatthias Ringwald hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context; 148a4bfc4feSMatthias Ringwald // notify client 149*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 150*0235c9e5SMilanka Ringwald if (!instance){ 151*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 152*0235c9e5SMilanka Ringwald return; 153*0235c9e5SMilanka Ringwald } 154*0235c9e5SMilanka Ringwald 155a4bfc4feSMatthias Ringwald if (!packet_handler) return; 156a4bfc4feSMatthias Ringwald uint8_t buffer[5]; 157a4bfc4feSMatthias Ringwald buffer[0] = HCI_EVENT_HIDS_META; 158a4bfc4feSMatthias Ringwald buffer[1] = 3; 159a4bfc4feSMatthias Ringwald buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW; 160a4bfc4feSMatthias Ringwald little_endian_store_16(buffer, 3, (uint16_t) con_handle); 161a4bfc4feSMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 162a4bfc4feSMatthias Ringwald } 163a4bfc4feSMatthias Ringwald 164a4bfc4feSMatthias Ringwald // ATT Client Read Callback for Dynamic Data 165a4bfc4feSMatthias Ringwald // - if buffer == NULL, don't copy data, just return size of value 166a4bfc4feSMatthias Ringwald // - if buffer != NULL, copy data and return number bytes copied 167*0235c9e5SMilanka Ringwald static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ 168*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 169*0235c9e5SMilanka Ringwald if (!instance){ 170*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 171*0235c9e5SMilanka Ringwald return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS; 172a4bfc4feSMatthias Ringwald } 173*0235c9e5SMilanka Ringwald 174*0235c9e5SMilanka Ringwald printf("att_read_callback att handle 0x%02x\n", att_handle); 175*0235c9e5SMilanka Ringwald 176*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_protocol_mode_value_handle){ 177*0235c9e5SMilanka Ringwald log_info("Read protocol mode"); 178*0235c9e5SMilanka Ringwald return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size); 179*0235c9e5SMilanka Ringwald } 180*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_map_handle){ 181523edd21SMatthias Ringwald log_info("Read report map"); 182*0235c9e5SMilanka Ringwald return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size); 183a4bfc4feSMatthias Ringwald } 184a4bfc4feSMatthias Ringwald // if (att_handle == hid_boot_mouse_input_value_handle){ 185a4bfc4feSMatthias Ringwald // } 186*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){ 187*0235c9e5SMilanka Ringwald return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size); 188a4bfc4feSMatthias Ringwald } 189a4bfc4feSMatthias Ringwald // if (att_handle == hid_boot_keyboard_input_value_handle){ 190a4bfc4feSMatthias Ringwald // } 191*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){ 192*0235c9e5SMilanka Ringwald return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size); 193a4bfc4feSMatthias Ringwald } 194a4bfc4feSMatthias Ringwald // if (att_handle == hid_report_input_value_handle){ 195a4bfc4feSMatthias Ringwald // } 196*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_input_client_configuration_handle){ 197*0235c9e5SMilanka Ringwald return att_read_callback_handle_little_endian_16(instance->hid_report_input_client_configuration_value, offset, buffer, buffer_size); 198*0235c9e5SMilanka Ringwald } 199*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_input_client_configuration_handle){ 200*0235c9e5SMilanka Ringwald return att_read_callback_handle_little_endian_16(instance->hid_report_output_client_configuration_value, offset, buffer, buffer_size); 201*0235c9e5SMilanka Ringwald } 202*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_input_client_configuration_handle){ 203*0235c9e5SMilanka Ringwald return att_read_callback_handle_little_endian_16(instance->hid_report_feature_client_configuration_value, offset, buffer, buffer_size); 204*0235c9e5SMilanka Ringwald } 205*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_control_point_value_handle){ 206*0235c9e5SMilanka Ringwald if (buffer && buffer_size >= 1){ 207*0235c9e5SMilanka Ringwald buffer[0] = instance->hid_control_point_suspend; 208*0235c9e5SMilanka Ringwald } 209*0235c9e5SMilanka Ringwald return 1; 210a4bfc4feSMatthias Ringwald } 211a4bfc4feSMatthias Ringwald return 0; 212a4bfc4feSMatthias Ringwald } 213a4bfc4feSMatthias Ringwald 214a4bfc4feSMatthias Ringwald static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ 215a4bfc4feSMatthias Ringwald UNUSED(transaction_mode); 216a4bfc4feSMatthias Ringwald UNUSED(buffer_size); 217a4bfc4feSMatthias Ringwald UNUSED(offset); 218a4bfc4feSMatthias Ringwald 219*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 220*0235c9e5SMilanka Ringwald if (!instance){ 221*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 222*0235c9e5SMilanka Ringwald return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS; 223*0235c9e5SMilanka Ringwald } 224*0235c9e5SMilanka Ringwald printf("att_write_callback att handle 0x%02x\n", att_handle); 225*0235c9e5SMilanka Ringwald 226*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){ 22785828295SMatthias Ringwald uint16_t new_value = little_endian_read_16(buffer, 0); 2289ff89460SMatthias Ringwald // if (new_value == hid_boot_mouse_input_client_configuration_value) return 0; 229*0235c9e5SMilanka Ringwald instance->hid_boot_mouse_input_client_configuration_value = new_value; 230*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, new_value); 231a4bfc4feSMatthias Ringwald } 232*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){ 23385828295SMatthias Ringwald uint16_t new_value = little_endian_read_16(buffer, 0); 2349ff89460SMatthias Ringwald // if (new_value == hid_boot_keyboard_input_client_configuration_value) return 0; 235*0235c9e5SMilanka Ringwald instance->hid_boot_keyboard_input_client_configuration_value = new_value; 236*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, new_value); 237a4bfc4feSMatthias Ringwald } 238*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_input_client_configuration_handle){ 23985828295SMatthias Ringwald uint16_t new_value = little_endian_read_16(buffer, 0); 2409ff89460SMatthias Ringwald // if (new_value == hid_report_input_client_configuration_value) return 0; 241*0235c9e5SMilanka Ringwald instance->hid_report_input_client_configuration_value = new_value; 24285828295SMatthias Ringwald log_info("Enable Report Input notifications: %x", new_value); 243*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, new_value); 244a4bfc4feSMatthias Ringwald } 245*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_output_client_configuration_handle){ 246*0235c9e5SMilanka Ringwald uint16_t new_value = little_endian_read_16(buffer, 0); 247*0235c9e5SMilanka Ringwald // if (new_value == hid_report_output_client_configuration_value) return 0; 248*0235c9e5SMilanka Ringwald instance->hid_report_output_client_configuration_value = new_value; 249*0235c9e5SMilanka Ringwald log_info("Enable Report Output notifications: %x", new_value); 250*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, new_value); 251a4bfc4feSMatthias Ringwald } 252*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_report_feature_client_configuration_handle){ 253*0235c9e5SMilanka Ringwald uint16_t new_value = little_endian_read_16(buffer, 0); 254*0235c9e5SMilanka Ringwald // if (new_value == hid_report_feature_client_configuration_value) return 0; 255*0235c9e5SMilanka Ringwald instance->hid_report_feature_client_configuration_value = new_value; 256*0235c9e5SMilanka Ringwald log_info("Enable Report Feature notifications: %x", new_value); 257*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, new_value); 258*0235c9e5SMilanka Ringwald } 259*0235c9e5SMilanka Ringwald 260*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_protocol_mode_value_handle){ 261*0235c9e5SMilanka Ringwald instance->hid_protocol_mode = buffer[0]; 262*0235c9e5SMilanka Ringwald log_info("Set protocol mode: %u", instance->hid_protocol_mode); 263*0235c9e5SMilanka Ringwald hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode); 264*0235c9e5SMilanka Ringwald } 265*0235c9e5SMilanka Ringwald 266*0235c9e5SMilanka Ringwald if (att_handle == instance->hid_control_point_value_handle){ 267*0235c9e5SMilanka Ringwald if (buffer_size < 1){ 268*0235c9e5SMilanka Ringwald return ATT_ERROR_INVALID_OFFSET; 269*0235c9e5SMilanka Ringwald } 270*0235c9e5SMilanka Ringwald instance->hid_control_point_suspend = buffer[0]; 271*0235c9e5SMilanka Ringwald instance->con_handle = con_handle; 272*0235c9e5SMilanka Ringwald log_info("Set suspend tp: %u", instance->hid_control_point_suspend ); 273*0235c9e5SMilanka Ringwald if (instance->hid_control_point_suspend == 0){ 274*0235c9e5SMilanka Ringwald hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle); 275*0235c9e5SMilanka Ringwald } else if (instance->hid_control_point_suspend == 1){ 276*0235c9e5SMilanka Ringwald hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle); 277*0235c9e5SMilanka Ringwald } 278*0235c9e5SMilanka Ringwald // } else { 279*0235c9e5SMilanka Ringwald // return ATT_ERROR_INAPPROPRIATE_CONNECTION_PARAMETERS; 280*0235c9e5SMilanka Ringwald // } 281*0235c9e5SMilanka Ringwald } 282*0235c9e5SMilanka Ringwald 283a4bfc4feSMatthias Ringwald return 0; 284a4bfc4feSMatthias Ringwald } 285a4bfc4feSMatthias Ringwald 286a4bfc4feSMatthias Ringwald /** 287a4bfc4feSMatthias Ringwald * @brief Set up HIDS Device 288a4bfc4feSMatthias Ringwald */ 289a4bfc4feSMatthias Ringwald void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){ 290*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_create_instance(); 291*0235c9e5SMilanka Ringwald if (!instance){ 292*0235c9e5SMilanka Ringwald log_error("hids_device_init: instance could not be created, not enough memory"); 293*0235c9e5SMilanka Ringwald return; 294*0235c9e5SMilanka Ringwald } 295a4bfc4feSMatthias Ringwald 296*0235c9e5SMilanka Ringwald instance->hid_country_code = country_code; 297*0235c9e5SMilanka Ringwald instance->hid_descriptor = descriptor; 298*0235c9e5SMilanka Ringwald instance->hid_descriptor_size = descriptor_size; 299a4bfc4feSMatthias Ringwald 300a4bfc4feSMatthias Ringwald // default 301*0235c9e5SMilanka Ringwald instance->hid_protocol_mode = 1; 302a4bfc4feSMatthias Ringwald 303a4bfc4feSMatthias Ringwald // get service handle range 304a4bfc4feSMatthias Ringwald uint16_t start_handle = 0; 305a4bfc4feSMatthias Ringwald uint16_t end_handle = 0xfff; 306a4bfc4feSMatthias Ringwald int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle); 307a4bfc4feSMatthias Ringwald if (!service_found) return; 308a4bfc4feSMatthias Ringwald 309a4bfc4feSMatthias Ringwald // get report map handle 310*0235c9e5SMilanka Ringwald instance->hid_report_map_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP); 311a4bfc4feSMatthias Ringwald 312a4bfc4feSMatthias Ringwald // get report map handle 313*0235c9e5SMilanka Ringwald instance->hid_protocol_mode_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE); 314a4bfc4feSMatthias Ringwald 315a4bfc4feSMatthias Ringwald // get value and client configuration handles for boot mouse input, boot keyboard input and report input 316*0235c9e5SMilanka Ringwald instance->hid_boot_mouse_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT); 317*0235c9e5SMilanka Ringwald instance->hid_boot_mouse_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT); 318a4bfc4feSMatthias Ringwald 319*0235c9e5SMilanka Ringwald instance->hid_boot_keyboard_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT); 320*0235c9e5SMilanka Ringwald instance->hid_boot_keyboard_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT); 321a4bfc4feSMatthias Ringwald 322*0235c9e5SMilanka Ringwald instance->hid_report_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 323*0235c9e5SMilanka Ringwald instance->hid_report_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 324*0235c9e5SMilanka Ringwald 325*0235c9e5SMilanka Ringwald instance->hid_report_output_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(instance->hid_report_input_client_configuration_handle+1, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 326*0235c9e5SMilanka Ringwald instance->hid_report_output_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(instance->hid_report_input_client_configuration_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 327*0235c9e5SMilanka Ringwald 328*0235c9e5SMilanka Ringwald instance->hid_report_feature_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(instance->hid_report_output_client_configuration_handle+1, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 329*0235c9e5SMilanka Ringwald instance->hid_report_feature_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(instance->hid_report_output_client_configuration_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 330*0235c9e5SMilanka Ringwald 331*0235c9e5SMilanka Ringwald instance->hid_control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT); 332*0235c9e5SMilanka Ringwald 333*0235c9e5SMilanka Ringwald printf("hid_report_map_handle 0x%02x\n", instance->hid_report_map_handle); 334*0235c9e5SMilanka Ringwald printf("hid_protocol_mode_value_handle 0x%02x\n", instance->hid_protocol_mode_value_handle); 335*0235c9e5SMilanka Ringwald printf("hid_boot_mouse_input_value_handle 0x%02x\n", instance->hid_boot_mouse_input_value_handle); 336*0235c9e5SMilanka Ringwald printf("hid_boot_mouse_input_client_configuration_handle 0x%02x\n", instance->hid_boot_mouse_input_client_configuration_handle); 337*0235c9e5SMilanka Ringwald printf("\n"); 338*0235c9e5SMilanka Ringwald printf("hid_boot_keyboard_input_value_handle 0x%02x\n", instance->hid_boot_keyboard_input_value_handle); 339*0235c9e5SMilanka Ringwald printf("hid_boot_keyboard_input_client_configuration_handle 0x%02x\n", instance->hid_boot_keyboard_input_client_configuration_handle); 340*0235c9e5SMilanka Ringwald printf("\n"); 341*0235c9e5SMilanka Ringwald printf("hid_report_input_value_handle 0x%02x\n", instance->hid_report_input_value_handle); 342*0235c9e5SMilanka Ringwald printf("hid_report_input_client_configuration_handle 0x%02x\n", instance->hid_report_input_client_configuration_handle); 343*0235c9e5SMilanka Ringwald printf("\n"); 344*0235c9e5SMilanka Ringwald printf("hid_report_output_value_handle 0x%02x\n", instance->hid_report_output_value_handle); 345*0235c9e5SMilanka Ringwald printf("hid_report_output_client_configuration_handle 0x%02x\n", instance->hid_report_output_client_configuration_handle); 346*0235c9e5SMilanka Ringwald printf("\n"); 347*0235c9e5SMilanka Ringwald printf("hid_report_feature_value_handle 0x%02x\n", instance->hid_report_feature_value_handle); 348*0235c9e5SMilanka Ringwald printf("hid_report_feature_client_configuration_handle 0x%02x\n", instance->hid_report_feature_client_configuration_handle); 349*0235c9e5SMilanka Ringwald 350*0235c9e5SMilanka Ringwald printf("hid_control_point_value_handle 0x%02x\n", instance->hid_control_point_value_handle); 351a4bfc4feSMatthias Ringwald 3523d71c7a4SMatthias Ringwald // register service with ATT Server 353a4bfc4feSMatthias Ringwald hid_service.start_handle = start_handle; 354a4bfc4feSMatthias Ringwald hid_service.end_handle = end_handle; 355a4bfc4feSMatthias Ringwald hid_service.read_callback = &att_read_callback; 356a4bfc4feSMatthias Ringwald hid_service.write_callback = &att_write_callback; 3573d71c7a4SMatthias Ringwald att_server_register_service_handler(&hid_service); 358a4bfc4feSMatthias Ringwald } 359a4bfc4feSMatthias Ringwald 360a4bfc4feSMatthias Ringwald /** 361a4bfc4feSMatthias Ringwald * @brief Register callback for the HIDS Device client. 362a4bfc4feSMatthias Ringwald * @param callback 363a4bfc4feSMatthias Ringwald */ 364a4bfc4feSMatthias Ringwald void hids_device_register_packet_handler(btstack_packet_handler_t callback){ 365a4bfc4feSMatthias Ringwald packet_handler = callback; 366a4bfc4feSMatthias Ringwald } 367a4bfc4feSMatthias Ringwald 368a4bfc4feSMatthias Ringwald /** 369a4bfc4feSMatthias Ringwald * @brief Request can send now event to send HID Report 370a4bfc4feSMatthias Ringwald * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent 371a4bfc4feSMatthias Ringwald * @param hid_cid 372a4bfc4feSMatthias Ringwald */ 373a4bfc4feSMatthias Ringwald void hids_device_request_can_send_now_event(hci_con_handle_t con_handle){ 374*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 375*0235c9e5SMilanka Ringwald if (!instance){ 376*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 377*0235c9e5SMilanka Ringwald return; 378*0235c9e5SMilanka Ringwald } 379*0235c9e5SMilanka Ringwald 380*0235c9e5SMilanka Ringwald instance->battery_callback.callback = &hids_device_can_send_now; 381*0235c9e5SMilanka Ringwald instance->battery_callback.context = (void*) (uintptr_t) con_handle; 382*0235c9e5SMilanka Ringwald att_server_register_can_send_now_callback(&instance->battery_callback, con_handle); 383a4bfc4feSMatthias Ringwald } 384a4bfc4feSMatthias Ringwald 385a4bfc4feSMatthias Ringwald /** 386a4bfc4feSMatthias Ringwald * @brief Send HID Report: Input 387a4bfc4feSMatthias Ringwald */ 388a4bfc4feSMatthias Ringwald void hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 389*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 390*0235c9e5SMilanka Ringwald if (!instance){ 391*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 392*0235c9e5SMilanka Ringwald return; 393*0235c9e5SMilanka Ringwald } 394*0235c9e5SMilanka Ringwald att_server_notify(con_handle, instance->hid_report_input_value_handle, (uint8_t*) report, report_len); 395*0235c9e5SMilanka Ringwald } 396*0235c9e5SMilanka Ringwald 397*0235c9e5SMilanka Ringwald /** 398*0235c9e5SMilanka Ringwald * @brief Send HID Report: Output 399*0235c9e5SMilanka Ringwald */ 400*0235c9e5SMilanka Ringwald void hids_device_send_output_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 401*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 402*0235c9e5SMilanka Ringwald if (!instance){ 403*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 404*0235c9e5SMilanka Ringwald return; 405*0235c9e5SMilanka Ringwald } 406*0235c9e5SMilanka Ringwald att_server_notify(con_handle, instance->hid_report_output_value_handle, (uint8_t*) report, report_len); 407*0235c9e5SMilanka Ringwald } 408*0235c9e5SMilanka Ringwald 409*0235c9e5SMilanka Ringwald /** 410*0235c9e5SMilanka Ringwald * @brief Send HID Report: Feature 411*0235c9e5SMilanka Ringwald */ 412*0235c9e5SMilanka Ringwald void hids_device_send_feature_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 413*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 414*0235c9e5SMilanka Ringwald if (!instance){ 415*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 416*0235c9e5SMilanka Ringwald return; 417*0235c9e5SMilanka Ringwald } 418*0235c9e5SMilanka Ringwald att_server_notify(con_handle, instance->hid_report_feature_value_handle, (uint8_t*) report, report_len); 419a4bfc4feSMatthias Ringwald } 420a4bfc4feSMatthias Ringwald 421a4bfc4feSMatthias Ringwald /** 422a4bfc4feSMatthias Ringwald * @brief Send HID Boot Mouse Input Report 423a4bfc4feSMatthias Ringwald */ 424a4bfc4feSMatthias Ringwald void hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 425*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 426*0235c9e5SMilanka Ringwald if (!instance){ 427*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 428*0235c9e5SMilanka Ringwald return; 429*0235c9e5SMilanka Ringwald } 430*0235c9e5SMilanka Ringwald att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, (uint8_t*) report, report_len); 431a4bfc4feSMatthias Ringwald } 432a4bfc4feSMatthias Ringwald 433a4bfc4feSMatthias Ringwald /** 434a4bfc4feSMatthias Ringwald * @brief Send HID Boot Mouse Input Report 435a4bfc4feSMatthias Ringwald */ 436a4bfc4feSMatthias Ringwald void hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 437*0235c9e5SMilanka Ringwald hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 438*0235c9e5SMilanka Ringwald if (!instance){ 439*0235c9e5SMilanka Ringwald log_error("no instance for handle 0x%02x", con_handle); 440*0235c9e5SMilanka Ringwald return; 441*0235c9e5SMilanka Ringwald } 442*0235c9e5SMilanka Ringwald att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, (uint8_t*) report, report_len); 443a4bfc4feSMatthias Ringwald } 444