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