xref: /btstack/src/ble/gatt-service/device_information_service_client.c (revision c1b6583a190ace9247a1f81941d6be4382eb4f1e)
1 /*
2  * Copyright (C) 2021 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__ "device_information_service_client.c"
39 
40 #include "btstack_config.h"
41 
42 #include <stdint.h>
43 #include <string.h>
44 
45 #ifdef ENABLE_TESTING_SUPPORT
46 #include <stdio.h>
47 #endif
48 
49 #include "ble/gatt-service/device_information_service_client.h"
50 
51 #include "ble/core.h"
52 #include "ble/gatt_client.h"
53 #include "bluetooth_gatt.h"
54 #include "btstack_debug.h"
55 #include "btstack_event.h"
56 #include "gap.h"
57 
58 #define DEVICE_INFORMATION_MAX_STRING_LEN 32
59 
60 
61 typedef enum {
62     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE,
63     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE,
64     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT,
65 #ifdef ENABLE_TESTING_SUPPORT
66     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS,
67     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
68 #endif
69     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC,
70     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE
71 } device_information_service_client_state_t;
72 
73 typedef struct {
74     hci_con_handle_t con_handle;
75     device_information_service_client_state_t  state;
76     btstack_packet_handler_t client_handler;
77 
78     // service
79     uint16_t start_handle;
80     uint16_t end_handle;
81     uint8_t  num_instances;
82 
83     // index of next characteristic to query
84     uint8_t characteristic_index;
85 } device_information_service_client_t;
86 
87 
88 static btstack_context_callback_registration_t device_information_service_handle_can_send_now;
89 
90 static device_information_service_client_t device_information_service_client;
91 
92 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
93 static void device_information_service_emit_string_value(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
94 static void device_information_service_emit_system_id(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
95 static void device_information_service_emit_certification_data_list(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
96 static void device_information_service_emit_pnp_id(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
97 static void device_information_service_emit_udi_for_medical_devices(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
98 
99 // list of uuids and how they are reported as events
100 static const struct device_information_characteristic {
101     uint16_t uuid;
102     uint8_t  subevent;
103     void (*handle_value)(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len);
104 } device_information_characteristics[] = {
105     {ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_MANUFACTURER_NAME, device_information_service_emit_string_value},
106     {ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_MODEL_NUMBER, device_information_service_emit_string_value},
107     {ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SERIAL_NUMBER, device_information_service_emit_string_value},
108     {ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_HARDWARE_REVISION, device_information_service_emit_string_value},
109     {ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_FIRMWARE_REVISION, device_information_service_emit_string_value},
110     {ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SOFTWARE_REVISION, device_information_service_emit_string_value},
111 
112     {ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SYSTEM_ID, device_information_service_emit_system_id},
113     {ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_IEEE_REGULATORY_CERTIFICATION, device_information_service_emit_certification_data_list},
114     {ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_PNP_ID, device_information_service_emit_pnp_id},
115     {ORG_BLUETOOTH_CHARACTERISTIC_UDI_FOR_MEDICAL_DEVICES, GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_UDI_FOR_MEDICAL_DEVICES, device_information_service_emit_udi_for_medical_devices}
116 };
117 
118 static uint8_t device_informatiom_client_request_send_gatt_query(device_information_service_client_t * client){
119     uint8_t status = gatt_client_request_to_send_gatt_query(&device_information_service_handle_can_send_now, client->con_handle);
120     if (status != ERROR_CODE_SUCCESS){
121         if (client->state >= DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE){
122             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE;
123         }
124 
125     }
126     return status;
127 }
128 
129 #ifdef ENABLE_TESTING_SUPPORT
130 static struct device_information_characteristic_handles{
131     uint16_t uuid;
132     uint16_t value_handle;
133 } device_information_characteristic_handles[] = {
134     {ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING, 0},
135     {ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING, 0},
136     {ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING, 0},
137     {ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING, 0},
138     {ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING, 0},
139     {ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING, 0},
140     {ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID, 0},
141     {ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST, 0},
142     {ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID, 0},
143     {ORG_BLUETOOTH_CHARACTERISTIC_UDI_FOR_MEDICAL_DEVICES, 0}
144 };
145 
146 static void device_information_service_update_handle_for_uuid(uint16_t uuid, uint16_t value_handle){
147     uint8_t i;
148     for (i = 0; i < 9; i++){
149         if (device_information_characteristic_handles[i].uuid == uuid){
150             device_information_characteristic_handles[i].value_handle = value_handle;
151             return;
152         }
153     }
154 }
155 #endif
156 
157 
158 static const uint8_t num_information_fields = sizeof(device_information_characteristics)/sizeof(struct device_information_characteristic);
159 
160 #ifdef ENABLE_TESTING_SUPPORT
161 static char * device_information_characteristic_name(uint16_t uuid){
162     switch (uuid){
163         case ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING:
164             return "MANUFACTURER_NAME_STRING";
165 
166         case ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING:
167             return "MODEL_NUMBER_STRING";
168 
169         case ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING:
170             return "SERIAL_NUMBER_STRING";
171 
172         case ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING:
173             return "HARDWARE_REVISION_STRING";
174 
175         case ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING:
176             return "FIRMWARE_REVISION_STRING";
177 
178         case ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING:
179             return "SOFTWARE_REVISION_STRING";
180 
181         case ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID:
182             return "SYSTEM_ID";
183 
184         case ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST:
185             return "IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST";
186 
187         case ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID:
188             return "PNP_ID";
189 
190         case ORG_BLUETOOTH_CHARACTERISTIC_UDI_FOR_MEDICAL_DEVICES:
191             return "ORG_BLUETOOTH_CHARACTERISTIC_UDI_FOR_MEDICAL_DEVICES";
192 
193         default:
194             return "UKNOWN";
195     }
196 }
197 #endif
198 static device_information_service_client_t * device_information_service_client_get_client(void){
199     return &device_information_service_client;
200 }
201 
202 static device_information_service_client_t * device_information_service_get_client_for_con_handle(hci_con_handle_t con_handle){
203     if (device_information_service_client.con_handle == con_handle){
204         return &device_information_service_client;
205     }
206     return NULL;
207 }
208 
209 static void device_information_service_finalize_client(device_information_service_client_t * client){
210     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE;
211     client->con_handle = HCI_CON_HANDLE_INVALID;
212     client->client_handler = NULL;
213     client->num_instances = 0;
214     client->start_handle = 0;
215     client->end_handle = 0;
216 }
217 
218 static void device_information_service_emit_query_done_and_finalize_client(device_information_service_client_t * client, uint8_t status){
219     hci_con_handle_t con_handle = client->con_handle;
220     btstack_packet_handler_t callback = client->client_handler;
221 
222     device_information_service_finalize_client(client);
223 
224     uint8_t event[6];
225     int pos = 0;
226     event[pos++] = HCI_EVENT_GATTSERVICE_META;
227     event[pos++] = sizeof(event) - 2;
228     event[pos++] = GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_DONE;
229     little_endian_store_16(event, pos, con_handle);
230     pos += 2;
231     event[pos++] = status;
232     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
233 }
234 
235 static void device_information_service_emit_string_value(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len){
236     uint8_t event[6 + DEVICE_INFORMATION_MAX_STRING_LEN + 1];
237     int pos = 0;
238 
239     event[pos++] = HCI_EVENT_GATTSERVICE_META;
240     pos++;
241     event[pos++] = subevent;
242     little_endian_store_16(event, pos, client->con_handle);
243     pos += 2;
244     event[pos++] = att_status;
245 
246     uint16_t bytes_to_copy = btstack_min(value_len, DEVICE_INFORMATION_MAX_STRING_LEN);
247     memcpy((char*)&event[pos], value, bytes_to_copy);
248     pos += bytes_to_copy;
249     event[pos++] = 0;
250 
251     event[1] = pos - 2;
252     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
253 }
254 
255 static void device_information_service_emit_system_id(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len){
256     if (value_len != 8) return;
257 
258     uint8_t event[14];
259     uint16_t pos = 0;
260     event[pos++] = HCI_EVENT_GATTSERVICE_META;
261     event[pos++] = sizeof(event) - 2;
262     event[pos++] = subevent;
263     little_endian_store_16(event, pos, client->con_handle);
264     pos += 2;
265     event[pos++] = att_status;
266     memcpy(event+pos, value, 8);
267     pos += 8;
268 
269     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
270 }
271 
272 static void device_information_service_emit_certification_data_list(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len){
273     if (value_len != 4) return;
274 
275     uint8_t event[10];
276     int pos = 0;
277     event[pos++] = HCI_EVENT_GATTSERVICE_META;
278     event[pos++] = sizeof(event) - 2;
279     event[pos++] = subevent;
280     little_endian_store_16(event, pos, client->con_handle);
281     pos += 2;
282     event[pos++] = att_status;
283     memcpy(event + pos, value, 4);
284     pos += 4;
285 
286     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
287 }
288 
289 static void device_information_service_emit_pnp_id(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len){
290     if (value_len != 7) return;
291 
292     uint8_t event[13];
293     uint16_t pos = 0;
294     event[pos++] = HCI_EVENT_GATTSERVICE_META;
295     event[pos++] = sizeof(event) - 2;
296     event[pos++] = subevent;
297     little_endian_store_16(event, pos, client->con_handle);
298     pos += 2;
299     event[pos++] = att_status;
300     memcpy(event + pos, value, 7);
301     pos += 7;
302 
303     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
304 }
305 
306 static void device_information_service_emit_udi_for_medical_devices(device_information_service_client_t * client, uint8_t subevent, uint8_t att_status, const uint8_t * value, uint16_t value_len){
307     uint16_t max_udi_length = 1 + 4 * DEVICE_INFORMATION_MAX_STRING_LEN;
308 
309     if (value_len > max_udi_length) return;
310 
311     uint8_t event[6 + 1 + 4 * DEVICE_INFORMATION_MAX_STRING_LEN];
312     uint16_t pos = 0;
313     event[pos++] = HCI_EVENT_GATTSERVICE_META;
314     event[pos++] = sizeof(event) - 2;
315     event[pos++] = subevent;
316     little_endian_store_16(event, pos, client->con_handle);
317     pos += 2;
318     event[pos++] = att_status;
319     memcpy(event + pos, value, value_len);
320     pos += value_len;
321 
322     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
323 }
324 
325 
326 static void device_information_service_send_next_query(void * context){
327     UNUSED(context);
328     device_information_service_client_t * client = device_information_service_client_get_client();
329 
330     if (client == NULL){
331         return;
332     }
333 
334     uint8_t att_status;
335 
336     switch (client->state){
337         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
338             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
339             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION);
340             // TODO handle status
341             UNUSED(att_status);
342             break;
343 #ifdef ENABLE_TESTING_SUPPORT
344         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
345             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
346             gatt_client_discover_characteristics_for_handle_range_by_uuid16(
347                 &handle_gatt_client_event,
348                 client->con_handle, client->start_handle, client->end_handle,
349                 device_information_characteristics[client->characteristic_index].uuid);
350             break;
351 #endif
352 
353         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC:
354             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE;
355 
356 #ifdef ENABLE_TESTING_SUPPORT
357             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
358                 handle_gatt_client_event,
359                 client->con_handle,
360                 device_information_characteristic_handles[client->characteristic_index].value_handle);
361 #else
362             att_status = gatt_client_read_value_of_characteristics_by_uuid16(
363                 handle_gatt_client_event,
364                 client->con_handle, client->start_handle, client->end_handle,
365                 device_information_characteristics[client->characteristic_index].uuid);
366 #endif
367             // TODO handle status
368             UNUSED(att_status);
369             break;
370 
371         default:
372             break;
373     }
374 }
375 
376 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
377     UNUSED(packet_type);
378     UNUSED(channel);
379     UNUSED(size);
380 
381     uint8_t att_status;
382     device_information_service_client_t * client = NULL;
383     gatt_client_service_t service;
384     bool trigger_next_query = false;
385 #ifdef ENABLE_TESTING_SUPPORT
386     gatt_client_characteristic_t characteristic;
387 #endif
388 
389     switch(hci_event_packet_get_type(packet)){
390 
391         case GATT_EVENT_SERVICE_QUERY_RESULT:
392             client = device_information_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
393             btstack_assert(client != NULL);
394 
395             gatt_event_service_query_result_get_service(packet, &service);
396             client->start_handle = service.start_group_handle;
397             client->end_handle = service.end_group_handle;
398 
399             client->characteristic_index = 0;
400             if (client->start_handle < client->end_handle){
401                 client->num_instances++;
402             }
403 #ifdef ENABLE_TESTING_SUPPORT
404             printf("Device Information Service: start handle 0x%04X, end handle 0x%04X, num_instances %d\n", client->start_handle, client->end_handle, client->num_instances);
405 #endif
406             break;
407 
408 #ifdef ENABLE_TESTING_SUPPORT
409         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
410             client = device_information_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
411             btstack_assert(client != NULL);
412             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
413 
414             device_information_service_update_handle_for_uuid(characteristic.uuid16, characteristic.value_handle);
415             printf("Device Information Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
416                 device_information_characteristic_name(characteristic.uuid16),
417                 characteristic.start_handle,
418                 characteristic.properties,
419                 characteristic.value_handle, characteristic.uuid16);
420             break;
421 #endif
422         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
423             client = device_information_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
424             btstack_assert(client != NULL);
425 
426 
427             (device_information_characteristics[client->characteristic_index].handle_value(
428                 client, device_information_characteristics[client->characteristic_index].subevent,
429                 ATT_ERROR_SUCCESS,
430                 gatt_event_characteristic_value_query_result_get_value(packet),
431                 gatt_event_characteristic_value_query_result_get_value_length(packet)));
432             break;
433 
434         case GATT_EVENT_QUERY_COMPLETE:
435             client = device_information_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
436             btstack_assert(client != NULL);
437 
438             att_status = gatt_event_query_complete_get_att_status(packet);
439             switch (client->state){
440                 case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
441                     if (att_status != ATT_ERROR_SUCCESS){
442                         device_information_service_emit_query_done_and_finalize_client(client, att_status);
443                         return;
444                     }
445 
446                     if (client->num_instances != 1){
447                         device_information_service_emit_query_done_and_finalize_client(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
448                         return;
449                     }
450                     client->characteristic_index = 0;
451 
452 #ifdef ENABLE_TESTING_SUPPORT
453                     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
454 #else
455                     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
456 #endif
457                     trigger_next_query = true;
458                     break;
459 
460 #ifdef ENABLE_TESTING_SUPPORT
461                     case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
462                         // check if there is another characteristic to query
463                         if ((client->characteristic_index + 1) < num_information_fields){
464                             client->characteristic_index++;
465                             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
466                             trigger_next_query = true;
467                             break;
468                         }
469                         client->characteristic_index = 0;
470                         client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
471                         trigger_next_query = true;
472                         break;
473 #endif
474                 case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE:
475                     // check if there is another characteristic to query
476                     if ((client->characteristic_index + 1) < num_information_fields){
477                         client->characteristic_index++;
478                         client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
479                         trigger_next_query = true;
480                         break;
481                     }
482                     // we are done with quering all characteristics
483                     device_information_service_emit_query_done_and_finalize_client(client, ERROR_CODE_SUCCESS);
484                     return;
485 
486                 default:
487                     break;
488             }
489             break;
490         default:
491             break;
492     }
493 
494     if (trigger_next_query){
495         device_informatiom_client_request_send_gatt_query(client);
496     }
497 }
498 
499 uint8_t device_information_service_client_query(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler){
500     btstack_assert(packet_handler != NULL);
501     device_information_service_client_t * client = device_information_service_get_client_for_con_handle(con_handle);
502 
503     if (client != NULL){
504         return ERROR_CODE_COMMAND_DISALLOWED;
505     }
506 
507     client = device_information_service_client_get_client();
508 
509     if (client->con_handle != HCI_CON_HANDLE_INVALID) {
510         return ERROR_CODE_COMMAND_DISALLOWED;
511     }
512 
513     client->con_handle = con_handle;
514     client->client_handler = packet_handler;
515     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
516 
517     device_informatiom_client_request_send_gatt_query(client);
518     return ERROR_CODE_SUCCESS;
519 }
520 
521 
522 void device_information_service_client_init(void){
523     device_information_service_client_t * client = device_information_service_client_get_client();
524     device_information_service_finalize_client(client);
525     device_information_service_handle_can_send_now.callback = &device_information_service_send_next_query;
526 }
527 
528 void device_information_service_client_deinit(void){}
529 
530 // unit test only
531 #if defined __cplusplus
532 extern "C"
533 #endif
534 void device_information_service_client_set_invalid_state(void);
535 void device_information_service_client_set_invalid_state(void){
536     device_information_service_client_t * client =  device_information_service_client_get_client();
537     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE;
538 }
539