xref: /btstack/src/ble/gatt-service/device_information_service_client.c (revision bdf6d26abc1b93e8c1e8665fb025fb5d370ff36e)
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 MATTHIAS
24  * RINGWALD 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 "btstack_memory.h"
52 #include "ble/att_db.h"
53 #include "ble/core.h"
54 #include "ble/gatt_client.h"
55 #include "ble/sm.h"
56 #include "bluetooth_gatt.h"
57 #include "btstack_debug.h"
58 #include "btstack_event.h"
59 #include "btstack_run_loop.h"
60 #include "gap.h"
61 
62 #define DEVICE_INFORMATION_MAX_STRING_LEN 32
63 
64 
65 typedef enum {
66     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE,
67     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE,
68     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT,
69 #ifdef ENABLE_TESTING_SUPPORT
70     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS,
71     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
72 #endif
73     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC,
74     DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE
75 } device_information_service_client_state_t;
76 
77 typedef struct {
78     hci_con_handle_t con_handle;
79     device_information_service_client_state_t  state;
80     btstack_packet_handler_t client_handler;
81 
82     // service
83     uint16_t start_handle;
84     uint16_t end_handle;
85     uint8_t  num_instances;
86 
87     // index of next characteristic to query
88     uint8_t characteristic_index;
89 } device_information_service_client_t;
90 
91 static device_information_service_client_t device_information_service_client;
92 
93 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
94 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);
95 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);
96 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);
97 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);
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_system_id},
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_string_value},
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 };
116 
117 #ifdef ENABLE_TESTING_SUPPORT
118 static struct device_information_characteristic_handles{
119     uint16_t uuid;
120     uint16_t value_handle;
121 } device_information_characteristic_handles[] = {
122     {ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING, 0},
123     {ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING, 0},
124     {ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING, 0},
125     {ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING, 0},
126     {ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING, 0},
127     {ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING, 0},
128     {ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID, 0},
129     {ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST, 0},
130     {ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID, 0}
131 };
132 
133 static void device_information_service_update_handle_for_uuid(uint16_t uuid, uint16_t value_handle){
134     uint8_t i;
135     for (i = 0; i < 9; i++){
136         if (device_information_characteristic_handles[i].uuid == uuid){
137             device_information_characteristic_handles[i].value_handle = value_handle;
138             return;
139         }
140     }
141 }
142 #endif
143 
144 
145 static const uint8_t num_information_fields = sizeof(device_information_characteristics)/sizeof(struct device_information_characteristic);
146 
147 #ifdef ENABLE_TESTING_SUPPORT
148 static char * device_information_characteristic_name(uint16_t uuid){
149     switch (uuid){
150         case ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING:
151             return "MANUFACTURER_NAME_STRING";
152 
153         case ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING:
154             return "MODEL_NUMBER_STRING";
155 
156         case ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING:
157             return "SERIAL_NUMBER_STRING";
158 
159         case ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING:
160             return "HARDWARE_REVISION_STRING";
161 
162         case ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING:
163             return "FIRMWARE_REVISION_STRING";
164 
165         case ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING:
166             return "SOFTWARE_REVISION_STRING";
167 
168         case ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID:
169             return "SYSTEM_ID";
170 
171         case ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST:
172             return "IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST";
173 
174         case ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID:
175             return "PNP_ID";
176         default:
177             return "UKNOWN";
178     }
179 }
180 #endif
181 static device_information_service_client_t * device_information_service_client_get_client(void){
182     return &device_information_service_client;
183 }
184 
185 static device_information_service_client_t * device_information_service_get_client_for_con_handle(hci_con_handle_t con_handle){
186     if (device_information_service_client.con_handle == con_handle){
187         return &device_information_service_client;
188     }
189     return NULL;
190 }
191 
192 static void device_information_service_finalize_client(device_information_service_client_t * client){
193     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE;
194     client->con_handle = HCI_CON_HANDLE_INVALID;
195 }
196 
197 static void device_information_service_emit_query_done(device_information_service_client_t * client, uint8_t att_status){
198     uint8_t event[6];
199     int pos = 0;
200     event[pos++] = HCI_EVENT_GATTSERVICE_META;
201     event[pos++] = sizeof(event) - 2;
202     event[pos++] = GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_DONE;
203     little_endian_store_16(event, pos, client->con_handle);
204     pos += 2;
205     event[pos++] = att_status;
206     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
207 }
208 
209 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){
210     uint8_t event[6 + DEVICE_INFORMATION_MAX_STRING_LEN + 1];
211     int pos = 0;
212 
213     event[pos++] = HCI_EVENT_GATTSERVICE_META;
214     pos++;
215     event[pos++] = subevent;
216     little_endian_store_16(event, pos, client->con_handle);
217     pos += 2;
218     event[pos++] = att_status;
219 
220     uint16_t bytes_to_copy = btstack_min(value_len, DEVICE_INFORMATION_MAX_STRING_LEN);
221     memcpy((char*)&event[pos], value, bytes_to_copy);
222     pos += bytes_to_copy;
223     event[pos++] = 0;
224 
225     event[1] = pos - 2;
226     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
227 }
228 
229 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){
230     if (value_len != 8) return;
231 
232     uint8_t event[14];
233     uint16_t pos = 0;
234     event[pos++] = HCI_EVENT_GATTSERVICE_META;
235     event[pos++] = sizeof(event) - 2;
236     event[pos++] = subevent;
237     little_endian_store_16(event, pos, client->con_handle);
238     pos += 2;
239     event[pos++] = att_status;
240     memcpy(event+pos, value, 8);
241     pos += 8;
242 
243     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
244 }
245 
246 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){
247     if (value_len != 4) return;
248 
249     uint8_t event[10];
250     int pos = 0;
251     event[pos++] = HCI_EVENT_GATTSERVICE_META;
252     event[pos++] = sizeof(event) - 2;
253     event[pos++] = subevent;
254     little_endian_store_16(event, pos, client->con_handle);
255     pos += 2;
256     event[pos++] = att_status;
257     memcpy(event + pos, value, 4);
258     pos += 4;
259 
260     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
261 }
262 
263 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){
264     if (value_len != 7) return;
265 
266     uint8_t event[13];
267     uint16_t pos = 0;
268     event[pos++] = HCI_EVENT_GATTSERVICE_META;
269     event[pos++] = sizeof(event) - 2;
270     event[pos++] = subevent;
271     little_endian_store_16(event, pos, client->con_handle);
272     pos += 2;
273     event[pos++] = att_status;
274     memcpy(event + pos, value, 7);
275     pos += 7;
276 
277     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, pos);
278 }
279 
280 
281 static void device_information_service_run_for_client(device_information_service_client_t * client){
282     uint8_t att_status;
283 
284     switch (client->state){
285         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
286             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
287             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION);
288             // TODO handle status
289             UNUSED(att_status);
290             break;
291 #ifdef ENABLE_TESTING_SUPPORT
292         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
293             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
294             gatt_client_discover_characteristics_for_handle_range_by_uuid16(
295                 &handle_gatt_client_event,
296                 client->con_handle, client->start_handle, client->end_handle,
297                 device_information_characteristics[client->characteristic_index].uuid);
298             break;
299 #endif
300 
301         case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC:
302             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE;
303 
304  #ifdef ENABLE_TESTING_SUPPORT
305             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
306                 handle_gatt_client_event,
307                 client->con_handle,
308                 device_information_characteristic_handles[client->characteristic_index].value_handle);
309  #else
310             att_status = gatt_client_read_value_of_characteristics_by_uuid16(
311                 handle_gatt_client_event,
312                 client->con_handle, client->start_handle, client->end_handle,
313                 device_information_characteristics[client->characteristic_index].uuid);
314 #endif
315             // TODO handle status
316             UNUSED(att_status);
317             break;
318 
319         default:
320             break;
321     }
322 }
323 
324 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
325     UNUSED(packet_type);
326     UNUSED(channel);
327     UNUSED(size);
328 
329     uint8_t att_status;
330     device_information_service_client_t * client = NULL;
331     gatt_client_service_t service;
332 
333 #ifdef ENABLE_TESTING_SUPPORT
334     gatt_client_characteristic_t characteristic;
335 #endif
336 
337     switch(hci_event_packet_get_type(packet)){
338 
339         case GATT_EVENT_SERVICE_QUERY_RESULT:
340             client = device_information_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
341             btstack_assert(client != NULL);
342 
343             if (client->state != DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) {
344                 device_information_service_emit_query_done(client, GATT_CLIENT_IN_WRONG_STATE);
345                 device_information_service_finalize_client(client);
346                 break;
347             }
348 
349             gatt_event_service_query_result_get_service(packet, &service);
350             client->start_handle = service.start_group_handle;
351             client->end_handle = service.end_group_handle;
352 #ifdef ENABLE_TESTING_SUPPORT
353             printf("Device Information Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle);
354 #endif
355             client->num_instances++;
356             client->characteristic_index = 0;
357             break;
358 
359 #ifdef ENABLE_TESTING_SUPPORT
360         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
361             client = device_information_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
362             btstack_assert(client != NULL);
363             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
364 
365             device_information_service_update_handle_for_uuid(characteristic.uuid16, characteristic.value_handle);
366             printf("Device Information Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
367                 device_information_characteristic_name(characteristic.uuid16),
368                 characteristic.start_handle,
369                 characteristic.properties,
370                 characteristic.value_handle, characteristic.uuid16);
371             break;
372 #endif
373         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
374             client = device_information_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
375             btstack_assert(client != NULL);
376 
377 
378             (device_information_characteristics[client->characteristic_index].handle_value(
379                 client, device_information_characteristics[client->characteristic_index].subevent,
380                 ATT_ERROR_SUCCESS,
381                 gatt_event_characteristic_value_query_result_get_value(packet),
382                 gatt_event_characteristic_value_query_result_get_value_length(packet)));
383             break;
384 
385         case GATT_EVENT_QUERY_COMPLETE:
386             client = device_information_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
387             btstack_assert(client != NULL);
388 
389             att_status = gatt_event_query_complete_get_att_status(packet);
390             switch (client->state){
391                 case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
392                     if (att_status != ATT_ERROR_SUCCESS){
393                         device_information_service_emit_query_done(client, att_status);
394                         device_information_service_finalize_client(client);
395                         break;
396                     }
397 
398                     if (client->num_instances != 1){
399                         device_information_service_emit_query_done(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
400                         device_information_service_finalize_client(client);
401                         break;
402                     }
403                     client->characteristic_index = 0;
404 #ifdef ENABLE_TESTING_SUPPORT
405                     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
406 #else
407                     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
408 #endif
409                     break;
410 
411 #ifdef ENABLE_TESTING_SUPPORT
412                     case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
413                         // check if there is another characteristic to query
414                         if ((client->characteristic_index + 1) < num_information_fields){
415                             client->characteristic_index++;
416                             client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
417                             break;
418                         }
419                         client->characteristic_index = 0;
420                         client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
421                         break;
422 #endif
423                 case DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE:
424                     if (att_status != ATT_ERROR_SUCCESS){
425                         (device_information_characteristics[client->characteristic_index].handle_value(
426                             client, device_information_characteristics[client->characteristic_index].subevent,
427                             att_status,
428                             gatt_event_characteristic_value_query_result_get_value(packet),
429                             gatt_event_characteristic_value_query_result_get_value_length(packet)));
430                     }
431 
432                     // check if there is another characteristic to query
433                     if ((client->characteristic_index + 1) < num_information_fields){
434                         client->characteristic_index++;
435                         client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_READ_VALUE_OF_CHARACTERISTIC;
436                         break;
437                     }
438                     // we are done with quering all characteristics
439                     device_information_service_emit_query_done(client, ERROR_CODE_SUCCESS);
440                     device_information_service_finalize_client(client);
441                     break;
442 
443                 default:
444                     break;
445             }
446             break;
447         default:
448             break;
449     }
450 
451     if (client != NULL){
452         device_information_service_run_for_client(client);
453     }
454 
455 }
456 
457 uint8_t device_information_service_client_query(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler){
458     btstack_assert(packet_handler != NULL);
459     device_information_service_client_t * client = device_information_service_client_get_client();
460 
461     if (client->state != DEVICE_INFORMATION_SERVICE_CLIENT_STATE_IDLE){
462         return GATT_CLIENT_IN_WRONG_STATE;
463     }
464 
465     client->con_handle = con_handle;
466     client->client_handler = packet_handler;
467     client->state = DEVICE_INFORMATION_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
468     device_information_service_run_for_client(client);
469     return ERROR_CODE_SUCCESS;
470 }
471 
472 
473 void device_information_service_client_init(void){}
474 
475 void device_information_service_client_deinit(void){}
476 
477