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