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