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