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