1 /* 2 * Copyright (C) 2021 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_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <string.h> 44 45 #include "ble/gatt-service/hids_client.h" 46 47 #include "btstack_memory.h" 48 #include "ble/att_db.h" 49 #include "ble/core.h" 50 #include "ble/gatt_client.h" 51 #include "ble/sm.h" 52 #include "bluetooth_gatt.h" 53 #include "btstack_debug.h" 54 #include "btstack_event.h" 55 #include "btstack_run_loop.h" 56 #include "gap.h" 57 58 static btstack_linked_list_t clients; 59 static uint16_t hids_cid_counter = 0; 60 61 #define HID_REPORT_MODE_REPORT_ID 3 62 #define HID_REPORT_MODE_REPORT_MAP_ID 4 63 #define HID_REPORT_MODE_HID_INFORMATION_ID 5 64 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID 6 65 66 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 67 68 static uint16_t hids_get_next_cid(void){ 69 if (hids_cid_counter == 0xffff) { 70 hids_cid_counter = 1; 71 } else { 72 hids_cid_counter++; 73 } 74 return hids_cid_counter; 75 } 76 77 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){ 78 uint8_t i; 79 for (i = 0; i < client->num_reports; i++){ 80 if (client->reports[i].value_handle == value_handle){ 81 return i; 82 } 83 } 84 return HIDS_CLIENT_INVALID_REPORT_INDEX; 85 } 86 87 static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){ 88 uint8_t i; 89 for (i = 0; i < client->num_reports; i++){ 90 if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){ 91 return i; 92 } 93 } 94 return HIDS_CLIENT_INVALID_REPORT_INDEX; 95 } 96 97 static hids_client_report_t * find_report_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){ 98 uint8_t i; 99 for (i = 0; i < client->num_reports; i++){ 100 if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){ 101 return &client->reports[i]; 102 } 103 } 104 return NULL; 105 } 106 107 static hids_client_report_t * get_boot_mouse_input_report(hids_client_t * client){ 108 return find_report_for_report_id_and_type(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT); 109 } 110 static hids_client_report_t * get_boot_keyboard_input_report(hids_client_t * client){ 111 return find_report_for_report_id_and_type(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT); 112 } 113 114 static void hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type){ 115 uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle); 116 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 117 return; 118 } 119 120 if (client->active_index < HIDS_CLIENT_NUM_REPORTS) { 121 client->reports[client->active_index].value_handle = characteristic->value_handle; 122 client->reports[client->active_index].end_handle = characteristic->end_handle; 123 client->reports[client->active_index].properties = characteristic->properties; 124 125 client->reports[client->active_index].service_index = client->service_index; 126 client->reports[client->active_index].report_id = report_id; 127 client->reports[client->active_index].report_type = report_type; 128 129 // printf("add index %d, id %d, type %d, value handle 0x%02x\n", client->active_index, report_id, report_type, characteristic->value_handle); 130 client->active_index++; 131 client->num_reports++; 132 } else { 133 log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS"); 134 } 135 } 136 137 static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){ 138 characteristic->value_handle = client->reports[report_index].value_handle; 139 characteristic->end_handle = client->reports[report_index].end_handle; 140 characteristic->properties = client->reports[report_index].properties; 141 } 142 143 static uint8_t hids_client_get_characteristic(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type, gatt_client_characteristic_t * characteristic){ 144 uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type); 145 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){ 146 return report_index; 147 } 148 hids_client_get_characteristic_for_report_index(client, report_index, characteristic); 149 return report_index; 150 } 151 152 153 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){ 154 uint8_t i; 155 uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX; 156 for (i = client->active_index; i < client->num_reports; i++){ 157 hids_client_report_t report = client->reports[i]; 158 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){ 159 index = i; 160 break; 161 } 162 } 163 client->active_index = index; 164 return index; 165 } 166 167 static bool hids_client_report_query_next_report_map(hids_client_t * client){ 168 client->active_index++; 169 if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){ 170 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS; 171 return true; 172 } 173 return false; 174 } 175 176 static bool hids_client_report_map_query_init(hids_client_t * client){ 177 client->service_index = 0; 178 client->active_index = 0; 179 180 if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){ 181 client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_CHARACTERISTIC_VALUE; 182 return true; 183 } 184 return false; 185 } 186 187 static uint8_t hids_client_get_next_report_index(hids_client_t * client){ 188 uint8_t i; 189 uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX; 190 for (i = client->active_index; i < client->num_reports; i++){ 191 hids_client_report_t report = client->reports[i]; 192 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){ 193 index = i; 194 break; 195 } 196 } 197 client->active_index = index; 198 return index; 199 } 200 201 static bool hids_client_report_query_next_report(hids_client_t * client){ 202 client->active_index++; 203 if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){ 204 client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS; 205 return true; 206 } 207 return false; 208 } 209 210 static bool hids_client_report_query_init(hids_client_t * client){ 211 client->service_index = 0; 212 client->active_index = 0; 213 214 if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){ 215 client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS; 216 return true; 217 } 218 return false; 219 } 220 221 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){ 222 hids_client_t * client = btstack_memory_hids_client_get(); 223 if (!client){ 224 log_error("Not enough memory to create client"); 225 return NULL; 226 } 227 client->state = HIDS_CLIENT_STATE_IDLE; 228 client->cid = cid; 229 client->con_handle = con_handle; 230 231 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 232 return client; 233 } 234 235 static void hids_finalize_client(hids_client_t * client){ 236 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 237 btstack_memory_hids_client_free(client); 238 } 239 240 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){ 241 btstack_linked_list_iterator_t it; 242 btstack_linked_list_iterator_init(&it, &clients); 243 while (btstack_linked_list_iterator_has_next(&it)){ 244 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 245 if (client->con_handle != con_handle) continue; 246 return client; 247 } 248 return NULL; 249 } 250 251 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){ 252 btstack_linked_list_iterator_t it; 253 btstack_linked_list_iterator_init(&it, &clients); 254 while (btstack_linked_list_iterator_has_next(&it)){ 255 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 256 if (client->cid != hids_cid) continue; 257 return client; 258 } 259 return NULL; 260 } 261 262 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){ 263 uint8_t event[8]; 264 int pos = 0; 265 event[pos++] = HCI_EVENT_GATTSERVICE_META; 266 event[pos++] = sizeof(event) - 2; 267 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED; 268 little_endian_store_16(event, pos, client->cid); 269 pos += 2; 270 event[pos++] = status; 271 event[pos++] = client->protocol_mode; 272 event[pos++] = client->num_instances; 273 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 274 } 275 276 277 static void hids_run_for_client(hids_client_t * client){ 278 uint8_t att_status; 279 gatt_client_service_t service; 280 gatt_client_characteristic_t characteristic; 281 uint8_t report_index; 282 283 switch (client->state){ 284 case HIDS_CLIENT_STATE_W2_QUERY_SERVICE: 285 client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT; 286 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 287 // TODO handle status 288 UNUSED(att_status); 289 break; 290 291 case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 292 client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 293 294 service.start_group_handle = client->services[client->service_index].start_handle; 295 service.end_group_handle = client->services[client->service_index].end_handle; 296 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service); 297 298 UNUSED(att_status); 299 break; 300 301 case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD: 302 client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED; 303 304 report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic); 305 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 306 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 307 } 308 309 if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){ 310 client->reports[report_index].value_handle = 0; 311 client->state = HIDS_CLIENT_STATE_CONNECTED; 312 } 313 break; 314 315 case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE: 316 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 317 318 report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic); 319 320 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 321 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 322 } 323 324 if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){ 325 client->reports[report_index].value_handle = 0; 326 client->state = HIDS_CLIENT_STATE_CONNECTED; 327 } 328 329 break; 330 331 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE: 332 client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE; 333 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode); 334 UNUSED(att_status); 335 336 client->protocol_mode = client->required_protocol_mode; 337 client->state = HIDS_CLIENT_STATE_CONNECTED; 338 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 339 break; 340 341 case HIDS_CLIENT_W2_SEND_REPORT:{ 342 client->state = HIDS_CLIENT_STATE_CONNECTED; 343 344 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, 345 client->reports[client->active_index].value_handle, 346 client->report_len, (uint8_t *)client->report); 347 UNUSED(att_status); 348 break; 349 } 350 351 case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_CHARACTERISTIC_VALUE: 352 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_VALUE_RESULT; 353 354 att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle); 355 UNUSED(att_status); 356 break; 357 358 case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS: 359 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT; 360 client->descriptor_handle = 0; 361 362 hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic); 363 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 364 UNUSED(att_status); 365 break; 366 367 case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_CHARACTERISTIC_DESCRIPTOR_VALUE: 368 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT; 369 370 att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle); 371 client->descriptor_handle = 0; 372 UNUSED(att_status); 373 break; 374 375 case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC: 376 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT; 377 // printf("discover external uuid 0x%02x\n", client->reports[client->active_index].external_report_reference_uuid); 378 379 att_status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&handle_gatt_client_event, client->con_handle, 0, 0xffff, 380 client->reports[client->active_index].external_report_reference_uuid); 381 UNUSED(att_status); 382 break; 383 384 case HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS: 385 client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT; 386 client->descriptor_handle = 0; 387 hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic); 388 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 389 UNUSED(att_status); 390 break; 391 392 case HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE: 393 client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT; 394 395 att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle); 396 client->descriptor_handle = 0; 397 UNUSED(att_status); 398 break; 399 400 default: 401 break; 402 } 403 } 404 405 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){ 406 uint16_t pos = 0; 407 buffer[pos++] = HCI_EVENT_GATTSERVICE_META; 408 pos++; // skip len 409 buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT; 410 little_endian_store_16(buffer, pos, client->cid); 411 pos += 2; 412 buffer[pos++] = report_id; 413 little_endian_store_16(buffer, pos, report_len); 414 pos += 2; 415 buffer[1] = pos + report_len - 2; 416 } 417 418 static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 419 UNUSED(packet_type); 420 UNUSED(channel); 421 UNUSED(size); 422 423 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 424 425 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 426 btstack_assert(client != NULL); 427 428 uint8_t * in_place_event = packet; 429 hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 430 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 431 } 432 433 static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 434 UNUSED(packet_type); 435 UNUSED(channel); 436 UNUSED(size); 437 438 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 439 440 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 441 btstack_assert(client != NULL); 442 443 uint8_t * in_place_event = packet; 444 hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 445 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 446 } 447 448 449 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 450 UNUSED(packet_type); 451 UNUSED(channel); 452 UNUSED(size); 453 454 hids_client_t * client = NULL; 455 uint8_t att_status; 456 gatt_client_service_t service; 457 gatt_client_characteristic_t characteristic; 458 gatt_client_characteristic_descriptor_t characteristic_descriptor; 459 460 hids_client_report_t * boot_keyboard_report; 461 hids_client_report_t * boot_mouse_report; 462 const uint8_t * characteristic_descriptor_value; 463 464 switch(hci_event_packet_get_type(packet)){ 465 case GATT_EVENT_SERVICE_QUERY_RESULT: 466 client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 467 btstack_assert(client != NULL); 468 469 if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) { 470 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 471 hids_finalize_client(client); 472 break; 473 } 474 475 if (client->num_instances < MAX_NUM_HID_SERVICES){ 476 gatt_event_service_query_result_get_service(packet, &service); 477 client->services[client->num_instances].start_handle = service.start_group_handle; 478 client->services[client->num_instances].end_handle = service.end_group_handle; 479 } 480 client->num_instances++; 481 break; 482 483 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 484 client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 485 btstack_assert(client != NULL); 486 487 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 488 switch (characteristic.uuid16){ 489 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE: 490 client->protocol_mode_value_handle = characteristic.value_handle; 491 break; 492 493 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT: 494 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT); 495 break; 496 497 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT: 498 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT); 499 break; 500 501 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT: 502 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT); 503 break; 504 505 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT: 506 // printf("ORG_BLUETOOTH_CHARACTERISTIC_REPORT\n"); 507 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED); 508 break; 509 510 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP: 511 // printf("ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP\n"); 512 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED); 513 break; 514 515 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION: 516 // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n"); 517 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT); 518 break; 519 520 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT: 521 // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n"); 522 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT); 523 break; 524 default: 525 break; 526 } 527 break; 528 529 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 530 // Map Report characteristic value == HID Descriptor 531 client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet)); 532 btstack_assert(client != NULL); 533 // TODO get value and store it 534 break; 535 536 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 537 client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet)); 538 btstack_assert(client != NULL); 539 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 540 541 switch (client->state) { 542 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT: 543 // setup for descriptor value query 544 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){ 545 client->descriptor_handle = characteristic_descriptor.handle; 546 } 547 break; 548 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT: 549 // setup for descriptor value query 550 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){ 551 client->descriptor_handle = characteristic_descriptor.handle; 552 } 553 break; 554 default: 555 break; 556 } 557 break; 558 559 case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT: 560 client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet)); 561 btstack_assert(client != NULL); 562 563 if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){ 564 break; 565 } 566 567 characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet); 568 569 switch (client->state) { 570 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT: 571 // get external report characteristic uuid 572 client->reports[client->active_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0); 573 // printf("add external report characteristic uuid 0x%02x\n", client->reports[client->active_index].external_report_reference_uuid); 574 break; 575 576 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT: 577 client->reports[client->active_index].report_id = characteristic_descriptor_value[0]; 578 client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1]; 579 // printf("update index %d, id %d, type %d, value handle 0x%02x\n", 580 // client->active_index, 581 // client->reports[client->active_index].report_id, 582 // client->reports[client->active_index].report_type, 583 // client->reports[client->active_index].value_handle); 584 585 break; 586 587 default: 588 break; 589 } 590 break; 591 592 case GATT_EVENT_QUERY_COMPLETE: 593 client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 594 btstack_assert(client != NULL); 595 596 att_status = gatt_event_query_complete_get_att_status(packet); 597 598 switch (client->state){ 599 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT: 600 if (att_status != ATT_ERROR_SUCCESS){ 601 hids_emit_connection_established(client, att_status); 602 hids_finalize_client(client); 603 break; 604 } 605 606 if (client->num_instances == 0){ 607 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 608 hids_finalize_client(client); 609 break; 610 } 611 612 if (client->num_instances > MAX_NUM_HID_SERVICES) { 613 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES); 614 client->num_instances = MAX_NUM_HID_SERVICES; 615 } 616 617 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 618 client->service_index = 0; 619 break; 620 621 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 622 if (att_status != ATT_ERROR_SUCCESS){ 623 hids_emit_connection_established(client, att_status); 624 hids_finalize_client(client); 625 break; 626 } 627 628 switch (client->required_protocol_mode){ 629 case HID_PROTOCOL_MODE_BOOT: 630 if (get_boot_keyboard_input_report(client) != NULL){ 631 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD; 632 break; 633 } 634 if (get_boot_mouse_input_report(client) != NULL){ 635 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 636 break; 637 } 638 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 639 hids_finalize_client(client); 640 break; 641 642 case HID_PROTOCOL_MODE_REPORT: 643 if ((client->service_index + 1) < client->num_instances){ 644 // discover characteristics of next service 645 client->service_index++; 646 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 647 } else { 648 // 1. we need to get HID Descriptor and 649 // 2. get external Report characteristics if referenced from Report Map 650 if (hids_client_report_map_query_init(client)){ 651 break; 652 } 653 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 654 hids_finalize_client(client); 655 } 656 break; 657 default: 658 break; 659 } 660 break; 661 662 663 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_VALUE_RESULT: 664 if (att_status != ATT_ERROR_SUCCESS){ 665 hids_emit_connection_established(client, att_status); 666 hids_finalize_client(client); 667 break; 668 } 669 // printf("HID Descriptor found, check for external Report IDs (read report map descriptor)\n"); 670 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS; 671 break; 672 673 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT: 674 if (client->descriptor_handle != 0){ 675 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_CHARACTERISTIC_DESCRIPTOR_VALUE; 676 break; 677 } 678 679 // go for next report map 680 if (hids_client_report_query_next_report_map(client)){ 681 break; 682 } 683 684 // discover characteristic descriptor for all Report characteristics, 685 // then read value of characteristic descriptor to get Report ID 686 if (hids_client_report_query_init(client)){ 687 break; 688 } 689 690 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 691 hids_finalize_client(client); 692 break; 693 694 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT: 695 if (client->descriptor_handle != 0){ 696 // descriptor found 697 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC; 698 break; 699 } 700 701 // go for next map report 702 if (hids_client_report_query_next_report_map(client)){ 703 break; 704 } 705 706 // discover characteristic descriptor for all Report characteristics, 707 // then read value of characteristic descriptor to get Report ID 708 if (hids_client_report_query_init(client)){ 709 break; 710 } 711 712 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 713 hids_finalize_client(client); 714 break; 715 716 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT: 717 // go for next map report 718 if (hids_client_report_query_next_report_map(client)){ 719 break; 720 } 721 722 // discover characteristic descriptor for all Report characteristics, 723 // then read value of characteristic descriptor to get Report ID 724 if (hids_client_report_query_init(client)){ 725 break; 726 } 727 728 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 729 hids_finalize_client(client); 730 break; 731 732 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT: 733 if (client->descriptor_handle != 0){ 734 // descriptor found 735 client->state = HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE; 736 break; 737 } 738 739 // go for next report 740 if (hids_client_report_query_next_report(client)){ 741 break; 742 } 743 744 // TODO continue with report mode 745 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 746 break; 747 748 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT: 749 // go for next report 750 if (hids_client_report_query_next_report(client)){ 751 break; 752 } 753 754 // TODO continue with report mode 755 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 756 break; 757 758 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 759 boot_keyboard_report = get_boot_keyboard_input_report(client); 760 if (boot_keyboard_report != NULL){ 761 // setup listener 762 characteristic.value_handle = boot_keyboard_report->value_handle; 763 gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic); 764 } 765 766 // check if there is mouse input report 767 boot_mouse_report = get_boot_mouse_input_report(client); 768 if (boot_mouse_report != NULL){ 769 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 770 break; 771 } 772 773 // if none of the reports is found bail out 774 if (!boot_mouse_report && !boot_keyboard_report){ 775 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 776 hids_finalize_client(client); 777 break; 778 } 779 780 // set protocol 781 if (client->protocol_mode_value_handle != 0){ 782 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 783 } else { 784 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 785 client->state = HIDS_CLIENT_STATE_CONNECTED; 786 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 787 } 788 hids_run_for_client(client); 789 break; 790 791 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED: 792 boot_mouse_report = get_boot_mouse_input_report(client); 793 if (boot_mouse_report != NULL){ 794 // setup listener 795 characteristic.value_handle = boot_mouse_report->value_handle; 796 gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic); 797 } 798 799 boot_keyboard_report = get_boot_keyboard_input_report(client); 800 if (!boot_mouse_report && !boot_keyboard_report){ 801 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 802 hids_finalize_client(client); 803 break; 804 } 805 806 if (client->protocol_mode_value_handle != 0){ 807 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 808 } else { 809 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 810 client->state = HIDS_CLIENT_STATE_CONNECTED; 811 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 812 } 813 break; 814 default: 815 break; 816 } 817 break; 818 819 default: 820 break; 821 } 822 823 if (client != NULL){ 824 hids_run_for_client(client); 825 } 826 } 827 828 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){ 829 btstack_assert(packet_handler != NULL); 830 831 hids_client_t * client = hids_get_client_for_con_handle(con_handle); 832 if (client != NULL){ 833 return ERROR_CODE_COMMAND_DISALLOWED; 834 } 835 836 uint16_t cid = hids_get_next_cid(); 837 if (hids_cid != NULL) { 838 *hids_cid = cid; 839 } 840 841 client = hids_create_client(con_handle, cid); 842 if (client == NULL) { 843 return BTSTACK_MEMORY_ALLOC_FAILED; 844 } 845 846 client->required_protocol_mode = protocol_mode; 847 client->client_handler = packet_handler; 848 client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE; 849 850 hids_run_for_client(client); 851 return ERROR_CODE_SUCCESS; 852 } 853 854 uint8_t hids_client_disconnect(uint16_t hids_cid){ 855 hids_client_t * client = hids_get_client_for_cid(hids_cid); 856 if (client == NULL){ 857 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 858 } 859 // finalize connection 860 hids_finalize_client(client); 861 return ERROR_CODE_SUCCESS; 862 } 863 864 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ 865 hids_client_t * client = hids_get_client_for_cid(hids_cid); 866 if (client == NULL){ 867 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 868 } 869 870 if (client->state != HIDS_CLIENT_STATE_CONNECTED) { 871 return ERROR_CODE_COMMAND_DISALLOWED; 872 } 873 874 uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT); 875 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){ 876 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 877 } 878 879 uint16_t mtu; 880 uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu); 881 882 if (status != ERROR_CODE_SUCCESS){ 883 return status; 884 } 885 886 if (mtu - 2 < report_len){ 887 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 888 } 889 890 client->state = HIDS_CLIENT_W2_SEND_REPORT; 891 client->active_index = report_index; 892 client->report = report; 893 client->report_len = report_len; 894 895 hids_run_for_client(client); 896 return ERROR_CODE_SUCCESS; 897 } 898 899 void hids_client_init(void){} 900 901 void hids_client_deinit(void){} 902