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 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 62 63 static uint16_t hids_get_next_cid(void){ 64 if (hids_cid_counter == 0xffff) { 65 hids_cid_counter = 1; 66 } else { 67 hids_cid_counter++; 68 } 69 return hids_cid_counter; 70 } 71 72 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){ 73 hids_client_t * client = btstack_memory_hids_client_get(); 74 if (!client){ 75 log_error("Not enough memory to create client"); 76 return NULL; 77 } 78 client->state = HIDS_CLIENT_STATE_IDLE; 79 client->cid = cid; 80 client->con_handle = con_handle; 81 82 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 83 return client; 84 } 85 86 static void hids_finalize_client(hids_client_t * client){ 87 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 88 btstack_memory_hids_client_free(client); 89 } 90 91 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){ 92 btstack_linked_list_iterator_t it; 93 btstack_linked_list_iterator_init(&it, &clients); 94 while (btstack_linked_list_iterator_has_next(&it)){ 95 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 96 if (client->con_handle != con_handle) continue; 97 return client; 98 } 99 return NULL; 100 } 101 102 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){ 103 btstack_linked_list_iterator_t it; 104 btstack_linked_list_iterator_init(&it, &clients); 105 while (btstack_linked_list_iterator_has_next(&it)){ 106 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it); 107 if (client->cid != hids_cid) continue; 108 return client; 109 } 110 return NULL; 111 } 112 113 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){ 114 uint8_t event[8]; 115 int pos = 0; 116 event[pos++] = HCI_EVENT_GATTSERVICE_META; 117 event[pos++] = sizeof(event) - 2; 118 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED; 119 little_endian_store_16(event, pos, client->cid); 120 pos += 2; 121 event[pos++] = status; 122 event[pos++] = client->protocol_mode; 123 event[pos++] = client->num_instances; 124 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 125 } 126 127 128 static void hids_run_for_client(hids_client_t * client){ 129 uint8_t att_status; 130 gatt_client_service_t service; 131 gatt_client_characteristic_t characteristic; 132 133 switch (client->state){ 134 case HIDS_CLIENT_STATE_W2_QUERY_SERVICE: 135 client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT; 136 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 137 // TODO handle status 138 UNUSED(att_status); 139 break; 140 141 case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 142 client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 143 144 service.start_group_handle = client->services[client->service_index].start_handle; 145 service.end_group_handle = client->services[client->service_index].end_handle; 146 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service); 147 148 UNUSED(att_status); 149 break; 150 151 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 152 characteristic.value_handle = client->boot_keyboard_input_value_handle; 153 characteristic.end_handle = client->boot_keyboard_input_end_handle; 154 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 155 UNUSED(att_status); 156 break; 157 158 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED: 159 characteristic.value_handle = client->boot_mouse_input_value_handle; 160 characteristic.end_handle = client->boot_mouse_input_end_handle; 161 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 162 UNUSED(att_status); 163 break; 164 165 case HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE: 166 client->protocol_mode = client->required_protocol_mode; 167 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->protocol_mode); 168 UNUSED(att_status); 169 client->state = HIDS_CLIENT_STATE_CONNECTED; 170 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 171 break; 172 173 case HIDS_CLIENT_STATE_CONNECTED: 174 175 break; 176 177 default: 178 break; 179 } 180 } 181 182 static hid_service_client_state_t boot_protocol_mode_setup_next_state(hids_client_t * client){ 183 hid_service_client_state_t state = HIDS_CLIENT_STATE_IDLE; 184 185 switch (client->state){ 186 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 187 // set protocol mode 188 if (client->boot_keyboard_input_value_handle != 0){ 189 state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED; 190 break; 191 } 192 if (client->boot_mouse_input_value_handle != 0){ 193 state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 194 break; 195 } 196 break; 197 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 198 if (client->boot_mouse_input_value_handle != 0){ 199 state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 200 break; 201 } 202 break; 203 default: 204 break; 205 } 206 return state; 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 void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 253 UNUSED(packet_type); 254 UNUSED(channel); 255 UNUSED(size); 256 257 hids_client_t * client = NULL; 258 uint8_t att_status; 259 gatt_client_service_t service; 260 gatt_client_characteristic_t characteristic; 261 262 switch(hci_event_packet_get_type(packet)){ 263 case GATT_EVENT_SERVICE_QUERY_RESULT: 264 client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 265 btstack_assert(client != NULL); 266 267 if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) { 268 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 269 hids_finalize_client(client); 270 break; 271 } 272 273 if (client->num_instances < MAX_NUM_HID_SERVICES){ 274 gatt_event_service_query_result_get_service(packet, &service); 275 client->services[client->num_instances].start_handle = service.start_group_handle; 276 client->services[client->num_instances].end_handle = service.end_group_handle; 277 } 278 client->num_instances++; 279 break; 280 281 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 282 client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 283 btstack_assert(client != NULL); 284 285 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 286 switch (characteristic.uuid16){ 287 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT: 288 client->boot_keyboard_input_value_handle = characteristic.value_handle; 289 break; 290 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT: 291 client->boot_mouse_input_value_handle = characteristic.value_handle; 292 break; 293 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE: 294 client->protocol_mode_value_handle = characteristic.value_handle; 295 break; 296 default: 297 break; 298 } 299 break; 300 301 case GATT_EVENT_QUERY_COMPLETE: 302 client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 303 btstack_assert(client != NULL); 304 305 att_status = gatt_event_query_complete_get_att_status(packet); 306 307 switch (client->state){ 308 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT: 309 if (att_status != ATT_ERROR_SUCCESS){ 310 hids_emit_connection_established(client, att_status); 311 hids_finalize_client(client); 312 break; 313 } 314 315 if (client->num_instances == 0){ 316 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 317 hids_finalize_client(client); 318 break; 319 } 320 321 if (client->num_instances > MAX_NUM_HID_SERVICES) { 322 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES); 323 client->num_instances = MAX_NUM_HID_SERVICES; 324 } 325 326 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 327 client->service_index = 0; 328 break; 329 330 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 331 if (att_status != ATT_ERROR_SUCCESS){ 332 hids_emit_connection_established(client, att_status); 333 hids_finalize_client(client); 334 break; 335 } 336 337 switch (client->required_protocol_mode){ 338 case HID_PROTOCOL_MODE_BOOT: 339 if (client->boot_keyboard_input_value_handle != 0){ 340 client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED; 341 break; 342 } 343 if (client->boot_mouse_input_value_handle != 0){ 344 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 345 break; 346 } 347 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 348 hids_finalize_client(client); 349 break; 350 default: 351 break; 352 } 353 break; 354 355 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED: 356 // setup listener 357 characteristic.value_handle = client->boot_keyboard_input_value_handle; 358 gatt_client_listen_for_characteristic_value_updates(&client->boot_keyboard_notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic); 359 360 if (client->boot_mouse_input_value_handle != 0){ 361 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED; 362 break; 363 } 364 // set protocol 365 if (client->protocol_mode_value_handle != 0){ 366 client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE; 367 } else { 368 client->state = HIDS_CLIENT_STATE_CONNECTED; 369 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 370 } 371 break; 372 373 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED: 374 // setup listener 375 characteristic.value_handle = client->boot_mouse_input_value_handle; 376 gatt_client_listen_for_characteristic_value_updates(&client->boot_mouse_notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic); 377 378 if (client->protocol_mode_value_handle != 0){ 379 client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE; 380 } else { 381 client->state = HIDS_CLIENT_STATE_CONNECTED; 382 hids_emit_connection_established(client, ERROR_CODE_SUCCESS); 383 } 384 break; 385 default: 386 break; 387 } 388 break; 389 390 default: 391 break; 392 } 393 394 if (client != NULL){ 395 hids_run_for_client(client); 396 } 397 } 398 399 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){ 400 btstack_assert(packet_handler != NULL); 401 402 hids_client_t * client = hids_get_client_for_con_handle(con_handle); 403 if (client != NULL){ 404 return ERROR_CODE_COMMAND_DISALLOWED; 405 } 406 407 uint16_t cid = hids_get_next_cid(); 408 if (hids_cid != NULL) { 409 *hids_cid = cid; 410 } 411 412 client = hids_create_client(con_handle, cid); 413 if (client == NULL) { 414 return BTSTACK_MEMORY_ALLOC_FAILED; 415 } 416 417 client->required_protocol_mode = protocol_mode; 418 client->client_handler = packet_handler; 419 client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE; 420 421 hids_run_for_client(client); 422 return ERROR_CODE_SUCCESS; 423 } 424 425 uint8_t hids_client_disconnect(uint16_t hids_cid){ 426 hids_client_t * client = hids_get_client_for_cid(hids_cid); 427 if (client == NULL){ 428 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 429 } 430 // finalize connection 431 hids_finalize_client(client); 432 return ERROR_CODE_SUCCESS; 433 } 434 435 void hids_client_init(void){} 436 437 void hids_client_deinit(void){} 438