xref: /btstack/src/ble/gatt-service/device_information_service_server.c (revision 639880bae58de1b4d95c577675d4860170dcdda9)
1a4355820SMatthias Ringwald /*
2a4355820SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3a4355820SMatthias Ringwald  *
4a4355820SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5a4355820SMatthias Ringwald  * modification, are permitted provided that the following conditions
6a4355820SMatthias Ringwald  * are met:
7a4355820SMatthias Ringwald  *
8a4355820SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9a4355820SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10a4355820SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11a4355820SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12a4355820SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13a4355820SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14a4355820SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15a4355820SMatthias Ringwald  *    from this software without specific prior written permission.
16a4355820SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17a4355820SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18a4355820SMatthias Ringwald  *    monetary gain.
19a4355820SMatthias Ringwald  *
20a4355820SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21a4355820SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22a4355820SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25a4355820SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26a4355820SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27a4355820SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28a4355820SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29a4355820SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30a4355820SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31a4355820SMatthias Ringwald  * SUCH DAMAGE.
32a4355820SMatthias Ringwald  *
33a4355820SMatthias Ringwald  * Please inquire about commercial licensing options at
34a4355820SMatthias Ringwald  * [email protected]
35a4355820SMatthias Ringwald  *
36a4355820SMatthias Ringwald  */
37a4355820SMatthias Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "device_information_service_server.c"
39ab2c6ae4SMatthias Ringwald 
40a4355820SMatthias Ringwald /**
41a4355820SMatthias Ringwald  * Implementation of the Device Information Service Server
42a4355820SMatthias Ringwald  *
4367b65e8fSChristian Erhardt  * To use with your application, add `#import <device_information_sevice.gatt>` to your .gatt file
44a4355820SMatthias Ringwald  * and call all functions below. All strings and blobs need to stay valid after calling the functions.
45a4355820SMatthias Ringwald  *
46a4355820SMatthias Ringwald  * @note: instead of calling all setters, you can create a local copy of the .gatt file and remove
47a4355820SMatthias Ringwald  * all Characteristics that are not relevant for your application.
48a4355820SMatthias Ringwald  */
49a4355820SMatthias Ringwald 
50a4355820SMatthias Ringwald 
51a4355820SMatthias Ringwald #include "btstack_defines.h"
52a4355820SMatthias Ringwald #include "ble/att_db.h"
53a4355820SMatthias Ringwald #include "ble/att_server.h"
54a4355820SMatthias Ringwald #include "btstack_util.h"
55a4355820SMatthias Ringwald #include "bluetooth_gatt.h"
561da84c09SMilanka Ringwald #include "btstack_debug.h"
57a4355820SMatthias Ringwald 
58a4355820SMatthias Ringwald #include "ble/gatt-service/device_information_service_server.h"
59a4355820SMatthias Ringwald 
60afb581afSMatthias Ringwald #define DEVICE_INFORMATION_MAX_STRING_LEN 32
61afb581afSMatthias Ringwald 
62afb581afSMatthias Ringwald #define UDI_FOR_MEDICAL_DEVICES_BITMASK_LABEL 			1
63afb581afSMatthias Ringwald #define UDI_FOR_MEDICAL_DEVICES_BITMASK_DEVICE_ID		2
64afb581afSMatthias Ringwald #define UDI_FOR_MEDICAL_DEVICES_BITMASK_ISSUER			4
65afb581afSMatthias Ringwald #define UDI_FOR_MEDICAL_DEVICES_BITMASK_AUTHORITY		8
66afb581afSMatthias Ringwald 
67a4355820SMatthias Ringwald typedef enum {
68a4355820SMatthias Ringwald 	MANUFACTURER_NAME = 0,
69a4355820SMatthias Ringwald 	MODEL_NUMBER,
70a4355820SMatthias Ringwald 	SERIAL_NUMBER,
71a4355820SMatthias Ringwald 	HARDWARE_REVISION,
72a4355820SMatthias Ringwald 	FIRMWARE_REVISION,
73a4355820SMatthias Ringwald 	SOFTWARE_REVISION,
74a4355820SMatthias Ringwald 	SYSTEM_ID,
75a4355820SMatthias Ringwald 	IEEE_REGULATORY_CERTIFICATION,
76a4355820SMatthias Ringwald 	PNP_ID,
77afb581afSMatthias Ringwald 	UDI_FOR_MEDICAL_DEVICES,
78a4355820SMatthias Ringwald 	NUM_INFORMATION_FIELDS
79a4355820SMatthias Ringwald } device_information_field_id_t;
80a4355820SMatthias Ringwald 
81a4355820SMatthias Ringwald typedef struct {
82a4355820SMatthias Ringwald 	uint8_t * data;
83a4355820SMatthias Ringwald 	uint16_t  len;
84a4355820SMatthias Ringwald 	uint16_t  value_handle;
85a4355820SMatthias Ringwald } device_information_field_t;
86a4355820SMatthias Ringwald 
87a4355820SMatthias Ringwald static device_information_field_t device_information_fields[NUM_INFORMATION_FIELDS];
88a4355820SMatthias Ringwald 
89a4355820SMatthias Ringwald static uint8_t device_information_system_id[8];
90a4355820SMatthias Ringwald static uint8_t device_information_ieee_regulatory_certification[4];
91a4355820SMatthias Ringwald static uint8_t device_information_pnp_id[7];
92afb581afSMatthias Ringwald static uint8_t device_information_udi_for_medical_devices[1 + 4 * DEVICE_INFORMATION_MAX_STRING_LEN];
93a4355820SMatthias Ringwald 
94a4355820SMatthias Ringwald static att_service_handler_t       device_information_service;
95afb581afSMatthias Ringwald 
set_string(device_information_field_id_t field_id,const char * text)96a4355820SMatthias Ringwald static void set_string(device_information_field_id_t field_id, const char * text){
97a4355820SMatthias Ringwald 	device_information_fields[field_id].data = (uint8_t*) text;
983a672ff9SMilanka Ringwald 	device_information_fields[field_id].len  = text != NULL ? (uint8_t) strlen(text) : 0;
99a4355820SMatthias Ringwald }
100a4355820SMatthias Ringwald 
device_information_service_read_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)101a4355820SMatthias Ringwald static uint16_t device_information_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
1023721458bSMatthias Ringwald 	UNUSED(con_handle);	// ok: info same for all devices
10341abe6e6SMatthias Ringwald 	unsigned int i;
104a4355820SMatthias Ringwald 	for (i=0; i < NUM_INFORMATION_FIELDS; i++){
10508b8a6e4SMatthias Ringwald 		if ((device_information_fields[i].value_handle == attribute_handle) && (device_information_fields[i].data != NULL)){
106e301411eSMatthias Ringwald 			return att_read_callback_handle_blob(device_information_fields[i].data, device_information_fields[i].len, offset, buffer, buffer_size);
10741abe6e6SMatthias Ringwald 		};
108a4355820SMatthias Ringwald 	}
109a4355820SMatthias Ringwald 	return 0;
110a4355820SMatthias Ringwald }
111a4355820SMatthias Ringwald 
device_information_service_server_init(void)112a4355820SMatthias Ringwald void device_information_service_server_init(void){
113a4355820SMatthias Ringwald 
1148334d3d8SMatthias Ringwald     const uint16_t device_information_characteristic_uuids[] = {
1158334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING,
1168334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING,
1178334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING,
1188334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING,
1198334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING,
1208334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING,
1218334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID,
1228334d3d8SMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST,
123afb581afSMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID,
124afb581afSMatthias Ringwald             ORG_BLUETOOTH_CHARACTERISTIC_UDI_FOR_MEDICAL_DEVICES
1258334d3d8SMatthias Ringwald     };
1268334d3d8SMatthias Ringwald 
1278334d3d8SMatthias Ringwald 
128a4355820SMatthias Ringwald     // get service handle range
129e8944524SMatthias Ringwald     uint16_t start_handle = 0;
130e8944524SMatthias Ringwald     uint16_t end_handle   = 0xffff;
131c436b760SMilanka Ringwald 	int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION, &start_handle, &end_handle);
132e86f2b50SMatthias Ringwald 	btstack_assert(service_found != 0);
133e86f2b50SMatthias Ringwald 	UNUSED(service_found);
134a4355820SMatthias Ringwald 
1350841522cSMatthias Ringwald 	// set length for fixed size characateristics
1360841522cSMatthias Ringwald 	device_information_fields[SYSTEM_ID].data = device_information_system_id;
137afb581afSMatthias Ringwald 	device_information_fields[SYSTEM_ID].len = sizeof(device_information_system_id);
1380841522cSMatthias Ringwald 	device_information_fields[IEEE_REGULATORY_CERTIFICATION].data = device_information_ieee_regulatory_certification;
139afb581afSMatthias Ringwald 	device_information_fields[IEEE_REGULATORY_CERTIFICATION].len = sizeof(device_information_ieee_regulatory_certification);
1400841522cSMatthias Ringwald 	device_information_fields[PNP_ID].data = device_information_pnp_id;
141afb581afSMatthias Ringwald 	device_information_fields[PNP_ID].len = sizeof(device_information_pnp_id);
142afb581afSMatthias Ringwald 	// reserve max space for UDI
143afb581afSMatthias Ringwald 	device_information_fields[UDI_FOR_MEDICAL_DEVICES].data = device_information_udi_for_medical_devices;
144afb581afSMatthias Ringwald 	device_information_fields[UDI_FOR_MEDICAL_DEVICES].len = 0;
1450841522cSMatthias Ringwald 
146a4355820SMatthias Ringwald 	// get characteristic value handles
147a4355820SMatthias Ringwald 	int i;
148a4355820SMatthias Ringwald 	for (i=0; i < NUM_INFORMATION_FIELDS; i++){
149a4355820SMatthias Ringwald 		device_information_fields[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, device_information_characteristic_uuids[i]);
150a4355820SMatthias Ringwald 	}
151a4355820SMatthias Ringwald 
1523d71c7a4SMatthias Ringwald 	// register service with ATT Server
153a4355820SMatthias Ringwald 	device_information_service.start_handle   = start_handle;
154a4355820SMatthias Ringwald 	device_information_service.end_handle     = end_handle;
155a4355820SMatthias Ringwald 	device_information_service.read_callback  = &device_information_service_read_callback;
156a4355820SMatthias Ringwald 	device_information_service.write_callback = NULL;
1573d71c7a4SMatthias Ringwald 	att_server_register_service_handler(&device_information_service);
158a4355820SMatthias Ringwald }
159a4355820SMatthias Ringwald 
160a4355820SMatthias Ringwald /**
161a4355820SMatthias Ringwald  * @brief Set Manufacturer Name
162a4355820SMatthias Ringwald  * @param manufacturer_name
163a4355820SMatthias Ringwald  */
device_information_service_server_set_manufacturer_name(const char * manufacturer_name)164a4355820SMatthias Ringwald void device_information_service_server_set_manufacturer_name(const char * manufacturer_name){
165a4355820SMatthias Ringwald 	set_string(MANUFACTURER_NAME, manufacturer_name);
166a4355820SMatthias Ringwald }
167a4355820SMatthias Ringwald 
168a4355820SMatthias Ringwald /**
169a4355820SMatthias Ringwald  * @brief Set Model Number
170a4355820SMatthias Ringwald  * @param model_number
171a4355820SMatthias Ringwald  */
device_information_service_server_set_model_number(const char * model_number)172a4355820SMatthias Ringwald void device_information_service_server_set_model_number(const char * model_number){
173a4355820SMatthias Ringwald 	set_string(MODEL_NUMBER, model_number);
174a4355820SMatthias Ringwald }
175a4355820SMatthias Ringwald 
176a4355820SMatthias Ringwald /**
177a4355820SMatthias Ringwald  * @brief Set Serial Number
178a4355820SMatthias Ringwald  * @param serial_number
179a4355820SMatthias Ringwald  */
device_information_service_server_set_serial_number(const char * serial_number)180a4355820SMatthias Ringwald void device_information_service_server_set_serial_number(const char * serial_number){
181a4355820SMatthias Ringwald 	set_string(SERIAL_NUMBER, serial_number);
182a4355820SMatthias Ringwald }
183a4355820SMatthias Ringwald 
184a4355820SMatthias Ringwald /**
185a4355820SMatthias Ringwald  * @brief Set Hardware Revision
186a4355820SMatthias Ringwald  * @param hardware_revision
187a4355820SMatthias Ringwald  */
device_information_service_server_set_hardware_revision(const char * hardware_revision)188a4355820SMatthias Ringwald void device_information_service_server_set_hardware_revision(const char * hardware_revision){
189a4355820SMatthias Ringwald 	set_string(HARDWARE_REVISION, hardware_revision);
190a4355820SMatthias Ringwald }
191a4355820SMatthias Ringwald 
192a4355820SMatthias Ringwald /**
193a4355820SMatthias Ringwald  * @brief Set Firmware Revision
194a4355820SMatthias Ringwald  * @param firmware_revision
195a4355820SMatthias Ringwald  */
device_information_service_server_set_firmware_revision(const char * firmware_revision)196a4355820SMatthias Ringwald void device_information_service_server_set_firmware_revision(const char * firmware_revision){
197a4355820SMatthias Ringwald 	set_string(FIRMWARE_REVISION, firmware_revision);
198a4355820SMatthias Ringwald }
199a4355820SMatthias Ringwald 
200a4355820SMatthias Ringwald /**
201a4355820SMatthias Ringwald  * @brief Set Software Revision
202a4355820SMatthias Ringwald  * @param software_revision
203a4355820SMatthias Ringwald  */
device_information_service_server_set_software_revision(const char * software_revision)204a4355820SMatthias Ringwald void device_information_service_server_set_software_revision(const char * software_revision){
205a4355820SMatthias Ringwald 	set_string(SOFTWARE_REVISION, software_revision);
206a4355820SMatthias Ringwald }
207a4355820SMatthias Ringwald 
208a4355820SMatthias Ringwald /**
209a4355820SMatthias Ringwald  * @brief Set System ID
210a4355820SMatthias Ringwald  * @param manufacturer_identifier uint40
211a4355820SMatthias Ringwald  * @param organizationally_unique_identifier uint24
212a4355820SMatthias Ringwald  */
device_information_service_server_set_system_id(uint64_t manufacturer_identifier,uint32_t organizationally_unique_identifier)213a4355820SMatthias Ringwald void device_information_service_server_set_system_id(uint64_t manufacturer_identifier, uint32_t organizationally_unique_identifier){
214c1dbba9dSMatthias Ringwald 	little_endian_store_32(device_information_system_id, 0, (uint32_t)(manufacturer_identifier & 0xffffffff));
215c1dbba9dSMatthias Ringwald 	device_information_system_id[4] = (uint8_t) (manufacturer_identifier >> 32);
216a4355820SMatthias Ringwald 	little_endian_store_16(device_information_system_id, 5, organizationally_unique_identifier);
217a4355820SMatthias Ringwald 	device_information_system_id[7] = organizationally_unique_identifier >> 16;
218a4355820SMatthias Ringwald }
219a4355820SMatthias Ringwald 
220a4355820SMatthias Ringwald /**
221a4355820SMatthias Ringwald  * @brief Set IEEE 11073-20601 regulatory certification data list
222a4355820SMatthias Ringwald  * @note: format duint16. duint16 is two uint16 values concatenated together.
223a4355820SMatthias Ringwald  * @param ieee_regulatory_certification
224a4355820SMatthias Ringwald  */
device_information_service_server_set_ieee_regulatory_certification(uint16_t value_a,uint16_t value_b)225a4355820SMatthias Ringwald void device_information_service_server_set_ieee_regulatory_certification(uint16_t value_a, uint16_t value_b){
226a4355820SMatthias Ringwald 	little_endian_store_16(device_information_ieee_regulatory_certification, 0, value_a);
227a4355820SMatthias Ringwald 	little_endian_store_16(device_information_ieee_regulatory_certification, 2, value_b);
228a4355820SMatthias Ringwald }
229a4355820SMatthias Ringwald 
device_information_service_server_set_pnp_id(uint8_t vendor_source_id,uint16_t vendor_id,uint16_t product_id,uint16_t product_version)230a4355820SMatthias Ringwald void device_information_service_server_set_pnp_id(uint8_t vendor_source_id, uint16_t vendor_id, uint16_t product_id, uint16_t product_version){
231a4355820SMatthias Ringwald 	device_information_pnp_id[0] = vendor_source_id;
2323721458bSMatthias Ringwald 	little_endian_store_16(device_information_pnp_id, 1, vendor_id);
2333721458bSMatthias Ringwald 	little_endian_store_16(device_information_pnp_id, 3, product_id);
2343721458bSMatthias Ringwald 	little_endian_store_16(device_information_pnp_id, 5, product_version);
235a4355820SMatthias Ringwald }
236afb581afSMatthias Ringwald 
237afb581afSMatthias Ringwald 
device_information_service_server_set_udi_for_medical_devices(const char * label,const char * device_id,const char * issuer,const char * authority)238afb581afSMatthias Ringwald void device_information_service_server_set_udi_for_medical_devices(const char * label, const char * device_id, const char * issuer, const char * authority){
2395ba953c2SMatthias Ringwald 
2405ba953c2SMatthias Ringwald     // suppress CppCheck warnings below. Buffer has size 1 + 4 * DEVICE_INFORMATION_MAX_STRING_LEN
2415ba953c2SMatthias Ringwald 	uint8_t * data = device_information_fields[UDI_FOR_MEDICAL_DEVICES].data;
242afb581afSMatthias Ringwald 
243afb581afSMatthias Ringwald 	uint16_t bytes_copied;
244afb581afSMatthias Ringwald 	uint16_t pos = 0;
245afb581afSMatthias Ringwald 	data[pos++] = 0; // reserved for flags
246afb581afSMatthias Ringwald 
2475ba953c2SMatthias Ringwald 
2485ba953c2SMatthias Ringwald     // cppcheck-suppress objectIndex
249*639880baSMilanka Ringwald     if (label != NULL){
250afb581afSMatthias Ringwald         bytes_copied = btstack_strcpy((char *) &data[pos], DEVICE_INFORMATION_MAX_STRING_LEN, label);
251afb581afSMatthias Ringwald         pos += bytes_copied;
252afb581afSMatthias Ringwald         data[0] |= (1 << UDI_FOR_MEDICAL_DEVICES_BITMASK_LABEL);
253afb581afSMatthias Ringwald     }
254afb581afSMatthias Ringwald 
255*639880baSMilanka Ringwald     if (device_id != NULL){
2565ba953c2SMatthias Ringwald         // cppcheck-suppress objectIndex
257afb581afSMatthias Ringwald         bytes_copied = btstack_strcpy((char *) &data[pos], DEVICE_INFORMATION_MAX_STRING_LEN, device_id);
258afb581afSMatthias Ringwald         pos += bytes_copied;
259afb581afSMatthias Ringwald         data[0] |= (1 << UDI_FOR_MEDICAL_DEVICES_BITMASK_DEVICE_ID);
260afb581afSMatthias Ringwald     }
261afb581afSMatthias Ringwald 
262*639880baSMilanka Ringwald     if (issuer != NULL) {
2635ba953c2SMatthias Ringwald         // cppcheck-suppress objectIndex
264afb581afSMatthias Ringwald         bytes_copied = btstack_strcpy((char *) &data[pos], DEVICE_INFORMATION_MAX_STRING_LEN, issuer);
265afb581afSMatthias Ringwald         pos += bytes_copied;
266afb581afSMatthias Ringwald         data[0] |= (1 << UDI_FOR_MEDICAL_DEVICES_BITMASK_ISSUER);
267afb581afSMatthias Ringwald     }
268afb581afSMatthias Ringwald 
269*639880baSMilanka Ringwald     if (authority != NULL){
2705ba953c2SMatthias Ringwald         // cppcheck-suppress objectIndex
271afb581afSMatthias Ringwald         bytes_copied = btstack_strcpy((char *) &data[pos], DEVICE_INFORMATION_MAX_STRING_LEN, authority);
272afb581afSMatthias Ringwald         pos += bytes_copied;
273afb581afSMatthias Ringwald         data[0] |= (1 << UDI_FOR_MEDICAL_DEVICES_BITMASK_AUTHORITY);
274afb581afSMatthias Ringwald     }
275afb581afSMatthias Ringwald 	device_information_fields[UDI_FOR_MEDICAL_DEVICES].len = pos;
276afb581afSMatthias Ringwald }
277afb581afSMatthias Ringwald 
278