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