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