xref: /btstack/src/ble/gatt_service_client.c (revision 13a624a6d940b0c052375dd10b7ae103edec0b14)
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