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
gatt_service_client_finalize_connection(gatt_service_client_t * client,gatt_service_client_connection_t * connection)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
gatt_service_client_get_service_client_for_id(uint16_t service_cid)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
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)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
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)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
gatt_service_client_get_connection_for_cid(const gatt_service_client_t * client,uint16_t connection_cid)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
gatt_service_client_get_mtu(const gatt_service_client_connection_t * connection)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
gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection)121 uint16_t gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection){
122 return connection->cid;
123
124 }
125
gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection)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
gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection)130 uint8_t gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection){
131 return connection->service_index;
132 }
133
gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client,uint8_t characteristic_index)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
gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t * connection,uint8_t characteristic_index)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
gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t * connection,uint16_t value_handle)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
gatt_service_client_emit_connected(btstack_packet_handler_t event_callback,hci_con_handle_t con_handle,uint16_t cid,uint8_t status)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
gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback,hci_con_handle_t con_handle,uint16_t cid)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
gatt_service_client_get_next_cid(gatt_service_client_t * client)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
gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client,gatt_service_client_connection_t * connection)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
gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client,gatt_service_client_connection_t * connection)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
gatt_service_client_register_notification(gatt_service_client_t * client,gatt_service_client_connection_t * connection)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
gatt_service_client_run_for_client(gatt_service_client_t * client,gatt_service_client_connection_t * connection)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
gatt_service_client_handle_query_complete(gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint8_t status)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
gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t uuid16)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
gatt_service_client_handle_disconnect(hci_con_handle_t con_handle)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
gatt_service_client_gatt_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)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
gatt_service_client_hci_event_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)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
gatt_service_client_init(void)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
gatt_service_client_register_client(gatt_service_client_t * client,btstack_packet_handler_t packet_handler,const uint16_t * characteristic_uuid16s,uint16_t characteristic_uuid16s_num)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
gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle,gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t service_uuid16,uint8_t service_index,gatt_service_client_characteristic_t * characteristics,uint8_t characteristics_num)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
gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle,gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t service_uuid16,uint8_t service_index,uint16_t service_start_handle,uint16_t service_end_handle,gatt_service_client_characteristic_t * characteristics,uint8_t characteristics_num)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
gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t * connection,uint8_t characteristic_index)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
gatt_service_client_disconnect(gatt_service_client_connection_t * connection)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
gatt_service_client_unregister_client(gatt_service_client_t * client)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
gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t * connection,const char ** characteristic_names)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
gatt_service_client_deinit(void)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