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