1 /* 2 * Copyright (C) 2023 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__ "gatt_service_client_helper.c" 39 40 #ifdef ENABLE_TESTING_SUPPORT 41 #include <stdio.h> 42 #endif 43 44 #include <stdint.h> 45 #include <string.h> 46 47 #include "ble/gatt_service_client.h" 48 49 #include "bluetooth_gatt.h" 50 #include "btstack_debug.h" 51 #include "btstack_event.h" 52 53 static void gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 54 55 static bool gatt_service_client_intitialized = false; 56 static uint16_t gatt_service_client_service_cid; 57 static btstack_linked_list_t gatt_service_clients; 58 static btstack_packet_callback_registration_t gatt_service_client_hci_callback_registration; 59 60 // LE Audio Service Client helper functions 61 static void gatt_service_client_finalize_connection(gatt_service_client_t * client, gatt_service_client_connection_t * connection){ 62 btstack_assert(client != NULL); 63 btstack_linked_list_remove(&client->connections, (btstack_linked_item_t*) connection); 64 } 65 66 static gatt_service_client_t * gatt_service_client_get_service_client_for_id(uint16_t service_cid){ 67 btstack_linked_list_iterator_t it; 68 btstack_linked_list_iterator_init(&it, &gatt_service_clients); 69 while (btstack_linked_list_iterator_has_next(&it)){ 70 gatt_service_client_t * service_client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&it); 71 if (service_client->service_id == service_cid) { 72 return service_client; 73 }; 74 } 75 return NULL; 76 } 77 78 static gatt_service_client_connection_t * gatt_service_client_get_connection_for_con_handle_and_service_index(const gatt_service_client_t * client, hci_con_handle_t con_handle, uint8_t service_index){ 79 btstack_linked_list_iterator_t it; 80 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 81 while (btstack_linked_list_iterator_has_next(&it)){ 82 gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 83 if (connection->con_handle != con_handle) continue; 84 if (connection->service_index != service_index) continue; 85 return connection; 86 } 87 return NULL; 88 } 89 90 static gatt_service_client_connection_t * gatt_service_client_get_connection_for_con_handle_and_attribute_handle(const gatt_service_client_t * client, hci_con_handle_t con_handle, uint16_t attribute_handle){ 91 btstack_linked_list_iterator_t it; 92 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 93 while (btstack_linked_list_iterator_has_next(&it)){ 94 gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 95 if (connection->con_handle != con_handle) continue; 96 if (attribute_handle < connection->start_handle) continue; 97 if (attribute_handle > connection->end_handle) continue; 98 return connection; 99 } 100 return NULL; 101 } 102 103 static gatt_service_client_connection_t * gatt_service_client_get_connection_for_cid( 104 const gatt_service_client_t *client, uint16_t connection_cid){ 105 btstack_linked_list_iterator_t it; 106 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 107 while (btstack_linked_list_iterator_has_next(&it)){ 108 gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 109 if (connection->cid != connection_cid) continue; 110 return connection; 111 } 112 return NULL; 113 } 114 115 uint16_t gatt_service_client_get_mtu(const gatt_service_client_connection_t *connection) { 116 uint16_t mtu = 0; 117 gatt_client_get_mtu(connection->con_handle, &mtu); 118 return mtu; 119 } 120 121 uint16_t gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection){ 122 return connection->cid; 123 124 } 125 126 hci_con_handle_t gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection){ 127 return connection->con_handle; 128 } 129 130 uint8_t gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection){ 131 return connection->service_index; 132 } 133 134 uint16_t gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client, uint8_t characteristic_index){ 135 if (characteristic_index < client->characteristics_desc16_num){ 136 return client->characteristics_desc16[characteristic_index]; 137 } else { 138 return 0; 139 } 140 } 141 142 uint16_t gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t *connection, 143 uint8_t characteristic_index) { 144 return connection->characteristics[characteristic_index].value_handle; 145 } 146 147 uint8_t gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t *connection, 148 uint16_t value_handle) { 149 for (int i = 0; i < connection->client->characteristics_desc16_num; i++){ 150 if (connection->characteristics[i].value_handle == value_handle) { 151 return i; 152 } 153 } 154 return GATT_SERVICE_CLIENT_INVALID_INDEX; 155 } 156 157 158 static void gatt_service_client_emit_connected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid, uint8_t status){ 159 btstack_assert(event_callback != NULL); 160 161 log_info("GATT Client Helper, connected 0x%04x, status 0x%02x", con_handle, status); 162 163 uint8_t event[9]; 164 int pos = 0; 165 event[pos++] = HCI_EVENT_GATTSERVICE_META; 166 event[pos++] = sizeof(event) - 2; 167 event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_CONNECTED; 168 little_endian_store_16(event, pos, con_handle); 169 pos += 2; 170 little_endian_store_16(event, pos, cid); 171 pos += 2; 172 event[pos++] = 0; // num included services 173 event[pos++] = status; 174 (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 175 } 176 177 static void gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid){ 178 btstack_assert(event_callback != NULL); 179 180 uint8_t event[7]; 181 int pos = 0; 182 event[pos++] = HCI_EVENT_GATTSERVICE_META; 183 event[pos++] = sizeof(event) - 2; 184 event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED; 185 little_endian_store_16(event, pos, con_handle); 186 pos += 2; 187 little_endian_store_16(event, pos, cid); 188 pos += 2; 189 (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 190 } 191 192 static uint16_t gatt_service_client_get_next_cid(gatt_service_client_t * client){ 193 client->cid_counter = btstack_next_cid_ignoring_zero(client->cid_counter); 194 return client->cid_counter; 195 } 196 197 static bool gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) { 198 bool next_query_found = false; 199 while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) { 200 uint16_t notify_or_indicate = ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE; 201 if ((connection->characteristics[connection->characteristic_index].properties & notify_or_indicate) != 0u){ 202 next_query_found = true; 203 break; 204 } 205 connection->characteristic_index++; 206 } 207 return next_query_found; 208 } 209 210 static bool gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) { 211 bool next_query_found = false; 212 while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) { 213 if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0) { 214 next_query_found = true; 215 break; 216 } 217 connection->characteristic_index++; 218 } 219 return next_query_found; 220 } 221 222 static uint8_t gatt_service_client_register_notification(gatt_service_client_t * client, 223 gatt_service_client_connection_t *connection) { 224 gatt_client_characteristic_t characteristic; 225 uint8_t status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 226 227 if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0){ 228 characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle; 229 characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle; 230 characteristic.properties = connection->characteristics[connection->characteristic_index].properties; 231 232 if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u){ 233 status = gatt_client_write_client_characteristic_configuration_with_context( 234 &gatt_service_client_gatt_packet_handler, 235 connection->con_handle, 236 &characteristic, 237 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION, 238 client->service_id, 239 connection->cid); 240 } else if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){ 241 status = gatt_client_write_client_characteristic_configuration_with_context( 242 &gatt_service_client_gatt_packet_handler, 243 connection->con_handle, 244 &characteristic, 245 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION, 246 client->service_id, 247 connection->cid); 248 } 249 } 250 return status; 251 } 252 253 254 static void gatt_service_client_run_for_client(gatt_service_client_t * client, gatt_service_client_connection_t * connection){ 255 uint8_t status = ATT_ERROR_SUCCESS; 256 gatt_client_service_t service; 257 gatt_client_characteristic_t characteristic; 258 259 switch (connection->state){ 260 case GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE: 261 connection->state = GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 262 status = gatt_client_discover_primary_services_by_uuid16_with_context( 263 &gatt_service_client_gatt_packet_handler, 264 connection->con_handle, 265 connection->service_uuid16, 266 client->service_id, 267 connection->cid); 268 break; 269 270 case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS: 271 #ifdef ENABLE_TESTING_SUPPORT 272 printf("Read characteristics [service 0x%04x]:\n", connection->service_uuid16); 273 #endif 274 connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 275 276 service.start_group_handle = connection->start_handle; 277 service.end_group_handle = connection->end_handle; 278 service.uuid16 = connection->service_uuid16; 279 280 status = gatt_client_discover_characteristics_for_service_with_context( 281 &gatt_service_client_gatt_packet_handler, 282 connection->con_handle, 283 &service, 284 client->service_id, 285 connection->cid); 286 break; 287 288 case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS: 289 #ifdef ENABLE_TESTING_SUPPORT 290 printf("Read client characteristic descriptors for characteristic[%u, uuid16 0x%04x, value_handle 0x%04x]:\n", 291 connection->characteristic_index, 292 client->characteristics_desc16[connection->characteristic_index], 293 connection->characteristics[connection->characteristic_index].value_handle); 294 #endif 295 connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT; 296 characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle; 297 characteristic.properties = connection->characteristics[connection->characteristic_index].properties; 298 characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle; 299 300 (void) gatt_client_discover_characteristic_descriptors_with_context( 301 &gatt_service_client_gatt_packet_handler, 302 connection->con_handle, 303 &characteristic, 304 client->service_id, 305 connection->cid); 306 break; 307 308 case GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION: 309 #ifdef ENABLE_TESTING_SUPPORT 310 if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){ 311 printf("Register notification for characteristic"); 312 } else { 313 printf("Register indication for characteristic"); 314 } 315 316 printf("[%u, uuid16 0x%04x, ccd handle 0x%04x]\n", 317 connection->characteristic_index, 318 client->characteristics_desc16[connection->characteristic_index], 319 connection->characteristics[connection->characteristic_index].client_configuration_handle); 320 #endif 321 connection->state = GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED; 322 status = gatt_service_client_register_notification(client, connection); 323 connection->characteristic_index++; 324 325 #ifdef ENABLE_TESTING_SUPPORT 326 if (status != ERROR_CODE_SUCCESS) { 327 printf("Notification not supported, status 0%02X\n.", status); 328 } 329 #else 330 UNUSED(status); 331 #endif 332 return; 333 334 case GATT_SERVICE_CLIENT_STATE_CONNECTED: 335 // TODO 336 break; 337 default: 338 break; 339 } 340 341 if (status != ATT_ERROR_SUCCESS){ 342 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status)); 343 gatt_service_client_finalize_connection(client, connection); 344 } 345 } 346 347 // @return true if client valid / run function should be called 348 static bool gatt_service_client_handle_query_complete(gatt_service_client_t *client, 349 gatt_service_client_connection_t *connection, 350 uint8_t status) { 351 btstack_assert(client != NULL); 352 btstack_assert(connection != NULL); 353 354 if (status != ATT_ERROR_SUCCESS){ 355 switch (connection->state){ 356 case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 357 case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 358 case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT: 359 case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED: 360 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status)); 361 gatt_service_client_finalize_connection(client, connection); 362 return false; 363 case GATT_SERVICE_CLIENT_STATE_CONNECTED: 364 break; 365 default: 366 btstack_unreachable(); 367 break; 368 } 369 } 370 371 switch (connection->state){ 372 case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 373 if (connection->service_instances_num == 0){ 374 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 375 gatt_service_client_finalize_connection(client, connection); 376 return false; 377 } 378 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS; 379 connection->characteristic_index = 0; 380 break; 381 382 case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 383 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS; 384 connection->characteristic_index = 0; 385 break; 386 387 case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT: 388 if (gatt_service_client_more_descriptor_queries(client, connection)){ 389 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS; 390 break; 391 } 392 393 connection->characteristic_index = 0; 394 if (gatt_service_client_have_more_notifications_to_enable(client, connection)){ 395 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION; 396 } else { 397 connection->characteristic_index = 0; 398 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED; 399 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS); 400 } 401 break; 402 403 case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED: 404 if (gatt_service_client_have_more_notifications_to_enable(client, connection)){ 405 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION; 406 } else { 407 // all notifications registered, start listening 408 gatt_client_service_t service; 409 service.start_group_handle = connection->start_handle; 410 service.end_group_handle = connection->end_handle; 411 412 gatt_client_listen_for_service_characteristic_value_updates(&connection->notification_listener, client->packet_handler, 413 connection->con_handle, &service, client->service_id, connection->cid); 414 415 connection->characteristic_index = 0; 416 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED; 417 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS); 418 } 419 420 break; 421 422 default: 423 break; 424 425 } 426 // TODO run_for_client 427 return true; 428 } 429 430 static uint8_t gatt_service_client_get_uninitialized_characteristic_index_for_uuid16( 431 gatt_service_client_t * client, 432 gatt_service_client_connection_t * connection, 433 uint16_t uuid16){ 434 435 uint8_t index = 0xff; 436 437 uint8_t i; 438 439 for (i = 0; i < client->characteristics_desc16_num; i++){ 440 if (client->characteristics_desc16[i] == uuid16){ 441 // allow for more then one instance of the same characteristic (as in OTS client) 442 if (connection->characteristics[i].value_handle != 0){ 443 continue; 444 } 445 index = i; 446 break; 447 } 448 } 449 return index; 450 } 451 452 static void gatt_service_client_handle_disconnect(hci_con_handle_t con_handle){ 453 btstack_linked_list_iterator_t service_it; 454 btstack_linked_list_iterator_init(&service_it, &gatt_service_clients); 455 while (btstack_linked_list_iterator_has_next(&service_it)){ 456 gatt_service_client_t * client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&service_it); 457 btstack_linked_list_iterator_t connection_it; 458 btstack_linked_list_iterator_init(&connection_it, &client->connections); 459 while (btstack_linked_list_iterator_has_next(&connection_it)){ 460 gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&connection_it); 461 if (connection->con_handle == con_handle) { 462 gatt_service_client_emit_disconnected(client->packet_handler, connection->con_handle, connection->cid); 463 gatt_service_client_finalize_connection(client, connection); 464 } 465 } 466 } 467 } 468 469 static void 470 gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 471 UNUSED(channel); 472 UNUSED(size); 473 474 if (packet_type != HCI_EVENT_PACKET) return; 475 476 gatt_service_client_t * client; 477 gatt_service_client_connection_t * connection; 478 gatt_client_service_t service; 479 gatt_client_characteristic_t characteristic; 480 gatt_client_characteristic_descriptor_t characteristic_descriptor; 481 uint8_t characteristic_index; 482 483 bool call_run = true; 484 switch (hci_event_packet_get_type(packet)){ 485 case GATT_EVENT_SERVICE_QUERY_RESULT: 486 client = gatt_service_client_get_service_client_for_id(gatt_event_service_query_result_get_service_id(packet)); 487 btstack_assert(client != NULL); 488 connection = gatt_service_client_get_connection_for_cid(client, gatt_event_service_query_result_get_connection_id(packet)); 489 btstack_assert(connection != NULL); 490 491 if (connection->service_instances_num < 1){ 492 gatt_event_service_query_result_get_service(packet, &service); 493 connection->start_handle = service.start_group_handle; 494 connection->end_handle = service.end_group_handle; 495 496 #ifdef ENABLE_TESTING_SUPPORT 497 printf("Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle); 498 #endif 499 connection->service_instances_num++; 500 } else { 501 log_info("Found more then one Service instance."); 502 } 503 break; 504 505 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 506 client = gatt_service_client_get_service_client_for_id(gatt_event_characteristic_query_result_get_service_id(packet)); 507 btstack_assert(client != NULL); 508 connection = gatt_service_client_get_connection_for_cid(client, gatt_event_characteristic_query_result_get_connection_id(packet)); 509 btstack_assert(connection != NULL); 510 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 511 512 characteristic_index = gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(client, connection, characteristic.uuid16); 513 if (characteristic_index < client->characteristics_desc16_num){ 514 connection->characteristics[characteristic_index].value_handle = characteristic.value_handle; 515 connection->characteristics[characteristic_index].properties = characteristic.properties; 516 connection->characteristics[characteristic_index].end_handle = characteristic.end_handle; 517 518 #ifdef ENABLE_TESTING_SUPPORT 519 printf(" [%u] Attribute Handle 0x%04X, Properties 0x%02X, Value Handle 0x%04X, UUID 0x%04X\n", 520 characteristic_index, characteristic.start_handle, 521 characteristic.properties, characteristic.value_handle, characteristic.uuid16); 522 #endif 523 } 524 break; 525 526 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 527 client = gatt_service_client_get_service_client_for_id(gatt_event_all_characteristic_descriptors_query_result_get_service_id(packet)); 528 btstack_assert(client != NULL); 529 connection = gatt_service_client_get_connection_for_cid(client, gatt_event_all_characteristic_descriptors_query_result_get_connection_id(packet)); 530 btstack_assert(connection != NULL); 531 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 532 533 if (characteristic_descriptor.uuid16 != ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){ 534 break; 535 } 536 btstack_assert(connection->state == GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT); 537 538 if ( ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u) || 539 ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u) 540 ){ 541 connection->characteristics[connection->characteristic_index].client_configuration_handle = characteristic_descriptor.handle; 542 } else { 543 connection->characteristics[connection->characteristic_index].client_configuration_handle = 0; 544 } 545 connection->characteristic_index++; 546 547 #ifdef ENABLE_TESTING_SUPPORT 548 printf(" Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n", 549 characteristic_descriptor.handle, 550 characteristic_descriptor.uuid16); 551 #endif 552 break; 553 554 case GATT_EVENT_QUERY_COMPLETE: 555 client = gatt_service_client_get_service_client_for_id(gatt_event_query_complete_get_service_id(packet)); 556 btstack_assert(client != NULL); 557 connection = gatt_service_client_get_connection_for_cid(client, gatt_event_query_complete_get_connection_id(packet)); 558 btstack_assert(connection != NULL); 559 call_run = gatt_service_client_handle_query_complete(client, connection, gatt_event_query_complete_get_att_status(packet)); 560 break; 561 562 default: 563 call_run = false; 564 break; 565 } 566 567 if (call_run){ 568 gatt_service_client_run_for_client(client, connection); 569 } 570 } 571 572 static void gatt_service_client_hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 573 UNUSED(channel); 574 UNUSED(size); 575 UNUSED(packet); 576 UNUSED(size); 577 578 if (packet_type != HCI_EVENT_PACKET) return; 579 580 hci_con_handle_t con_handle; 581 switch (hci_event_packet_get_type(packet)) { 582 case HCI_EVENT_DISCONNECTION_COMPLETE: 583 con_handle = hci_event_disconnection_complete_get_connection_handle(packet); 584 gatt_service_client_handle_disconnect(con_handle); 585 break; 586 default: 587 break; 588 } 589 } 590 591 /* API */ 592 593 void gatt_service_client_init(void){ 594 if (false == gatt_service_client_intitialized){ 595 gatt_service_client_hci_callback_registration.callback = gatt_service_client_hci_event_handler; 596 hci_add_event_handler(&gatt_service_client_hci_callback_registration); 597 gatt_service_client_intitialized = true; 598 } 599 } 600 601 void gatt_service_client_register_client(gatt_service_client_t *client, btstack_packet_handler_t packet_handler, 602 const uint16_t *characteristic_uuid16s, uint16_t characteristic_uuid16s_num) { 603 604 btstack_assert(gatt_service_client_intitialized); 605 606 gatt_service_client_service_cid = btstack_next_cid_ignoring_zero(gatt_service_client_service_cid); 607 client->service_id =gatt_service_client_service_cid; 608 client->cid_counter = 0; 609 client->characteristics_desc16_num = 0; 610 client->packet_handler = packet_handler; 611 client->characteristics_desc16 = characteristic_uuid16s; 612 client->characteristics_desc16_num = characteristic_uuid16s_num; 613 btstack_linked_list_add(&gatt_service_clients, &client->item); 614 } 615 616 uint8_t 617 gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client, 618 gatt_service_client_connection_t *connection, 619 uint16_t service_uuid16, uint8_t service_index, 620 gatt_service_client_characteristic_t *characteristics, 621 uint8_t characteristics_num) { 622 623 btstack_assert(client != NULL); 624 btstack_assert(connection != NULL); 625 btstack_assert(characteristics != NULL); 626 627 if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){ 628 return ERROR_CODE_COMMAND_DISALLOWED; 629 } 630 631 if (characteristics_num < client->characteristics_desc16_num){ 632 log_info("At least %u characteristics needed", client->characteristics_desc16_num); 633 return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 634 } 635 636 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE; 637 connection->client = client; 638 connection->cid = gatt_service_client_get_next_cid(client); 639 connection->con_handle = con_handle; 640 connection->service_uuid16 = service_uuid16; 641 connection->service_index = service_index; 642 connection->characteristics = characteristics; 643 btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection); 644 645 gatt_service_client_run_for_client(client, connection); 646 return ERROR_CODE_SUCCESS; 647 } 648 649 uint8_t 650 gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client, 651 gatt_service_client_connection_t *connection, 652 uint16_t service_uuid16, uint8_t service_index, 653 uint16_t service_start_handle, uint16_t service_end_handle, 654 gatt_service_client_characteristic_t *characteristics, 655 uint8_t characteristics_num) { 656 657 btstack_assert(client != NULL); 658 btstack_assert(connection != NULL); 659 btstack_assert(characteristics != NULL); 660 btstack_assert(characteristics_num >= client->characteristics_desc16_num); 661 662 if (gatt_service_client_get_connection_for_con_handle_and_attribute_handle(client, con_handle, service_start_handle) != NULL){ 663 return ERROR_CODE_COMMAND_DISALLOWED; 664 } 665 if (gatt_service_client_get_connection_for_con_handle_and_attribute_handle(client, con_handle, service_end_handle) != NULL){ 666 return ERROR_CODE_COMMAND_DISALLOWED; 667 } 668 669 uint16_t cid = gatt_service_client_get_next_cid(client); 670 671 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS; 672 connection->client = client; 673 connection->cid = cid; 674 connection->con_handle = con_handle; 675 connection->service_uuid16 = service_uuid16; 676 connection->service_index = service_index; 677 connection->start_handle = service_start_handle; 678 connection->end_handle = service_end_handle; 679 connection->characteristics = characteristics; 680 btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection); 681 682 gatt_service_client_run_for_client(client, connection); 683 return ERROR_CODE_SUCCESS; 684 } 685 686 uint8_t 687 gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t *connection, 688 uint8_t characteristic_index) { 689 if (connection->state != GATT_SERVICE_CLIENT_STATE_CONNECTED){ 690 return ERROR_CODE_COMMAND_DISALLOWED; 691 } 692 693 if (connection->characteristics[characteristic_index].value_handle == 0){ 694 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 695 } 696 return ERROR_CODE_SUCCESS; 697 } 698 699 uint8_t gatt_service_client_disconnect(gatt_service_client_connection_t *connection) { 700 // finalize connections 701 gatt_service_client_emit_disconnected(connection->client->packet_handler, connection->con_handle, connection->cid); 702 gatt_service_client_finalize_connection(connection->client, connection); 703 return ERROR_CODE_SUCCESS; 704 } 705 706 void gatt_service_client_unregister_client(gatt_service_client_t * client){ 707 btstack_assert(client != NULL); 708 709 client->packet_handler = NULL; 710 711 client->cid_counter = 0; 712 client->characteristics_desc16_num = 0; 713 714 btstack_linked_list_iterator_t it; 715 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 716 while (btstack_linked_list_iterator_has_next(&it)){ 717 gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 718 gatt_service_client_finalize_connection(client, connection); 719 } 720 721 btstack_linked_list_remove(&gatt_service_clients, &client->item); 722 } 723 724 void gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t *connection, 725 const char **characteristic_names) { 726 #ifdef ENABLE_TESTING_SUPPORT 727 uint8_t i; 728 for (i = 0; i < connection->client->characteristics_desc16_num; i++) { 729 printf("0x%04X %s\n", connection->characteristics[i].value_handle, characteristic_names[i]); 730 } 731 #else 732 UNUSED(connection); 733 UNUSED(characteristic_names); 734 #endif 735 } 736 737 void gatt_service_client_deinit(void){ 738 gatt_service_client_service_cid = 0; 739 gatt_service_clients = NULL; 740 gatt_service_client_intitialized = false; 741 } 742 743