1 /** @file
2  *  @brief GATT Device Information Service
3  */
4 
5 /*
6  * Copyright (c) 2019 Demant
7  * Copyright (c) 2018 Nordic Semiconductor ASA
8  * Copyright (c) 2016 Intel Corporation
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 
13 #include <zephyr/types.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <zephyr/kernel.h>
18 #include <zephyr/init.h>
19 
20 #include <zephyr/settings/settings.h>
21 
22 #include <zephyr/bluetooth/bluetooth.h>
23 #include <zephyr/bluetooth/hci.h>
24 #include <zephyr/bluetooth/conn.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/bluetooth/gatt.h>
27 
28 #include <zephyr/sys/byteorder.h>
29 #include <zephyr/bluetooth/byteorder.h>
30 
31 #define LOG_LEVEL CONFIG_BT_SERVICE_LOG_LEVEL
32 #include <zephyr/logging/log.h>
33 LOG_MODULE_REGISTER(bt_dis);
34 
35 #if CONFIG_BT_DIS_PNP
36 struct dis_pnp {
37 	uint8_t pnp_vid_src;
38 	uint16_t pnp_vid;
39 	uint16_t pnp_pid;
40 	uint16_t pnp_ver;
41 } __packed;
42 
43 static struct dis_pnp dis_pnp_id = {
44 	.pnp_vid_src = CONFIG_BT_DIS_PNP_VID_SRC,
45 	.pnp_vid = CONFIG_BT_DIS_PNP_VID,
46 	.pnp_pid = CONFIG_BT_DIS_PNP_PID,
47 	.pnp_ver = CONFIG_BT_DIS_PNP_VER,
48 };
49 #endif
50 
51 #if defined(CONFIG_BT_DIS_SYSTEM_ID)
52 /*
53  * Casting to uint64_t since the value is at most 5 bytes, but will appear as a 32-bit literal if it
54  * is less, giving a warning when right-shifting by 32.
55  */
56 static uint8_t dis_system_id[8] = {BT_BYTES_LIST_LE40((uint64_t)CONFIG_BT_DIS_SYSTEM_ID_IDENTIFIER),
57 				   BT_BYTES_LIST_LE24(CONFIG_BT_DIS_SYSTEM_ID_OUI)};
58 #endif
59 
60 #if defined(CONFIG_BT_DIS_SETTINGS)
61 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MODEL) <= CONFIG_BT_DIS_STR_MAX + 1);
62 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_MANUF) <= CONFIG_BT_DIS_STR_MAX + 1);
63 static uint8_t dis_model[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MODEL;
64 static uint8_t dis_manuf[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_MANUF;
65 #if defined(CONFIG_BT_DIS_SERIAL_NUMBER)
66 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_SERIAL_NUMBER_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
67 static uint8_t dis_serial_number[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_SERIAL_NUMBER_STR;
68 #endif
69 #if defined(CONFIG_BT_DIS_FW_REV)
70 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_FW_REV_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
71 static uint8_t dis_fw_rev[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_FW_REV_STR;
72 #endif
73 #if defined(CONFIG_BT_DIS_HW_REV)
74 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_HW_REV_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
75 static uint8_t dis_hw_rev[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_HW_REV_STR;
76 #endif
77 #if defined(CONFIG_BT_DIS_SW_REV)
78 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_SW_REV_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
79 static uint8_t dis_sw_rev[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_SW_REV_STR;
80 #endif
81 #if defined(CONFIG_BT_DIS_UDI)
82 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_UDI_LABEL_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
83 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_UDI_DI_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
84 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_UDI_ISSUER_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
85 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_UDI_AUTHORITY_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
86 
87 static uint8_t dis_udi_label[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_UDI_LABEL_STR;
88 static uint8_t dis_udi_di[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_UDI_DI_STR;
89 static uint8_t dis_udi_issuer[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_UDI_ISSUER_STR;
90 static uint8_t dis_udi_authority[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_UDI_AUTHORITY_STR;
91 #endif
92 #if defined(CONFIG_BT_DIS_IEEE_RCDL)
93 BUILD_ASSERT(sizeof(CONFIG_BT_DIS_IEEE_RCDL_STR) <= CONFIG_BT_DIS_STR_MAX + 1);
94 static uint8_t dis_ieee_rcdl[CONFIG_BT_DIS_STR_MAX + 1] = CONFIG_BT_DIS_IEEE_RCDL_STR;
95 #endif
96 
97 #define BT_DIS_MODEL_REF             dis_model
98 #define BT_DIS_MANUF_REF             dis_manuf
99 #define BT_DIS_SERIAL_NUMBER_STR_REF dis_serial_number
100 #define BT_DIS_FW_REV_STR_REF        dis_fw_rev
101 #define BT_DIS_HW_REV_STR_REF        dis_hw_rev
102 #define BT_DIS_SW_REV_STR_REF        dis_sw_rev
103 #define BT_DIS_UDI_LABEL_STR_REF     dis_udi_label
104 #define BT_DIS_UDI_DI_STR_REF        dis_udi_di
105 #define BT_DIS_UDI_ISSUER_STR_REF    dis_udi_issuer
106 #define BT_DIS_UDI_AUTHORITY_STR_REF dis_udi_authority
107 #define BT_DIS_IEEE_RCDL_STR_REF     dis_ieee_rcdl
108 
109 /*
110  * When assigning too long string literals to the arrays,
111  * the literals may be truncated, removing the null terminator.
112  * Using strnlen to avoid sending data outside the array.
113  */
114 
115 #else /* CONFIG_BT_DIS_SETTINGS */
116 
117 #define BT_DIS_MODEL_REF             CONFIG_BT_DIS_MODEL
118 #define BT_DIS_MANUF_REF             CONFIG_BT_DIS_MANUF
119 #define BT_DIS_SERIAL_NUMBER_STR_REF CONFIG_BT_DIS_SERIAL_NUMBER_STR
120 #define BT_DIS_FW_REV_STR_REF        CONFIG_BT_DIS_FW_REV_STR
121 #define BT_DIS_HW_REV_STR_REF        CONFIG_BT_DIS_HW_REV_STR
122 #define BT_DIS_SW_REV_STR_REF        CONFIG_BT_DIS_SW_REV_STR
123 #define BT_DIS_UDI_LABEL_STR_REF     CONFIG_BT_DIS_UDI_LABEL_STR
124 #define BT_DIS_UDI_DI_STR_REF        CONFIG_BT_DIS_UDI_DI_STR
125 #define BT_DIS_UDI_ISSUER_STR_REF    CONFIG_BT_DIS_UDI_ISSUER_STR
126 #define BT_DIS_UDI_AUTHORITY_STR_REF CONFIG_BT_DIS_UDI_AUTHORITY_STR
127 #define BT_DIS_IEEE_RCDL_STR_REF     CONFIG_BT_DIS_IEEE_RCDL_STR
128 
129 #endif /* CONFIG_BT_DIS_SETTINGS */
130 
read_str(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)131 static ssize_t read_str(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
132 			uint16_t len, uint16_t offset)
133 {
134 	return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
135 				 strlen(attr->user_data));
136 }
137 
138 #if CONFIG_BT_DIS_PNP
read_pnp_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)139 static ssize_t read_pnp_id(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
140 			   uint16_t len, uint16_t offset)
141 {
142 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &dis_pnp_id, sizeof(dis_pnp_id));
143 }
144 #endif
145 
146 #if CONFIG_BT_DIS_SYSTEM_ID
read_system_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)147 static ssize_t read_system_id(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
148 			      uint16_t len, uint16_t offset)
149 {
150 	return bt_gatt_attr_read(conn, attr, buf, len, offset, dis_system_id,
151 				 sizeof(dis_system_id));
152 }
153 #endif
154 
155 #if CONFIG_BT_DIS_UDI
156 #define DIS_STR_SIZE(x)           ((x[0]) != '\0' ? strlen(x) + sizeof('\0') : 0)
157 #define BT_DIS_UDI_FLAG_LABEL     (!!BT_DIS_UDI_LABEL_STR_REF[0])
158 #define BT_DIS_UDI_FLAG_DI        (!!BT_DIS_UDI_DI_STR_REF[0])
159 #define BT_DIS_UDI_FLAG_ISSUER    (!!BT_DIS_UDI_ISSUER_STR_REF[0])
160 #define BT_DIS_UDI_FLAG_AUTHORITY (!!BT_DIS_UDI_AUTHORITY_STR_REF[0])
161 #define BT_DIS_UDI_FLAGS                                                                           \
162 	(BT_DIS_UDI_FLAG_LABEL | (BT_DIS_UDI_FLAG_DI << 1) | (BT_DIS_UDI_FLAG_ISSUER << 2) |       \
163 	 (BT_DIS_UDI_FLAG_AUTHORITY << 3))
164 
165 /*
166  * UDI for medical devices contains a flag and 4 different null-terminated strings that may have
167  * unknown length. This requires its own encode method.
168  */
read_udi_subval(const char * str,uint16_t val_len,char * buf,uint16_t * bytes_read,uint16_t * index,uint16_t len,uint16_t offset)169 static void read_udi_subval(const char *str, uint16_t val_len, char *buf, uint16_t *bytes_read,
170 			    uint16_t *index, uint16_t len, uint16_t offset)
171 {
172 	/* String should not be with included null-terminator if empty */
173 	if (val_len == sizeof('\0')) {
174 		return;
175 	}
176 
177 	if (*bytes_read == len) {
178 		return;
179 	}
180 
181 	if (*index + val_len < offset) {
182 		*index += val_len;
183 		return;
184 	}
185 
186 	for (uint16_t i = 0; i < val_len; i++) {
187 		if (*index >= offset && *bytes_read < len) {
188 			buf[*bytes_read] = str[i];
189 
190 			(*bytes_read)++;
191 		}
192 
193 		(*index)++;
194 	}
195 }
196 
read_udi(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)197 static ssize_t read_udi(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
198 			uint16_t len, uint16_t offset)
199 {
200 	uint16_t bytes_read = 0;
201 
202 	char *buf_i = (char *)buf;
203 
204 	/* Flag */
205 	uint16_t index = sizeof(uint8_t);
206 
207 	if (offset == 0) {
208 		buf_i[0] = BT_DIS_UDI_FLAGS;
209 		bytes_read = 1U;
210 	}
211 
212 	read_udi_subval(BT_DIS_UDI_LABEL_STR_REF, DIS_STR_SIZE(BT_DIS_UDI_LABEL_STR_REF), buf_i,
213 			&bytes_read, &index, len, offset);
214 	read_udi_subval(BT_DIS_UDI_DI_STR_REF, DIS_STR_SIZE(BT_DIS_UDI_DI_STR_REF), buf_i,
215 			&bytes_read, &index, len, offset);
216 	read_udi_subval(BT_DIS_UDI_ISSUER_STR_REF, DIS_STR_SIZE(BT_DIS_UDI_ISSUER_STR_REF), buf_i,
217 			&bytes_read, &index, len, offset);
218 	read_udi_subval(BT_DIS_UDI_AUTHORITY_STR_REF, DIS_STR_SIZE(BT_DIS_UDI_AUTHORITY_STR_REF),
219 			buf_i, &bytes_read, &index, len, offset);
220 
221 	return bytes_read;
222 }
223 #endif
224 
225 /* Device Information Service Declaration */
226 BT_GATT_SERVICE_DEFINE(
227 	dis_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DIS),
228 
229 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
230 			       read_str, NULL, BT_DIS_MODEL_REF),
231 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MANUFACTURER_NAME, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
232 			       read_str, NULL, BT_DIS_MANUF_REF),
233 #if CONFIG_BT_DIS_PNP
234 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_PNP_ID, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
235 			       read_pnp_id, NULL, &dis_pnp_id),
236 #endif
237 
238 #if defined(CONFIG_BT_DIS_SERIAL_NUMBER)
239 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_SERIAL_NUMBER, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
240 			       read_str, NULL, BT_DIS_SERIAL_NUMBER_STR_REF),
241 #endif
242 #if defined(CONFIG_BT_DIS_FW_REV)
243 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_FIRMWARE_REVISION, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
244 			       read_str, NULL, BT_DIS_FW_REV_STR_REF),
245 #endif
246 #if defined(CONFIG_BT_DIS_HW_REV)
247 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_HARDWARE_REVISION, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
248 			       read_str, NULL, BT_DIS_HW_REV_STR_REF),
249 #endif
250 #if defined(CONFIG_BT_DIS_SW_REV)
251 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_SOFTWARE_REVISION, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
252 			       read_str, NULL, BT_DIS_SW_REV_STR_REF),
253 #endif
254 #if defined(CONFIG_BT_DIS_UDI)
255 	BT_GATT_CHARACTERISTIC(BT_UUID_UDI_FOR_MEDICAL_DEVICES, BT_GATT_CHRC_READ,
256 			       BT_GATT_PERM_READ, read_udi, NULL, NULL),
257 #endif
258 #if defined(CONFIG_BT_DIS_SYSTEM_ID)
259 	BT_GATT_CHARACTERISTIC(BT_UUID_DIS_SYSTEM_ID, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
260 			       read_system_id, NULL, NULL),
261 #endif
262 #if defined(CONFIG_BT_DIS_IEEE_RCDL)
263 	BT_GATT_CHARACTERISTIC(BT_UUID_GATT_IEEE_RCDL, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
264 			       read_str, NULL, BT_DIS_IEEE_RCDL_STR_REF),
265 #endif
266 
267 );
268 
269 #if defined(CONFIG_BT_DIS_SETTINGS)
270 #if defined(CONFIG_BT_DIS_UDI)
dis_update_udi_value(const char * new,char * old,settings_read_cb read_cb,const char * logkey)271 static void dis_update_udi_value(const char *new, char *old, settings_read_cb read_cb,
272 				 const char *logkey)
273 {
274 	/*
275 	 * The characteristic contains 1 one-byte flag and 4-null-terminated.
276 	 * The null-terminators are only present for strings that are in the flags.
277 	 */
278 	const size_t merged_size = sizeof(uint8_t) + DIS_STR_SIZE(BT_DIS_UDI_LABEL_STR_REF) +
279 				   DIS_STR_SIZE(BT_DIS_UDI_DI_STR_REF) +
280 				   DIS_STR_SIZE(BT_DIS_UDI_ISSUER_STR_REF) +
281 				   DIS_STR_SIZE(BT_DIS_UDI_AUTHORITY_STR_REF);
282 
283 	size_t without_old = merged_size - DIS_STR_SIZE(old);
284 
285 	bool valid = BT_ATT_MAX_ATTRIBUTE_LEN >= without_old + DIS_STR_SIZE(new);
286 
287 	if (!valid) {
288 		LOG_ERR("Failed to set UDI %s. Not enough space. The sum of the 4 DIS UDI for "
289 			"Medical Devices strings may not exceed the maximum attribute length.",
290 			logkey);
291 		return;
292 	}
293 
294 	int16_t len = read_cb((void *)new, (void *)old, CONFIG_BT_DIS_STR_MAX);
295 
296 	if (len < 0) {
297 		LOG_ERR("Failed to read UDI %s from storage (err %zd)", logkey, len);
298 	} else {
299 		old[len] = '\0';
300 
301 		LOG_DBG("UDI %s set to %s", logkey, old);
302 	}
303 }
304 #endif
305 
dis_set(const char * name,size_t len_rd,settings_read_cb read_cb,void * store)306 static int dis_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *store)
307 {
308 	ssize_t len;
309 	int nlen;
310 	const char *next;
311 
312 	nlen = settings_name_next(name, &next);
313 	if (!strncmp(name, "manuf", nlen)) {
314 		len = read_cb(store, &dis_manuf, sizeof(dis_manuf) - 1);
315 		if (len < 0) {
316 			LOG_ERR("Failed to read manufacturer from storage (err %zd)", len);
317 		} else {
318 			dis_manuf[len] = '\0';
319 
320 			LOG_DBG("Manufacturer set to %s", dis_manuf);
321 		}
322 		return 0;
323 	}
324 	if (!strncmp(name, "model", nlen)) {
325 		len = read_cb(store, &dis_model, sizeof(dis_model) - 1);
326 		if (len < 0) {
327 			LOG_ERR("Failed to read model from storage (err %zd)", len);
328 		} else {
329 			dis_model[len] = '\0';
330 
331 			LOG_DBG("Model set to %s", dis_model);
332 		}
333 		return 0;
334 	}
335 #if defined(CONFIG_BT_DIS_SERIAL_NUMBER)
336 	if (!strncmp(name, "serial", nlen)) {
337 		len = read_cb(store, &dis_serial_number, sizeof(dis_serial_number) - 1);
338 		if (len < 0) {
339 			LOG_ERR("Failed to read serial number from storage (err %zd)", len);
340 		} else {
341 			dis_serial_number[len] = '\0';
342 
343 			LOG_DBG("Serial number set to %s", dis_serial_number);
344 		}
345 		return 0;
346 	}
347 #endif
348 #if defined(CONFIG_BT_DIS_FW_REV)
349 	if (!strncmp(name, "fw", nlen)) {
350 		len = read_cb(store, &dis_fw_rev, sizeof(dis_fw_rev) - 1);
351 		if (len < 0) {
352 			LOG_ERR("Failed to read firmware revision from storage (err %zd)", len);
353 		} else {
354 			dis_fw_rev[len] = '\0';
355 
356 			LOG_DBG("Firmware revision set to %s", dis_fw_rev);
357 		}
358 		return 0;
359 	}
360 #endif
361 #if defined(CONFIG_BT_DIS_HW_REV)
362 	if (!strncmp(name, "hw", nlen)) {
363 		len = read_cb(store, &dis_hw_rev, sizeof(dis_hw_rev) - 1);
364 		if (len < 0) {
365 			LOG_ERR("Failed to read hardware revision from storage (err %zd)", len);
366 		} else {
367 			dis_hw_rev[len] = '\0';
368 
369 			LOG_DBG("Hardware revision set to %s", dis_hw_rev);
370 		}
371 		return 0;
372 	}
373 #endif
374 #if defined(CONFIG_BT_DIS_SW_REV)
375 	if (!strncmp(name, "sw", nlen)) {
376 		len = read_cb(store, &dis_sw_rev, sizeof(dis_sw_rev) - 1);
377 		if (len < 0) {
378 			LOG_ERR("Failed to read software revision from storage (err %zd)", len);
379 		} else {
380 			dis_sw_rev[len] = '\0';
381 
382 			LOG_DBG("Software revision set to %s", dis_sw_rev);
383 		}
384 		return 0;
385 	}
386 #endif
387 #if defined(CONFIG_BT_DIS_UDI)
388 	if (!strncmp(name, "udi_label", nlen)) {
389 		dis_update_udi_value(store, BT_DIS_UDI_LABEL_STR_REF, read_cb, "label");
390 		return 0;
391 	}
392 
393 	if (!strncmp(name, "udi_di", nlen)) {
394 		dis_update_udi_value(store, BT_DIS_UDI_DI_STR_REF, read_cb, "device information");
395 		return 0;
396 	}
397 
398 	if (!strncmp(name, "udi_issuer", nlen)) {
399 		dis_update_udi_value(store, BT_DIS_UDI_ISSUER_STR_REF, read_cb, "issuer");
400 		return 0;
401 	}
402 
403 	if (!strncmp(name, "udi_authority", nlen)) {
404 		dis_update_udi_value(store, BT_DIS_UDI_AUTHORITY_STR_REF, read_cb, "authority");
405 		return 0;
406 	}
407 #endif
408 #if defined(CONFIG_BT_DIS_SYSTEM_ID)
409 	if (!strncmp(name, "sysid_oui", nlen)) {
410 		uint32_t oui = 0;
411 
412 		len = read_cb(store, &oui, sizeof(oui));
413 		if (len < 0) {
414 			LOG_ERR("Failed to read System ID OUI from storage (err %zd)", len);
415 		} else {
416 			sys_put_le24(oui, &dis_system_id[5]);
417 			LOG_DBG("System ID OUI set to %06X", oui);
418 		}
419 		return 0;
420 	}
421 	if (!strncmp(name, "sysid_identifier", nlen)) {
422 		uint64_t identifier = 0;
423 
424 		len = read_cb(store, &identifier, sizeof(identifier));
425 		if (len < 0) {
426 			LOG_ERR("Failed to read System ID identifier from storage (err %zd)", len);
427 		} else {
428 			sys_put_le40(identifier, &dis_system_id[0]);
429 			LOG_DBG("System ID identifier set to %10llX", identifier);
430 		}
431 		return 0;
432 	}
433 #endif
434 #if defined(CONFIG_BT_DIS_IEEE_RCDL)
435 	if (!strncmp(name, "ieeercdl", nlen)) {
436 		len = read_cb(store, &dis_ieee_rcdl, sizeof(dis_ieee_rcdl) - 1);
437 		if (len < 0) {
438 			LOG_ERR("Failed to read IEEE 11073-20601 Regulatory Certification Data "
439 				"List from storage (err %zd)",
440 				len);
441 		} else {
442 			dis_ieee_rcdl[len] = '\0';
443 
444 			LOG_DBG("IEEE 11073-20601 Regulatory Certification Data List set to %s",
445 				dis_ieee_rcdl);
446 		}
447 		return 0;
448 	}
449 #endif
450 	return 0;
451 }
452 
453 SETTINGS_STATIC_HANDLER_DEFINE(bt_dis, "bt/dis", NULL, dis_set, NULL, NULL);
454 
455 #endif /* CONFIG_BT_DIS_SETTINGS*/
456