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