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_report_index < HIDS_CLIENT_NUM_REPORTS) { 121 client->reports[client->active_report_index].value_handle = characteristic->value_handle; 122 client->reports[client->active_report_index].end_handle = characteristic->end_handle; 123 client->reports[client->active_report_index].properties = characteristic->properties; 124 125 client->reports[client->active_report_index].service_index = client->service_index; 126 client->reports[client->active_report_index].report_id = report_id; 127 client->reports[client->active_report_index].report_type = report_type; 128 129 client->active_report_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 void hids_client_get_next_active_report_index(hids_client_t * client){ 152 uint8_t i; 153 154 for (i = client->active_report_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_ID){ 157 client->active_report_index = i; 158 } 159 return; 160 } 161 client->active_report_index = HIDS_CLIENT_INVALID_REPORT_INDEX; 162 } 163 164 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){ 165 hids_client_t * client = btstack_memory_hids_client_get(); 166 if (!client){ 167 log_error("Not enough memory to create client"); 168 return NULL; 169 } 170 client->state = HIDS_CLIENT_STATE_IDLE; 171 client->cid = cid; 172 client->con_handle = con_handle; 173 174 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 175 return client; 176 } 177 178 static void hids_finalize_client(hids_client_t * client){ 179 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 180 btstack_memory_hids_client_free(client); 181 } 182 183 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){ 184 btstack_linked_list_iterator_t it; 185 btstack_linked_list_iterator_init(&it, &clients); 186 while (btstack_linked_list_iterator_has_next(&it)){ 187 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 188 if (client->con_handle != con_handle) continue; 189 return client; 190 } 191 return NULL; 192 } 193 194 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){ 195 btstack_linked_list_iterator_t it; 196 btstack_linked_list_iterator_init(&it, &clients); 197 while (btstack_linked_list_iterator_has_next(&it)){ 198 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 199 if (client->cid != hids_cid) continue; 200 return client; 201 } 202 return NULL; 203 } 204 205 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){ 206 uint8_t event[8]; 207 int pos = 0; 208 event[pos++] = HCI_EVENT_GATTSERVICE_META; 209 event[pos++] = sizeof(event) - 2; 210 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED; 211 little_endian_store_16(event, pos, client->cid); 212 pos += 2; 213 event[pos++] = status; 214 event[pos++] = client->protocol_mode; 215 event[pos++] = client->num_instances; 216 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 217 } 218 219 220 static void hids_run_for_client(hids_client_t * client){ 221 uint8_t att_status; 222 gatt_client_service_t service; 223 gatt_client_characteristic_t characteristic; 224 uint8_t report_index; 225 226 switch (client->state){ 227 case HIDS_CLIENT_STATE_W2_QUERY_SERVICE: 228 client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT; 229 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 230 // TODO handle status 231 UNUSED(att_status); 232 break; 233 234 case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 235 client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 236 237 service.start_group_handle = client->services[client->service_index].start_handle; 238 service.end_group_handle = client->services[client->service_index].end_handle; 239 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service); 240 241 UNUSED(att_status); 242 break; 243 244 case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD: 245 client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED; 246 247 report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic); 248 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 249 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 250 } 251 252 if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){ 253 client->reports[report_index].value_handle = 0; 254 client->state = HIDS_CLIENT_STATE_CONNECTED; 255 } 256 break; 257 258 case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE: 259 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 260 261 report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic); 262 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 272 break; 273 274 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE: 275 client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE; 276 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); 277 UNUSED(att_status); 278 279 client->protocol_mode = client->required_protocol_mode; 280 client->state = HIDS_CLIENT_STATE_CONNECTED; 281 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 282 break; 283 284 case HIDS_CLIENT_W2_SEND_REPORT:{ 285 client->state = HIDS_CLIENT_STATE_CONNECTED; 286 287 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, 288 client->reports[client->active_report_index].value_handle, 289 client->report_len, (uint8_t *)client->report); 290 UNUSED(att_status); 291 break; 292 } 293 294 case HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS: 295 client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT; 296 client->descriptor_handle = 0; 297 hids_client_get_characteristic_for_report_index(client, client->active_report_index, &characteristic); 298 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 299 UNUSED(att_status); 300 break; 301 302 case HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE: 303 client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT; 304 305 att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle); 306 client->descriptor_handle = 0; 307 UNUSED(att_status); 308 break; 309 310 default: 311 break; 312 } 313 } 314 315 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){ 316 uint16_t pos = 0; 317 buffer[pos++] = HCI_EVENT_GATTSERVICE_META; 318 pos++; // skip len 319 buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT; 320 little_endian_store_16(buffer, pos, client->cid); 321 pos += 2; 322 buffer[pos++] = report_id; 323 little_endian_store_16(buffer, pos, report_len); 324 pos += 2; 325 buffer[1] = pos + report_len - 2; 326 } 327 328 static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 329 UNUSED(packet_type); 330 UNUSED(channel); 331 UNUSED(size); 332 333 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 334 335 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 336 btstack_assert(client != NULL); 337 338 uint8_t * in_place_event = packet; 339 hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 340 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 341 } 342 343 static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 344 UNUSED(packet_type); 345 UNUSED(channel); 346 UNUSED(size); 347 348 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 349 350 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 351 btstack_assert(client != NULL); 352 353 uint8_t * in_place_event = packet; 354 hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 355 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 356 } 357 358 359 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 360 UNUSED(packet_type); 361 UNUSED(channel); 362 UNUSED(size); 363 364 hids_client_t * client = NULL; 365 uint8_t att_status; 366 gatt_client_service_t service; 367 gatt_client_characteristic_t characteristic; 368 gatt_client_characteristic_descriptor_t characteristic_descriptor; 369 370 hids_client_report_t * boot_keyboard_report; 371 hids_client_report_t * boot_mouse_report; 372 373 switch(hci_event_packet_get_type(packet)){ 374 case GATT_EVENT_SERVICE_QUERY_RESULT: 375 client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 376 btstack_assert(client != NULL); 377 378 if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) { 379 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 380 hids_finalize_client(client); 381 break; 382 } 383 384 if (client->num_instances < MAX_NUM_HID_SERVICES){ 385 gatt_event_service_query_result_get_service(packet, &service); 386 client->services[client->num_instances].start_handle = service.start_group_handle; 387 client->services[client->num_instances].end_handle = service.end_group_handle; 388 } 389 client->num_instances++; 390 break; 391 392 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 393 client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 394 btstack_assert(client != NULL); 395 396 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 397 switch (characteristic.uuid16){ 398 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE: 399 client->protocol_mode_value_handle = characteristic.value_handle; 400 break; 401 402 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT: 403 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT); 404 break; 405 406 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT: 407 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT); 408 break; 409 410 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT: 411 hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT); 412 break; 413 414 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT: 415 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED); 416 break; 417 418 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP: 419 // printf("ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP\n"); 420 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_INPUT); 421 break; 422 423 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION: 424 // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n"); 425 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT); 426 break; 427 428 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT: 429 // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n"); 430 hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT); 431 break; 432 default: 433 break; 434 } 435 break; 436 437 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 438 client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet)); 439 btstack_assert(client != NULL); 440 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 441 442 // setup for descriptor value query 443 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){ 444 client->descriptor_handle = characteristic_descriptor.handle; 445 } 446 break; 447 448 case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT: 449 // get report ID 450 client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet)); 451 btstack_assert(client != NULL); 452 if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) == 2){ 453 const uint8_t * value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet); 454 client->reports[client->active_report_index].report_id = value[0]; 455 client->reports[client->active_report_index].report_type = (hid_report_type_t)value[1]; 456 } 457 break; 458 459 case GATT_EVENT_QUERY_COMPLETE: 460 client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 461 btstack_assert(client != NULL); 462 463 att_status = gatt_event_query_complete_get_att_status(packet); 464 465 switch (client->state){ 466 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT: 467 if (att_status != ATT_ERROR_SUCCESS){ 468 hids_emit_connection_established(client, att_status); 469 hids_finalize_client(client); 470 break; 471 } 472 473 if (client->num_instances == 0){ 474 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 475 hids_finalize_client(client); 476 break; 477 } 478 479 if (client->num_instances > MAX_NUM_HID_SERVICES) { 480 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES); 481 client->num_instances = MAX_NUM_HID_SERVICES; 482 } 483 484 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 485 client->service_index = 0; 486 break; 487 488 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 489 if (att_status != ATT_ERROR_SUCCESS){ 490 hids_emit_connection_established(client, att_status); 491 hids_finalize_client(client); 492 break; 493 } 494 495 switch (client->required_protocol_mode){ 496 case HID_PROTOCOL_MODE_BOOT: 497 if (get_boot_keyboard_input_report(client) != NULL){ 498 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD; 499 break; 500 } 501 if (get_boot_mouse_input_report(client) != NULL){ 502 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 503 break; 504 } 505 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 506 hids_finalize_client(client); 507 break; 508 case HID_PROTOCOL_MODE_REPORT: 509 if ((client->service_index + 1) < client->num_instances){ 510 // discover characteristics of next service 511 client->service_index++; 512 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 513 } else { 514 515 // discover characteristic descriptor for all Report characteristics, 516 // then read value of characteristic descriptor to get Report ID 517 client->active_report_index = 0; 518 client->service_index = 0; 519 520 hids_client_get_next_active_report_index(client); 521 if (client->active_report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 522 client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS; 523 break; 524 } 525 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 526 hids_finalize_client(client); 527 } 528 break; 529 default: 530 break; 531 } 532 break; 533 534 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT: 535 if (client->descriptor_handle != 0){ 536 // descriptor found 537 client->state = HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE; 538 break; 539 } 540 541 // go for next report 542 client->active_report_index++; 543 hids_client_get_next_active_report_index(client); 544 545 if (client->active_report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 546 client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS; 547 break; 548 } 549 550 // TODO continue with report mode 551 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 552 break; 553 554 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT: 555 // go for next report 556 client->active_report_index++; 557 hids_client_get_next_active_report_index(client); 558 if (client->active_report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 559 client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS; 560 break; 561 } 562 563 // TODO continue with report mode 564 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 565 break; 566 567 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 568 boot_keyboard_report = get_boot_keyboard_input_report(client); 569 if (boot_keyboard_report != NULL){ 570 // setup listener 571 characteristic.value_handle = boot_keyboard_report->value_handle; 572 gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic); 573 } 574 575 // check if there is mouse input report 576 boot_mouse_report = get_boot_mouse_input_report(client); 577 if (boot_mouse_report != NULL){ 578 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 579 break; 580 } 581 582 // if none of the reports is found bail out 583 if (!boot_mouse_report && !boot_keyboard_report){ 584 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 585 hids_finalize_client(client); 586 break; 587 } 588 589 // set protocol 590 if (client->protocol_mode_value_handle != 0){ 591 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 592 } else { 593 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 594 client->state = HIDS_CLIENT_STATE_CONNECTED; 595 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 596 } 597 hids_run_for_client(client); 598 break; 599 600 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED: 601 boot_mouse_report = get_boot_mouse_input_report(client); 602 if (boot_mouse_report != NULL){ 603 // setup listener 604 characteristic.value_handle = boot_mouse_report->value_handle; 605 gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic); 606 } 607 608 boot_keyboard_report = get_boot_keyboard_input_report(client); 609 if (!boot_mouse_report && !boot_keyboard_report){ 610 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 611 hids_finalize_client(client); 612 break; 613 } 614 615 if (client->protocol_mode_value_handle != 0){ 616 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 617 } else { 618 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 619 client->state = HIDS_CLIENT_STATE_CONNECTED; 620 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 621 } 622 break; 623 default: 624 break; 625 } 626 break; 627 628 default: 629 break; 630 } 631 632 if (client != NULL){ 633 hids_run_for_client(client); 634 } 635 } 636 637 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){ 638 btstack_assert(packet_handler != NULL); 639 640 hids_client_t * client = hids_get_client_for_con_handle(con_handle); 641 if (client != NULL){ 642 return ERROR_CODE_COMMAND_DISALLOWED; 643 } 644 645 uint16_t cid = hids_get_next_cid(); 646 if (hids_cid != NULL) { 647 *hids_cid = cid; 648 } 649 650 client = hids_create_client(con_handle, cid); 651 if (client == NULL) { 652 return BTSTACK_MEMORY_ALLOC_FAILED; 653 } 654 655 client->required_protocol_mode = protocol_mode; 656 client->client_handler = packet_handler; 657 client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE; 658 659 hids_run_for_client(client); 660 return ERROR_CODE_SUCCESS; 661 } 662 663 uint8_t hids_client_disconnect(uint16_t hids_cid){ 664 hids_client_t * client = hids_get_client_for_cid(hids_cid); 665 if (client == NULL){ 666 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 667 } 668 // finalize connection 669 hids_finalize_client(client); 670 return ERROR_CODE_SUCCESS; 671 } 672 673 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ 674 hids_client_t * client = hids_get_client_for_cid(hids_cid); 675 if (client == NULL){ 676 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 677 } 678 679 if (client->state != HIDS_CLIENT_STATE_CONNECTED) { 680 return ERROR_CODE_COMMAND_DISALLOWED; 681 } 682 683 uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT); 684 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){ 685 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 686 } 687 688 uint16_t mtu; 689 uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu); 690 691 if (status != ERROR_CODE_SUCCESS){ 692 return status; 693 } 694 695 if (mtu - 2 < report_len){ 696 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 697 } 698 699 client->state = HIDS_CLIENT_W2_SEND_REPORT; 700 client->active_report_index = report_index; 701 client->report = report; 702 client->report_len = report_len; 703 704 hids_run_for_client(client); 705 return ERROR_CODE_SUCCESS; 706 } 707 708 void hids_client_init(void){} 709 710 void hids_client_deinit(void){} 711