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 static uint16_t hids_report_counter = 0; 61 62 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 63 64 static uint16_t hids_get_next_cid(void){ 65 if (hids_cid_counter == 0xffff) { 66 hids_cid_counter = 1; 67 } else { 68 hids_cid_counter++; 69 } 70 return hids_cid_counter; 71 } 72 73 static uint8_t hids_get_next_report_index(void){ 74 if (hids_report_counter < HIDS_CLIENT_NUM_REPORTS) { 75 hids_report_counter++; 76 } else { 77 hids_report_counter = HIDS_CLIENT_INVALID_REPORT_INDEX; 78 } 79 return hids_report_counter; 80 } 81 82 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){ 83 hids_client_t * client = btstack_memory_hids_client_get(); 84 if (!client){ 85 log_error("Not enough memory to create client"); 86 return NULL; 87 } 88 client->state = HIDS_CLIENT_STATE_IDLE; 89 client->cid = cid; 90 client->con_handle = con_handle; 91 92 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 93 return client; 94 } 95 96 static void hids_finalize_client(hids_client_t * client){ 97 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 98 btstack_memory_hids_client_free(client); 99 } 100 101 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){ 102 btstack_linked_list_iterator_t it; 103 btstack_linked_list_iterator_init(&it, &clients); 104 while (btstack_linked_list_iterator_has_next(&it)){ 105 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 106 if (client->con_handle != con_handle) continue; 107 return client; 108 } 109 return NULL; 110 } 111 112 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){ 113 btstack_linked_list_iterator_t it; 114 btstack_linked_list_iterator_init(&it, &clients); 115 while (btstack_linked_list_iterator_has_next(&it)){ 116 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 117 if (client->cid != hids_cid) continue; 118 return client; 119 } 120 return NULL; 121 } 122 123 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){ 124 uint8_t event[8]; 125 int pos = 0; 126 event[pos++] = HCI_EVENT_GATTSERVICE_META; 127 event[pos++] = sizeof(event) - 2; 128 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED; 129 little_endian_store_16(event, pos, client->cid); 130 pos += 2; 131 event[pos++] = status; 132 event[pos++] = client->protocol_mode; 133 event[pos++] = client->num_instances; 134 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 135 } 136 137 138 static void hids_run_for_client(hids_client_t * client){ 139 uint8_t att_status; 140 gatt_client_service_t service; 141 gatt_client_characteristic_t characteristic; 142 143 switch (client->state){ 144 case HIDS_CLIENT_STATE_W2_QUERY_SERVICE: 145 client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT; 146 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 147 // TODO handle status 148 UNUSED(att_status); 149 break; 150 151 case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 152 client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 153 154 service.start_group_handle = client->services[client->service_index].start_handle; 155 service.end_group_handle = client->services[client->service_index].end_handle; 156 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service); 157 158 UNUSED(att_status); 159 break; 160 161 case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD: 162 client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED; 163 characteristic.value_handle = client->boot_keyboard_input_value_handle; 164 characteristic.end_handle = client->boot_keyboard_input_end_handle; 165 characteristic.properties = client->boot_keyboard_input_properties; 166 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 167 168 if (att_status != ERROR_CODE_SUCCESS){ 169 client->boot_keyboard_input_value_handle = 0; 170 } 171 break; 172 173 case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE: 174 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 175 characteristic.value_handle = client->boot_mouse_input_value_handle; 176 characteristic.end_handle = client->boot_mouse_input_end_handle; 177 characteristic.properties = client->boot_mouse_input_properties; 178 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 179 180 if (att_status != ERROR_CODE_SUCCESS){ 181 client->boot_mouse_input_value_handle = 0; 182 } 183 break; 184 185 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE: 186 client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE; 187 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); 188 UNUSED(att_status); 189 190 client->protocol_mode = client->required_protocol_mode; 191 client->state = HIDS_CLIENT_STATE_CONNECTED; 192 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 193 break; 194 195 case HIDS_CLIENT_W2_SEND_REPORT:{ 196 client->state = HIDS_CLIENT_STATE_CONNECTED; 197 198 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, 199 client->reports[client->active_report_index].value_handle, 200 client->report_len, (uint8_t *)client->report); 201 UNUSED(att_status); 202 break; 203 } 204 default: 205 break; 206 } 207 } 208 209 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){ 210 uint16_t pos = 0; 211 buffer[pos++] = HCI_EVENT_GATTSERVICE_META; 212 pos++; // skip len 213 buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT; 214 little_endian_store_16(buffer, pos, client->cid); 215 pos += 2; 216 buffer[pos++] = report_id; 217 little_endian_store_16(buffer, pos, report_len); 218 pos += 2; 219 buffer[1] = pos + report_len - 2; 220 } 221 222 static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 223 UNUSED(packet_type); 224 UNUSED(channel); 225 UNUSED(size); 226 227 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 228 229 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 230 btstack_assert(client != NULL); 231 232 uint8_t * in_place_event = packet; 233 hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 234 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 235 } 236 237 static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 238 UNUSED(packet_type); 239 UNUSED(channel); 240 UNUSED(size); 241 242 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 243 244 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 245 btstack_assert(client != NULL); 246 247 uint8_t * in_place_event = packet; 248 hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet)); 249 (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size); 250 } 251 252 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){ 253 uint8_t i; 254 for (i = 0; i < client->num_reports; client++){ 255 if (client->reports[i].value_handle == value_handle){ 256 return i; 257 } 258 } 259 return HIDS_CLIENT_INVALID_REPORT_INDEX; 260 } 261 262 static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t report_id){ 263 uint8_t i; 264 for (i = 0; i < client->num_reports; client++){ 265 if (client->reports[i].report_id == report_id){ 266 return i; 267 } 268 } 269 return HIDS_CLIENT_INVALID_REPORT_INDEX; 270 } 271 272 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 273 UNUSED(packet_type); 274 UNUSED(channel); 275 UNUSED(size); 276 277 hids_client_t * client = NULL; 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(hci_event_packet_get_type(packet)){ 284 case GATT_EVENT_SERVICE_QUERY_RESULT: 285 client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 286 btstack_assert(client != NULL); 287 288 if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) { 289 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 290 hids_finalize_client(client); 291 break; 292 } 293 294 if (client->num_instances < MAX_NUM_HID_SERVICES){ 295 gatt_event_service_query_result_get_service(packet, &service); 296 client->services[client->num_instances].start_handle = service.start_group_handle; 297 client->services[client->num_instances].end_handle = service.end_group_handle; 298 } 299 client->num_instances++; 300 break; 301 302 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 303 client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 304 btstack_assert(client != NULL); 305 306 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 307 switch (characteristic.uuid16){ 308 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT: 309 client->boot_keyboard_input_value_handle = characteristic.value_handle; 310 client->boot_keyboard_input_end_handle = characteristic.end_handle; 311 client->boot_keyboard_input_properties = characteristic.properties; 312 break; 313 314 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT: 315 client->boot_mouse_input_value_handle = characteristic.value_handle; 316 client->boot_mouse_input_end_handle = characteristic.end_handle; 317 client->boot_mouse_input_properties = characteristic.properties; 318 break; 319 320 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE: 321 client->protocol_mode_value_handle = characteristic.value_handle; 322 break; 323 324 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT: 325 report_index = find_report_index_for_value_handle(client, characteristic.value_handle); 326 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 327 break; 328 } 329 330 report_index = hids_get_next_report_index(); 331 332 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){ 333 client->reports[report_index].value_handle = characteristic.value_handle; 334 client->reports[report_index].report_id = HID_BOOT_MODE_KEYBOARD_ID; 335 client->reports[report_index].report_type = HID_REPORT_TYPE_OUTPUT; 336 client->num_reports++; 337 } else { 338 log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS"); 339 } 340 break; 341 342 default: 343 break; 344 } 345 break; 346 347 case GATT_EVENT_QUERY_COMPLETE: 348 client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 349 btstack_assert(client != NULL); 350 351 att_status = gatt_event_query_complete_get_att_status(packet); 352 353 switch (client->state){ 354 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT: 355 if (att_status != ATT_ERROR_SUCCESS){ 356 hids_emit_connection_established(client, att_status); 357 hids_finalize_client(client); 358 break; 359 } 360 361 if (client->num_instances == 0){ 362 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 363 hids_finalize_client(client); 364 break; 365 } 366 367 if (client->num_instances > MAX_NUM_HID_SERVICES) { 368 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES); 369 client->num_instances = MAX_NUM_HID_SERVICES; 370 } 371 372 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 373 client->service_index = 0; 374 break; 375 376 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 377 if (att_status != ATT_ERROR_SUCCESS){ 378 hids_emit_connection_established(client, att_status); 379 hids_finalize_client(client); 380 break; 381 } 382 383 switch (client->required_protocol_mode){ 384 case HID_PROTOCOL_MODE_BOOT: 385 if (client->boot_keyboard_input_value_handle != 0){ 386 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD; 387 break; 388 } 389 if (client->boot_mouse_input_value_handle != 0){ 390 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 391 break; 392 } 393 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 394 hids_finalize_client(client); 395 break; 396 default: 397 break; 398 } 399 break; 400 401 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 402 if (client->boot_keyboard_input_value_handle != 0){ 403 // setup listener 404 characteristic.value_handle = client->boot_keyboard_input_value_handle; 405 gatt_client_listen_for_characteristic_value_updates(&client->boot_keyboard_notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic); 406 } else { 407 if (client->boot_mouse_input_value_handle == 0){ 408 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 409 hids_finalize_client(client); 410 break; 411 } 412 } 413 414 if (client->boot_mouse_input_value_handle != 0){ 415 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE; 416 break; 417 } 418 419 // set protocol 420 if (client->protocol_mode_value_handle != 0){ 421 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 422 } else { 423 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 424 client->state = HIDS_CLIENT_STATE_CONNECTED; 425 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 426 } 427 hids_run_for_client(client); 428 break; 429 430 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED: 431 if (client->boot_mouse_input_value_handle != 0){ 432 // setup listener 433 characteristic.value_handle = client->boot_mouse_input_value_handle; 434 gatt_client_listen_for_characteristic_value_updates(&client->boot_mouse_notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic); 435 } else { 436 if (client->boot_keyboard_input_value_handle == 0){ 437 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 438 hids_finalize_client(client); 439 break; 440 } 441 } 442 443 if (client->protocol_mode_value_handle != 0){ 444 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE; 445 } else { 446 client->protocol_mode = HID_PROTOCOL_MODE_BOOT; 447 client->state = HIDS_CLIENT_STATE_CONNECTED; 448 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 449 } 450 break; 451 default: 452 break; 453 } 454 break; 455 456 default: 457 break; 458 } 459 460 if (client != NULL){ 461 hids_run_for_client(client); 462 } 463 } 464 465 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){ 466 btstack_assert(packet_handler != NULL); 467 468 hids_client_t * client = hids_get_client_for_con_handle(con_handle); 469 if (client != NULL){ 470 return ERROR_CODE_COMMAND_DISALLOWED; 471 } 472 473 uint16_t cid = hids_get_next_cid(); 474 if (hids_cid != NULL) { 475 *hids_cid = cid; 476 } 477 478 client = hids_create_client(con_handle, cid); 479 if (client == NULL) { 480 return BTSTACK_MEMORY_ALLOC_FAILED; 481 } 482 483 client->required_protocol_mode = protocol_mode; 484 client->client_handler = packet_handler; 485 client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE; 486 487 hids_run_for_client(client); 488 return ERROR_CODE_SUCCESS; 489 } 490 491 uint8_t hids_client_disconnect(uint16_t hids_cid){ 492 hids_client_t * client = hids_get_client_for_cid(hids_cid); 493 if (client == NULL){ 494 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 495 } 496 // finalize connection 497 hids_finalize_client(client); 498 return ERROR_CODE_SUCCESS; 499 } 500 501 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ 502 hids_client_t * client = hids_get_client_for_cid(hids_cid); 503 if (client == NULL){ 504 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 505 } 506 507 if (client->state != HIDS_CLIENT_STATE_CONNECTED) { 508 return ERROR_CODE_COMMAND_DISALLOWED; 509 } 510 511 uint8_t report_index = find_report_index_for_report_id(client, report_id); 512 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){ 513 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 514 } 515 516 uint16_t mtu; 517 uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu); 518 519 if (status != ERROR_CODE_SUCCESS){ 520 return status; 521 } 522 523 if (mtu - 2 < report_len){ 524 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 525 } 526 527 client->state = HIDS_CLIENT_W2_SEND_REPORT; 528 client->active_report_index = report_index; 529 client->report = report; 530 client->report_len = report_len; 531 532 hids_run_for_client(client); 533 return ERROR_CODE_SUCCESS; 534 } 535 536 void hids_client_init(void){} 537 538 void hids_client_deinit(void){} 539