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