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