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 BLUEKITCHEN 24 * GMBH 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__ "scan_parameters_service_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <string.h> 44 45 #ifdef ENABLE_TESTING_SUPPORT 46 #include <stdio.h> 47 #endif 48 49 #include "scan_parameters_service_client.h" 50 51 #include "btstack_memory.h" 52 #include "ble/core.h" 53 #include "ble/gatt_client.h" 54 #include "bluetooth_gatt.h" 55 #include "btstack_debug.h" 56 #include "btstack_event.h" 57 #include "btstack_run_loop.h" 58 #include "gap.h" 59 60 #include "ble/gatt_service_client.h" 61 62 static btstack_packet_callback_registration_t hci_event_callback_registration; 63 64 static btstack_linked_list_t clients; 65 static uint16_t scan_parameters_service_cid_counter = 0; 66 static uint16_t scan_parameters_service_scan_window = 0; 67 static uint16_t scan_parameters_service_scan_interval = 0; 68 69 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 70 static void scan_parameters_service_send_next_query(void * context); 71 static btstack_context_callback_registration_t scan_parameters_service_handle_can_send_now; 72 73 static uint16_t scan_parameters_service_get_next_cid(void){ 74 if (scan_parameters_service_cid_counter == 0xffff) { 75 scan_parameters_service_cid_counter = 1; 76 } else { 77 scan_parameters_service_cid_counter++; 78 } 79 return scan_parameters_service_cid_counter; 80 } 81 82 static uint8_t scan_parameters_client_request_send_gatt_query(scan_parameters_service_client_t * client){ 83 scan_parameters_service_handle_can_send_now.context = (void *) (uintptr_t)client->cid; 84 uint8_t status = gatt_client_request_to_send_gatt_query(&scan_parameters_service_handle_can_send_now, client->con_handle); 85 if (status != ERROR_CODE_SUCCESS){ 86 if (client->state >= SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE){ 87 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE; 88 } 89 } 90 return status; 91 } 92 93 static scan_parameters_service_client_t * scan_parameters_service_create_client(hci_con_handle_t con_handle, uint16_t cid){ 94 scan_parameters_service_client_t * client = btstack_memory_scan_parameters_service_client_get(); 95 if (!client){ 96 log_error("Not enough memory to create client"); 97 return NULL; 98 } 99 100 client->cid = cid; 101 client->con_handle = con_handle; 102 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE; 103 104 client->start_handle = 0; 105 client->end_handle = 0; 106 107 client->scan_interval_window_value_handle = 0; 108 client->scan_interval_window_value_update = false; 109 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 110 return client; 111 } 112 113 static void scan_parameters_service_finalize_client(scan_parameters_service_client_t * client){ 114 gatt_client_stop_listening_for_characteristic_value_updates(&client->notification_listener); 115 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 116 btstack_memory_scan_parameters_service_client_free(client); 117 } 118 119 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_con_handle(hci_con_handle_t con_handle){ 120 btstack_linked_list_iterator_t it; 121 btstack_linked_list_iterator_init(&it, &clients); 122 while (btstack_linked_list_iterator_has_next(&it)){ 123 scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it); 124 if (client->con_handle != con_handle) continue; 125 return client; 126 } 127 return NULL; 128 } 129 130 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_cid(uint16_t scan_parameters_service_cid){ 131 btstack_linked_list_iterator_t it; 132 btstack_linked_list_iterator_init(&it, &clients); 133 while (btstack_linked_list_iterator_has_next(&it)){ 134 scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it); 135 if (client->cid != scan_parameters_service_cid) continue; 136 return client; 137 } 138 return NULL; 139 } 140 141 static void scan_parameters_service_emit_connection_established(scan_parameters_service_client_t * client, uint8_t status){ 142 uint8_t event[6]; 143 int pos = 0; 144 event[pos++] = HCI_EVENT_GATTSERVICE_META; 145 event[pos++] = sizeof(event) - 2; 146 event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_CONNECTED; 147 little_endian_store_16(event, pos, client->cid); 148 pos += 2; 149 event[pos++] = status; 150 (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, 0, event, sizeof(event)); 151 } 152 153 static void scan_parameters_service_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){ 154 uint8_t event[5]; 155 int pos = 0; 156 event[pos++] = HCI_EVENT_GATTSERVICE_META; 157 event[pos++] = sizeof(event) - 2; 158 event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_DISCONNECTED; 159 little_endian_store_16(event, pos, cid); 160 pos += 2; 161 btstack_assert(pos == sizeof(event)); 162 (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 163 } 164 165 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 166 UNUSED(packet_type); 167 UNUSED(channel); 168 UNUSED(size); 169 170 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 171 172 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 173 btstack_assert(client != NULL); 174 client->scan_interval_window_value_update = true; 175 scan_parameters_client_request_send_gatt_query(client); 176 } 177 178 static void scan_parameters_service_send_next_query(void * context){ 179 uint16_t cid = (uint16_t)(uintptr_t)context; 180 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(cid); 181 if (client == NULL){ 182 return; 183 } 184 185 uint8_t att_status; 186 gatt_client_service_t service; 187 188 gatt_client_characteristic_t characteristic; 189 190 switch (client->state){ 191 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE: 192 #ifdef ENABLE_TESTING_SUPPORT 193 printf("\n\nQuery Services:\n"); 194 #endif 195 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 196 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS); 197 // TODO handle status 198 UNUSED(att_status); 199 break; 200 201 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 202 #ifdef ENABLE_TESTING_SUPPORT 203 printf("\n\nQuery Characteristics of service\n"); 204 #endif 205 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 206 service.start_group_handle = client->start_handle; 207 service.end_group_handle = client->end_handle; 208 att_status = gatt_client_discover_characteristics_for_service( 209 handle_gatt_client_event, client->con_handle, &service); 210 // TODO handle status 211 UNUSED(att_status); 212 break; 213 214 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED: 215 if (client->scan_interval_window_value_update){ 216 client->scan_interval_window_value_update = false; 217 218 #ifdef ENABLE_TESTING_SUPPORT 219 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window); 220 #endif 221 uint8_t value[4]; 222 little_endian_store_16(value, 0, scan_parameters_service_scan_interval); 223 little_endian_store_16(value, 2, scan_parameters_service_scan_window); 224 225 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value); 226 // TODO handle status 227 UNUSED(att_status); 228 } 229 break; 230 #ifdef ENABLE_TESTING_SUPPORT 231 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC: 232 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC; 233 234 characteristic.value_handle = client->scan_refresh_value_handle; 235 characteristic.end_handle = client->scan_refresh_end_handle; 236 237 // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT 238 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 239 UNUSED(att_status); 240 break; 241 #endif 242 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS: 243 #ifdef ENABLE_TESTING_SUPPORT 244 printf(" Notification configuration enable "); 245 #endif 246 247 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED; 248 249 characteristic.value_handle = client->scan_refresh_value_handle; 250 characteristic.end_handle = client->scan_refresh_end_handle; 251 characteristic.properties = client->scan_refresh_properties; 252 253 // end of write marked in GATT_EVENT_QUERY_COMPLETE 254 255 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 256 257 if (att_status == ERROR_CODE_SUCCESS){ 258 gatt_client_listen_for_characteristic_value_updates( 259 &client->notification_listener, 260 &handle_notification_event, client->con_handle, &characteristic); 261 } 262 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 263 break; 264 default: 265 break; 266 } 267 } 268 269 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 270 UNUSED(packet_type); 271 UNUSED(channel); 272 UNUSED(size); 273 274 scan_parameters_service_client_t * client = NULL; 275 gatt_client_service_t service; 276 gatt_client_characteristic_t characteristic; 277 uint8_t status; 278 279 #ifdef ENABLE_TESTING_SUPPORT 280 gatt_client_characteristic_descriptor_t characteristic_descriptor; 281 #endif 282 283 switch(hci_event_packet_get_type(packet)){ 284 case GATT_EVENT_SERVICE_QUERY_RESULT: 285 client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 286 btstack_assert(client != NULL); 287 288 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) { 289 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 290 scan_parameters_service_finalize_client(client); 291 return; 292 } 293 294 gatt_event_service_query_result_get_service(packet, &service); 295 client->start_handle = service.start_group_handle; 296 client->end_handle = service.end_group_handle; 297 #ifdef ENABLE_TESTING_SUPPORT 298 printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle); 299 #endif 300 break; 301 302 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 303 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 304 btstack_assert(client != NULL); 305 306 // found scan_interval_window_value_handle, check att_status 307 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 308 switch (characteristic.uuid16){ 309 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW: 310 client->scan_interval_window_value_handle = characteristic.value_handle; 311 312 #ifdef ENABLE_TESTING_SUPPORT 313 printf("ScS Scan Interval Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 314 characteristic.start_handle, 315 characteristic.properties, 316 characteristic.value_handle, characteristic.uuid16); 317 #endif 318 break; 319 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH: 320 client->scan_refresh_value_handle = characteristic.value_handle; 321 client->scan_refresh_end_handle = characteristic.end_handle; 322 client->scan_refresh_properties = characteristic.properties; 323 324 #ifdef ENABLE_TESTING_SUPPORT 325 printf("ScS Scan Refresh Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 326 characteristic.start_handle, 327 characteristic.properties, 328 characteristic.value_handle, characteristic.uuid16); 329 #endif 330 break; 331 default: 332 break; 333 } 334 break; 335 336 #ifdef ENABLE_TESTING_SUPPORT 337 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 338 client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet)); 339 btstack_assert(client != NULL); 340 341 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 342 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){ 343 printf(" Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n", 344 characteristic_descriptor.handle, 345 characteristic_descriptor.uuid16); 346 } 347 break; 348 349 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 350 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet)); 351 btstack_assert(client != NULL); 352 353 printf(" Received CCC value: "); 354 printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet)); 355 break; 356 #endif 357 358 case GATT_EVENT_QUERY_COMPLETE: 359 client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 360 btstack_assert(client != NULL); 361 362 status = gatt_service_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet)); 363 364 switch (client->state){ 365 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 366 if (status != ERROR_CODE_SUCCESS){ 367 scan_parameters_service_emit_connection_established(client, status); 368 scan_parameters_service_finalize_client(client); 369 return; 370 } 371 372 if (client->start_handle != 0){ 373 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 374 break; 375 } 376 377 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 378 scan_parameters_service_finalize_client(client); 379 return; 380 381 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 382 if (status != ERROR_CODE_SUCCESS){ 383 scan_parameters_service_emit_connection_established(client, status); 384 scan_parameters_service_finalize_client(client); 385 break; 386 } 387 if (client->scan_interval_window_value_handle == 0){ 388 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 389 scan_parameters_service_finalize_client(client); 390 return; 391 } 392 #ifdef ENABLE_TESTING_SUPPORT 393 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC; 394 #else 395 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 396 client->scan_interval_window_value_update = true; 397 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 398 #endif 399 break; 400 401 #ifdef ENABLE_TESTING_SUPPORT 402 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC: 403 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 404 client->scan_interval_window_value_update = true; 405 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 406 break; 407 #endif 408 default: 409 break; 410 } 411 break; 412 default: 413 break; 414 } 415 416 if (client != NULL){ 417 scan_parameters_client_request_send_gatt_query(client); 418 } 419 } 420 421 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 422 UNUSED(packet_type); // ok: only hci events 423 UNUSED(channel); // ok: there is no channel 424 UNUSED(size); // ok: fixed format events read from HCI buffer 425 426 hci_con_handle_t con_handle; 427 scan_parameters_service_client_t * client; 428 429 switch (hci_event_packet_get_type(packet)) { 430 case HCI_EVENT_DISCONNECTION_COMPLETE: 431 con_handle = hci_event_disconnection_complete_get_connection_handle(packet); 432 client = scan_parameters_service_get_client_for_con_handle(con_handle); 433 if (client != NULL){ 434 // emit disconnected 435 btstack_packet_handler_t packet_handler = client->client_handler; 436 uint16_t cid = client->cid; 437 scan_parameters_service_emit_disconnected(packet_handler, cid); 438 // finalize 439 scan_parameters_service_finalize_client(client); 440 } 441 break; 442 default: 443 break; 444 } 445 } 446 447 void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){ 448 scan_parameters_service_scan_interval = scan_interval; 449 scan_parameters_service_scan_window = scan_window; 450 451 btstack_linked_list_iterator_t it; 452 btstack_linked_list_iterator_init(&it, &clients); 453 while (btstack_linked_list_iterator_has_next(&it)){ 454 scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it); 455 client->scan_interval_window_value_update = true; 456 scan_parameters_client_request_send_gatt_query(client); 457 } 458 } 459 460 uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){ 461 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 462 463 if (client == NULL){ 464 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 465 } 466 467 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) { 468 return ERROR_CODE_COMMAND_DISALLOWED; 469 } 470 471 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS; 472 return scan_parameters_client_request_send_gatt_query(client); 473 } 474 475 uint8_t scan_parameters_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint16_t * scan_parameters_service_cid){ 476 btstack_assert(packet_handler != NULL); 477 478 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle); 479 if (client != NULL){ 480 return ERROR_CODE_COMMAND_DISALLOWED; 481 } 482 483 uint16_t cid = scan_parameters_service_get_next_cid(); 484 if (scan_parameters_service_cid != NULL) { 485 *scan_parameters_service_cid = cid; 486 } 487 488 client = scan_parameters_service_create_client(con_handle, cid); 489 if (client == NULL) { 490 return BTSTACK_MEMORY_ALLOC_FAILED; 491 } 492 493 client->client_handler = packet_handler; 494 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE; 495 return scan_parameters_client_request_send_gatt_query(client); 496 } 497 498 uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){ 499 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 500 if (client == NULL){ 501 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 502 } 503 // finalize connections 504 scan_parameters_service_finalize_client(client); 505 return ERROR_CODE_SUCCESS; 506 } 507 508 void scan_parameters_service_client_init(void){ 509 hci_event_callback_registration.callback = &handle_hci_event; 510 hci_add_event_handler(&hci_event_callback_registration); 511 scan_parameters_service_handle_can_send_now.callback = &scan_parameters_service_send_next_query; 512 } 513 514 void scan_parameters_service_client_deinit(void){ 515 } 516 517