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