1 /** @file
2  * @brief Bluetooth GATT shell functions
3  *
4  */
5 
6 /*
7  * Copyright (c) 2017 Intel Corporation
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <errno.h>
13 #include <zephyr/types.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <zephyr/shell/shell_string_conv.h>
19 #include <zephyr/sys/byteorder.h>
20 #include <zephyr/sys/util.h>
21 #include <zephyr/kernel.h>
22 
23 #include <zephyr/bluetooth/bluetooth.h>
24 #include <zephyr/bluetooth/conn.h>
25 #include <zephyr/bluetooth/gatt.h>
26 
27 #include <zephyr/shell/shell.h>
28 
29 #include "bt.h"
30 
31 #if defined(CONFIG_BT_GATT_CLIENT) || defined(CONFIG_BT_GATT_DYNAMIC_DB)
32 extern uint8_t selected_id;
33 
34 static struct write_stats {
35 	uint32_t count;
36 	uint32_t len;
37 	uint32_t total;
38 	uint32_t rate;
39 } write_stats;
40 
update_write_stats(uint16_t len)41 static void update_write_stats(uint16_t len)
42 {
43 	static uint32_t cycle_stamp;
44 	uint32_t delta;
45 
46 	delta = k_cycle_get_32() - cycle_stamp;
47 	delta = (uint32_t)k_cyc_to_ns_floor64(delta);
48 
49 	if (!delta) {
50 		delta = 1;
51 	}
52 
53 	write_stats.count++;
54 	write_stats.total += len;
55 
56 	/* if last data rx-ed was greater than 1 second in the past,
57 	 * reset the metrics.
58 	 */
59 	if (delta > 1000000000) {
60 		write_stats.len = 0U;
61 		write_stats.rate = 0U;
62 		cycle_stamp = k_cycle_get_32();
63 	} else {
64 		write_stats.len += len;
65 		write_stats.rate = ((uint64_t)write_stats.len << 3) *
66 			1000000000U / delta;
67 	}
68 }
69 
70 #if defined(CONFIG_BT_EATT)
71 #define SET_CHAN_OPT_ANY(params) (params).chan_opt = BT_ATT_CHAN_OPT_NONE
72 #else
73 #define SET_CHAN_OPT_ANY(params)
74 #endif /* CONFIG_BT_EATT */
75 
print_write_stats(void)76 static void print_write_stats(void)
77 {
78 	shell_print(ctx_shell, "Write #%u: %u bytes (%u bps)",
79 		    write_stats.count, write_stats.total, write_stats.rate);
80 }
81 #endif /* CONFIG_BT_GATT_CLIENT || CONFIG_BT_GATT_DYNAMIC_DB */
82 
83 #if defined(CONFIG_BT_GATT_CLIENT)
reset_write_stats(void)84 static void reset_write_stats(void)
85 {
86 	memset(&write_stats, 0, sizeof(write_stats));
87 }
88 
89 /* This variable is write-locked when `(exchange_params.func != NULL)`.
90  * Must be zero-initialized when unlocked.
91  */
92 static struct bt_gatt_exchange_params exchange_params;
93 
exchange_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)94 static void exchange_func(struct bt_conn *conn, uint8_t err,
95 			  struct bt_gatt_exchange_params *params)
96 {
97 	shell_print(ctx_shell, "Exchange %s", err == 0U ? "successful" :
98 		    "failed");
99 
100 	/* Release global `exchange_params`. */
101 	__ASSERT_NO_MSG(params == &exchange_params);
102 	(void)memset(params, 0, sizeof(*params));
103 }
104 
cmd_exchange_mtu(const struct shell * sh,size_t argc,char * argv[])105 static int cmd_exchange_mtu(const struct shell *sh,
106 			     size_t argc, char *argv[])
107 {
108 	int err;
109 
110 	if (!default_conn) {
111 		shell_print(sh, "Not connected");
112 		return -ENOEXEC;
113 	}
114 
115 	if (exchange_params.func) {
116 		shell_print(sh, "Shell command busy. A previous invocation is in progress.");
117 		return -EBUSY;
118 	}
119 
120 	exchange_params.func = exchange_func;
121 
122 	err = bt_gatt_exchange_mtu(default_conn, &exchange_params);
123 	if (err) {
124 		/* Release global `exchange_params`. */
125 		exchange_params.func = NULL;
126 	}
127 
128 	if (err == -EALREADY) {
129 		shell_print(sh, "Already exchanged");
130 	} else if (err) {
131 		shell_print(sh, "Exchange failed (err %d)", err);
132 	} else {
133 		shell_print(sh, "Exchange pending");
134 	}
135 
136 	return err;
137 }
138 
139 static struct bt_gatt_discover_params discover_params;
140 static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
141 
print_chrc_props(const struct shell * sh,uint8_t properties)142 static void print_chrc_props(const struct shell *sh, uint8_t properties)
143 {
144 	shell_print(sh, "Properties: ");
145 
146 	if (properties & BT_GATT_CHRC_BROADCAST) {
147 		shell_print(sh, "[bcast]");
148 	}
149 
150 	if (properties & BT_GATT_CHRC_READ) {
151 		shell_print(sh, "[read]");
152 	}
153 
154 	if (properties & BT_GATT_CHRC_WRITE) {
155 		shell_print(sh, "[write]");
156 	}
157 
158 	if (properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) {
159 		shell_print(sh, "[write w/w rsp]");
160 	}
161 
162 	if (properties & BT_GATT_CHRC_NOTIFY) {
163 		shell_print(sh, "[notify]");
164 	}
165 
166 	if (properties & BT_GATT_CHRC_INDICATE) {
167 		shell_print(sh, "[indicate]");
168 	}
169 
170 	if (properties & BT_GATT_CHRC_AUTH) {
171 		shell_print(sh, "[auth]");
172 	}
173 
174 	if (properties & BT_GATT_CHRC_EXT_PROP) {
175 		shell_print(sh, "[ext prop]");
176 	}
177 
178 	shell_print(sh, "");
179 }
180 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)181 static uint8_t discover_func(struct bt_conn *conn,
182 			     const struct bt_gatt_attr *attr,
183 			     struct bt_gatt_discover_params *params)
184 {
185 	struct bt_gatt_service_val *gatt_service;
186 	struct bt_gatt_chrc *gatt_chrc;
187 	struct bt_gatt_include *gatt_include;
188 	char str[BT_UUID_STR_LEN];
189 
190 	if (!attr) {
191 		shell_print(ctx_shell, "Discover complete");
192 		(void)memset(params, 0, sizeof(*params));
193 		return BT_GATT_ITER_STOP;
194 	}
195 
196 	switch (params->type) {
197 	case BT_GATT_DISCOVER_SECONDARY:
198 	case BT_GATT_DISCOVER_PRIMARY:
199 		gatt_service = attr->user_data;
200 		bt_uuid_to_str(gatt_service->uuid, str, sizeof(str));
201 		shell_print(ctx_shell, "Service %s found: start handle %x, "
202 			    "end_handle %x", str, attr->handle,
203 			    gatt_service->end_handle);
204 		break;
205 	case BT_GATT_DISCOVER_CHARACTERISTIC:
206 		gatt_chrc = attr->user_data;
207 		bt_uuid_to_str(gatt_chrc->uuid, str, sizeof(str));
208 		shell_print(ctx_shell, "Characteristic %s found: handle %x",
209 			    str, attr->handle);
210 		print_chrc_props(ctx_shell, gatt_chrc->properties);
211 		break;
212 	case BT_GATT_DISCOVER_INCLUDE:
213 		gatt_include = attr->user_data;
214 		bt_uuid_to_str(gatt_include->uuid, str, sizeof(str));
215 		shell_print(ctx_shell, "Include %s found: handle %x, start %x, "
216 			    "end %x", str, attr->handle,
217 			    gatt_include->start_handle,
218 			    gatt_include->end_handle);
219 		break;
220 	default:
221 		bt_uuid_to_str(attr->uuid, str, sizeof(str));
222 		shell_print(ctx_shell, "Descriptor %s found: handle %x", str,
223 			    attr->handle);
224 		break;
225 	}
226 
227 	return BT_GATT_ITER_CONTINUE;
228 }
229 
cmd_discover(const struct shell * sh,size_t argc,char * argv[])230 static int cmd_discover(const struct shell *sh, size_t argc, char *argv[])
231 {
232 	int err;
233 
234 	if (!default_conn) {
235 		shell_error(sh, "Not connected");
236 		return -ENOEXEC;
237 	}
238 
239 	if (discover_params.func) {
240 		shell_print(sh, "Discover ongoing");
241 		return -ENOEXEC;
242 	}
243 
244 	discover_params.func = discover_func;
245 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
246 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
247 	SET_CHAN_OPT_ANY(discover_params);
248 
249 	if (argc > 1) {
250 		/* Only set the UUID if the value is valid (non zero) */
251 		uuid.val = strtoul(argv[1], NULL, 16);
252 		if (uuid.val) {
253 			discover_params.uuid = &uuid.uuid;
254 		}
255 	}
256 
257 	if (argc > 2) {
258 		discover_params.start_handle = strtoul(argv[2], NULL, 16);
259 		if (argc > 3) {
260 			discover_params.end_handle = strtoul(argv[3], NULL, 16);
261 		}
262 	}
263 
264 	if (!strcmp(argv[0], "discover")) {
265 		discover_params.type = BT_GATT_DISCOVER_ATTRIBUTE;
266 	} else if (!strcmp(argv[0], "discover-secondary")) {
267 		discover_params.type = BT_GATT_DISCOVER_SECONDARY;
268 	} else if (!strcmp(argv[0], "discover-include")) {
269 		discover_params.type = BT_GATT_DISCOVER_INCLUDE;
270 	} else if (!strcmp(argv[0], "discover-characteristic")) {
271 		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
272 	} else if (!strcmp(argv[0], "discover-descriptor")) {
273 		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
274 	} else {
275 		discover_params.type = BT_GATT_DISCOVER_PRIMARY;
276 	}
277 
278 	err = bt_gatt_discover(default_conn, &discover_params);
279 	if (err) {
280 		shell_error(sh, "Discover failed (err %d)", err);
281 	} else {
282 		shell_print(sh, "Discover pending");
283 	}
284 
285 	return err;
286 }
287 
288 static struct bt_gatt_read_params read_params;
289 
read_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)290 static uint8_t read_func(struct bt_conn *conn, uint8_t err,
291 			 struct bt_gatt_read_params *params,
292 			 const void *data, uint16_t length)
293 {
294 	shell_print(ctx_shell, "Read complete: err 0x%02x length %u", err, length);
295 
296 	if (!data) {
297 		(void)memset(params, 0, sizeof(*params));
298 		return BT_GATT_ITER_STOP;
299 	} else {
300 		shell_hexdump(ctx_shell, data, length);
301 	}
302 
303 	return BT_GATT_ITER_CONTINUE;
304 }
305 
cmd_read(const struct shell * sh,size_t argc,char * argv[])306 static int cmd_read(const struct shell *sh, size_t argc, char *argv[])
307 {
308 	int err;
309 
310 	if (!default_conn) {
311 		shell_error(sh, "Not connected");
312 		return -ENOEXEC;
313 	}
314 
315 	if (read_params.func) {
316 		shell_print(sh, "Read ongoing");
317 		return -ENOEXEC;
318 	}
319 
320 	read_params.func = read_func;
321 	read_params.handle_count = 1;
322 	read_params.single.handle = strtoul(argv[1], NULL, 16);
323 	read_params.single.offset = 0U;
324 	SET_CHAN_OPT_ANY(read_params);
325 
326 	if (argc > 2) {
327 		read_params.single.offset = strtoul(argv[2], NULL, 16);
328 	}
329 
330 	err = bt_gatt_read(default_conn, &read_params);
331 	if (err) {
332 		shell_error(sh, "Read failed (err %d)", err);
333 	} else {
334 		shell_print(sh, "Read pending");
335 	}
336 
337 	return err;
338 }
339 
cmd_mread(const struct shell * sh,size_t argc,char * argv[])340 static int cmd_mread(const struct shell *sh, size_t argc, char *argv[])
341 {
342 	uint16_t h[8];
343 	size_t i;
344 	int err;
345 
346 	if (!default_conn) {
347 		shell_error(sh, "Not connected");
348 		return -ENOEXEC;
349 	}
350 
351 	if (read_params.func) {
352 		shell_print(sh, "Read ongoing");
353 		return -ENOEXEC;
354 	}
355 
356 	if ((argc - 1) >  ARRAY_SIZE(h)) {
357 		shell_print(sh, "Enter max %zu handle items to read",
358 			    ARRAY_SIZE(h));
359 		return -EINVAL;
360 	}
361 
362 	for (i = 0; i < argc - 1; i++) {
363 		h[i] = strtoul(argv[i + 1], NULL, 16);
364 	}
365 
366 	read_params.func = read_func;
367 	read_params.handle_count = i;
368 	read_params.multiple.handles = h;
369 	read_params.multiple.variable = true;
370 	SET_CHAN_OPT_ANY(read_params);
371 
372 	err = bt_gatt_read(default_conn, &read_params);
373 	if (err) {
374 		shell_error(sh, "GATT multiple read request failed (err %d)",
375 			    err);
376 	}
377 
378 	return err;
379 }
380 
cmd_read_uuid(const struct shell * sh,size_t argc,char * argv[])381 static int cmd_read_uuid(const struct shell *sh, size_t argc, char *argv[])
382 {
383 	int err;
384 
385 	if (!default_conn) {
386 		shell_error(sh, "Not connected");
387 		return -ENOEXEC;
388 	}
389 
390 	if (read_params.func) {
391 		shell_print(sh, "Read ongoing");
392 		return -ENOEXEC;
393 	}
394 
395 	read_params.func = read_func;
396 	read_params.handle_count = 0;
397 	read_params.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
398 	read_params.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
399 	SET_CHAN_OPT_ANY(read_params);
400 
401 	if (argc > 1) {
402 		uuid.val = strtoul(argv[1], NULL, 16);
403 		if (uuid.val) {
404 			read_params.by_uuid.uuid = &uuid.uuid;
405 		}
406 	}
407 
408 	if (argc > 2) {
409 		read_params.by_uuid.start_handle = strtoul(argv[2], NULL, 16);
410 		if (argc > 3) {
411 			read_params.by_uuid.end_handle = strtoul(argv[3],
412 								 NULL, 16);
413 		}
414 	}
415 
416 	err = bt_gatt_read(default_conn, &read_params);
417 	if (err) {
418 		shell_error(sh, "Read failed (err %d)", err);
419 	} else {
420 		shell_print(sh, "Read pending");
421 	}
422 
423 	return err;
424 }
425 
426 static struct bt_gatt_write_params write_params;
427 static uint8_t gatt_write_buf[BT_ATT_MAX_ATTRIBUTE_LEN];
428 
write_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)429 static void write_func(struct bt_conn *conn, uint8_t err,
430 		       struct bt_gatt_write_params *params)
431 {
432 	shell_print(ctx_shell, "Write complete: err 0x%02x", err);
433 
434 	(void)memset(&write_params, 0, sizeof(write_params));
435 }
436 
cmd_write(const struct shell * sh,size_t argc,char * argv[])437 static int cmd_write(const struct shell *sh, size_t argc, char *argv[])
438 {
439 	int err;
440 	uint16_t handle, offset;
441 
442 	if (!default_conn) {
443 		shell_error(sh, "Not connected");
444 		return -ENOEXEC;
445 	}
446 
447 	if (write_params.func) {
448 		shell_error(sh, "Write ongoing");
449 		return -ENOEXEC;
450 	}
451 
452 	handle = strtoul(argv[1], NULL, 16);
453 	offset = strtoul(argv[2], NULL, 16);
454 
455 	write_params.length = hex2bin(argv[3], strlen(argv[3]),
456 				      gatt_write_buf, sizeof(gatt_write_buf));
457 	if (write_params.length == 0) {
458 		shell_error(sh, "No data set");
459 		return -ENOEXEC;
460 	}
461 
462 	write_params.data = gatt_write_buf;
463 	write_params.handle = handle;
464 	write_params.offset = offset;
465 	write_params.func = write_func;
466 	SET_CHAN_OPT_ANY(write_params);
467 
468 	err = bt_gatt_write(default_conn, &write_params);
469 	if (err) {
470 		write_params.func = NULL;
471 		shell_error(sh, "Write failed (err %d)", err);
472 	} else {
473 		shell_print(sh, "Write pending");
474 	}
475 
476 	return err;
477 }
478 
write_without_rsp_cb(struct bt_conn * conn,void * user_data)479 static void write_without_rsp_cb(struct bt_conn *conn, void *user_data)
480 {
481 	uint16_t len = POINTER_TO_UINT(user_data);
482 
483 	update_write_stats(len);
484 
485 	print_write_stats();
486 }
487 
cmd_write_without_rsp(const struct shell * sh,size_t argc,char * argv[])488 static int cmd_write_without_rsp(const struct shell *sh,
489 				 size_t argc, char *argv[])
490 {
491 	uint16_t handle;
492 	uint16_t repeat;
493 	int err;
494 	uint16_t len;
495 	bool sign;
496 	bt_gatt_complete_func_t func = NULL;
497 
498 	if (!default_conn) {
499 		shell_error(sh, "Not connected");
500 		return -ENOEXEC;
501 	}
502 
503 	sign = !strcmp(argv[0], "signed-write");
504 	if (!sign) {
505 		if (!strcmp(argv[0], "write-without-response-cb")) {
506 			func = write_without_rsp_cb;
507 			reset_write_stats();
508 		}
509 	}
510 
511 	handle = strtoul(argv[1], NULL, 16);
512 	gatt_write_buf[0] = strtoul(argv[2], NULL, 16);
513 	len = 1U;
514 
515 	if (argc > 3) {
516 		int i;
517 
518 		len = MIN(strtoul(argv[3], NULL, 16), sizeof(gatt_write_buf));
519 
520 		for (i = 1; i < len; i++) {
521 			gatt_write_buf[i] = gatt_write_buf[0];
522 		}
523 	}
524 
525 	repeat = 0U;
526 	err = 0;
527 
528 	if (argc > 4) {
529 		repeat = strtoul(argv[4], NULL, 16);
530 	}
531 
532 	if (!repeat) {
533 		repeat = 1U;
534 	}
535 
536 	while (repeat--) {
537 		err = bt_gatt_write_without_response_cb(default_conn, handle,
538 							gatt_write_buf, len,
539 							sign, func,
540 							UINT_TO_POINTER(len));
541 		if (err) {
542 			break;
543 		}
544 
545 		k_yield();
546 
547 	}
548 
549 	shell_print(sh, "Write Complete (err %d)", err);
550 	return err;
551 }
552 
553 static struct bt_gatt_subscribe_params subscribe_params;
554 
notify_func(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)555 static uint8_t notify_func(struct bt_conn *conn,
556 			struct bt_gatt_subscribe_params *params,
557 			const void *data, uint16_t length)
558 {
559 	if (!data) {
560 		shell_print(ctx_shell, "Unsubscribed");
561 		params->value_handle = 0U;
562 		return BT_GATT_ITER_STOP;
563 	}
564 
565 	shell_print(ctx_shell, "Notification: value_handle %u, length %u",
566 		    params->value_handle, length);
567 	shell_hexdump(ctx_shell, data, length);
568 
569 	return BT_GATT_ITER_CONTINUE;
570 }
571 
cmd_subscribe(const struct shell * sh,size_t argc,char * argv[])572 static int cmd_subscribe(const struct shell *sh, size_t argc, char *argv[])
573 {
574 	int err;
575 
576 	if (subscribe_params.value_handle) {
577 		shell_error(sh, "Cannot subscribe: subscription to %x "
578 			    "already exists", subscribe_params.value_handle);
579 		return -ENOEXEC;
580 	}
581 
582 	if (!default_conn) {
583 		shell_error(sh, "Not connected");
584 		return -ENOEXEC;
585 	}
586 
587 	subscribe_params.ccc_handle = strtoul(argv[1], NULL, 16);
588 	subscribe_params.value_handle = strtoul(argv[2], NULL, 16);
589 	subscribe_params.value = BT_GATT_CCC_NOTIFY;
590 	subscribe_params.notify = notify_func;
591 	SET_CHAN_OPT_ANY(subscribe_params);
592 
593 #if defined(CONFIG_BT_GATT_AUTO_DISCOVER_CCC)
594 	if (subscribe_params.ccc_handle == 0) {
595 		static struct bt_gatt_discover_params disc_params;
596 
597 		subscribe_params.disc_params = &disc_params;
598 		subscribe_params.end_handle = 0xFFFF;
599 	}
600 #endif /* CONFIG_BT_GATT_AUTO_DISCOVER_CCC */
601 
602 
603 	if (argc > 3 && !strcmp(argv[3], "ind")) {
604 		subscribe_params.value = BT_GATT_CCC_INDICATE;
605 	}
606 
607 	err = bt_gatt_subscribe(default_conn, &subscribe_params);
608 	if (err) {
609 		subscribe_params.value_handle = 0U;
610 		shell_error(sh, "Subscribe failed (err %d)", err);
611 	} else {
612 		shell_print(sh, "Subscribed");
613 	}
614 
615 	return err;
616 }
617 
cmd_resubscribe(const struct shell * sh,size_t argc,char * argv[])618 static int cmd_resubscribe(const struct shell *sh, size_t argc,
619 				char *argv[])
620 {
621 	bt_addr_le_t addr;
622 	int err;
623 
624 	if (subscribe_params.value_handle) {
625 		shell_error(sh, "Cannot resubscribe: subscription to %x"
626 			    " already exists", subscribe_params.value_handle);
627 		return -ENOEXEC;
628 	}
629 
630 	err = bt_addr_le_from_str(argv[1], argv[2], &addr);
631 	if (err) {
632 		shell_error(sh, "Invalid peer address (err %d)", err);
633 		return -ENOEXEC;
634 	}
635 
636 	subscribe_params.ccc_handle = strtoul(argv[3], NULL, 16);
637 	subscribe_params.value_handle = strtoul(argv[4], NULL, 16);
638 	subscribe_params.value = BT_GATT_CCC_NOTIFY;
639 	subscribe_params.notify = notify_func;
640 	SET_CHAN_OPT_ANY(subscribe_params);
641 
642 	if (argc > 5 && !strcmp(argv[5], "ind")) {
643 		subscribe_params.value = BT_GATT_CCC_INDICATE;
644 	}
645 
646 	err = bt_gatt_resubscribe(selected_id, &addr, &subscribe_params);
647 	if (err) {
648 		subscribe_params.value_handle = 0U;
649 		shell_error(sh, "Resubscribe failed (err %d)", err);
650 	} else {
651 		shell_print(sh, "Resubscribed");
652 	}
653 
654 	return err;
655 }
656 
cmd_unsubscribe(const struct shell * sh,size_t argc,char * argv[])657 static int cmd_unsubscribe(const struct shell *sh,
658 			   size_t argc, char *argv[])
659 {
660 	int err;
661 
662 	if (!default_conn) {
663 		shell_error(sh, "Not connected");
664 		return -ENOEXEC;
665 	}
666 
667 	if (!subscribe_params.value_handle) {
668 		shell_error(sh, "No subscription found");
669 		return -ENOEXEC;
670 	}
671 
672 	err = bt_gatt_unsubscribe(default_conn, &subscribe_params);
673 	if (err) {
674 		shell_error(sh, "Unsubscribe failed (err %d)", err);
675 	} else {
676 		shell_print(sh, "Unsubscribe success");
677 	}
678 
679 	return err;
680 }
681 #endif /* CONFIG_BT_GATT_CLIENT */
682 
683 static struct db_stats {
684 	uint16_t svc_count;
685 	uint16_t attr_count;
686 	uint16_t chrc_count;
687 	uint16_t ccc_count;
688 } stats;
689 
print_attr(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)690 static uint8_t print_attr(const struct bt_gatt_attr *attr, uint16_t handle,
691 			  void *user_data)
692 {
693 	const struct shell *sh = user_data;
694 	char str[BT_UUID_STR_LEN];
695 
696 	stats.attr_count++;
697 
698 	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) ||
699 	    !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) {
700 		stats.svc_count++;
701 	}
702 
703 	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) {
704 		stats.chrc_count++;
705 	}
706 
707 	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC) &&
708 	    attr->write == bt_gatt_attr_write_ccc) {
709 		stats.ccc_count++;
710 	}
711 
712 	bt_uuid_to_str(attr->uuid, str, sizeof(str));
713 	shell_print(sh, "attr %p handle 0x%04x uuid %s perm 0x%02x",
714 		    attr, handle, str, attr->perm);
715 
716 	return BT_GATT_ITER_CONTINUE;
717 }
718 
cmd_show_db(const struct shell * sh,size_t argc,char * argv[])719 static int cmd_show_db(const struct shell *sh, size_t argc, char *argv[])
720 {
721 	struct bt_uuid_16 uuid16;
722 	size_t total_len;
723 
724 	memset(&stats, 0, sizeof(stats));
725 
726 	if (argc > 1) {
727 		uint16_t num_matches = 0;
728 
729 		uuid16.uuid.type = BT_UUID_TYPE_16;
730 		uuid16.val = strtoul(argv[1], NULL, 16);
731 
732 		if (argc > 2) {
733 			num_matches = strtoul(argv[2], NULL, 10);
734 		}
735 
736 		bt_gatt_foreach_attr_type(0x0001, 0xffff, &uuid16.uuid, NULL,
737 					  num_matches, print_attr,
738 					  (void *)sh);
739 		return 0;
740 	}
741 
742 	bt_gatt_foreach_attr(0x0001, 0xffff, print_attr, (void *)sh);
743 
744 	if (!stats.attr_count) {
745 		shell_print(sh, "No attribute found");
746 		return 0;
747 	}
748 
749 	total_len = stats.svc_count * sizeof(struct bt_gatt_service);
750 	total_len += stats.chrc_count * sizeof(struct bt_gatt_chrc);
751 	total_len += stats.attr_count * sizeof(struct bt_gatt_attr);
752 	total_len += stats.ccc_count * sizeof(struct _bt_gatt_ccc);
753 
754 	shell_print(sh, "=================================================");
755 	shell_print(sh, "Total: %u services %u attributes (%zu bytes)",
756 		    stats.svc_count, stats.attr_count, total_len);
757 
758 	return 0;
759 }
760 
761 #if defined(CONFIG_BT_GATT_DYNAMIC_DB)
762 /* Custom Service Variables */
763 
764 static const struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
765 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));
766 
767 static const struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
768 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));
769 
770 static const struct bt_uuid_128 vnd_long_uuid1 = BT_UUID_INIT_128(
771 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef3));
772 
773 static const struct bt_uuid_128 vnd_long_uuid2 = BT_UUID_INIT_128(
774 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x5678cefaadde));
775 
776 static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' };
777 
778 static const struct bt_uuid_128 vnd1_uuid = BT_UUID_INIT_128(
779 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x56789abcdef4));
780 
781 static const struct bt_uuid_128 vnd1_echo_uuid = BT_UUID_INIT_128(
782 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x56789abcdef5));
783 
784 static uint8_t echo_enabled;
785 
vnd1_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)786 static void vnd1_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
787 {
788 	echo_enabled = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
789 }
790 
write_vnd1(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)791 static ssize_t write_vnd1(struct bt_conn *conn, const struct bt_gatt_attr *attr,
792 			  const void *buf, uint16_t len, uint16_t offset,
793 			  uint8_t flags)
794 {
795 	if (echo_enabled) {
796 		shell_print(ctx_shell, "Echo attr len %u", len);
797 		bt_gatt_notify(conn, attr, buf, len);
798 	}
799 
800 	return len;
801 }
802 
read_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)803 static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
804 			void *buf, uint16_t len, uint16_t offset)
805 {
806 	const char *value = attr->user_data;
807 
808 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
809 				 strlen(value));
810 }
811 
write_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)812 static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
813 			 const void *buf, uint16_t len, uint16_t offset,
814 			 uint8_t flags)
815 {
816 	uint8_t *value = attr->user_data;
817 
818 	if (offset + len > sizeof(vnd_value)) {
819 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
820 	}
821 
822 	memcpy(value + offset, buf, len);
823 
824 	return len;
825 }
826 
827 #define MAX_DATA 30
828 static uint8_t vnd_long_value1[MAX_DATA] = { 'V', 'e', 'n', 'd', 'o', 'r' };
829 static uint8_t vnd_long_value2[MAX_DATA] = { 'S', 't', 'r', 'i', 'n', 'g' };
830 
read_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)831 static ssize_t read_long_vnd(struct bt_conn *conn,
832 			     const struct bt_gatt_attr *attr, void *buf,
833 			     uint16_t len, uint16_t offset)
834 {
835 	uint8_t *value = attr->user_data;
836 
837 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
838 				 sizeof(vnd_long_value1));
839 }
840 
write_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)841 static ssize_t write_long_vnd(struct bt_conn *conn,
842 			      const struct bt_gatt_attr *attr, const void *buf,
843 			      uint16_t len, uint16_t offset, uint8_t flags)
844 {
845 	uint8_t *value = attr->user_data;
846 
847 	if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
848 		return 0;
849 	}
850 
851 	if (offset + len > sizeof(vnd_long_value1)) {
852 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
853 	}
854 
855 	/* Copy to buffer */
856 	memcpy(value + offset, buf, len);
857 
858 	return len;
859 }
860 
861 static struct bt_gatt_attr vnd_attrs[] = {
862 	/* Vendor Primary Service Declaration */
863 	BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
864 
865 	BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
866 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
867 			       BT_GATT_PERM_READ_AUTHEN |
868 			       BT_GATT_PERM_WRITE_AUTHEN,
869 			       read_vnd, write_vnd, vnd_value),
870 
871 	BT_GATT_CHARACTERISTIC(&vnd_long_uuid1.uuid, BT_GATT_CHRC_READ |
872 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
873 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
874 			       BT_GATT_PERM_PREPARE_WRITE,
875 			       read_long_vnd, write_long_vnd,
876 			       &vnd_long_value1),
877 
878 	BT_GATT_CHARACTERISTIC(&vnd_long_uuid2.uuid, BT_GATT_CHRC_READ |
879 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
880 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
881 			       BT_GATT_PERM_PREPARE_WRITE,
882 			       read_long_vnd, write_long_vnd,
883 			       &vnd_long_value2),
884 };
885 
886 static struct bt_gatt_service vnd_svc = BT_GATT_SERVICE(vnd_attrs);
887 
888 static struct bt_gatt_attr vnd1_attrs[] = {
889 	/* Vendor Primary Service Declaration */
890 	BT_GATT_PRIMARY_SERVICE(&vnd1_uuid),
891 
892 	BT_GATT_CHARACTERISTIC(&vnd1_echo_uuid.uuid,
893 			       BT_GATT_CHRC_WRITE_WITHOUT_RESP |
894 			       BT_GATT_CHRC_NOTIFY,
895 			       BT_GATT_PERM_WRITE, NULL, write_vnd1, NULL),
896 	BT_GATT_CCC(vnd1_ccc_cfg_changed,
897 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
898 };
899 
900 static struct bt_gatt_service vnd1_svc = BT_GATT_SERVICE(vnd1_attrs);
901 
cmd_register_test_svc(const struct shell * sh,size_t argc,char * argv[])902 static int cmd_register_test_svc(const struct shell *sh,
903 				  size_t argc, char *argv[])
904 {
905 	char str[BT_UUID_STR_LEN];
906 	int err;
907 
908 	bt_uuid_to_str(&vnd_uuid.uuid, str, sizeof(str));
909 	err = bt_gatt_service_register(&vnd_svc);
910 	if (!err) {
911 		shell_print(sh, "Registered test vendor service %s", str);
912 	} else {
913 		shell_error(sh, "Failed to register test vendor service %s (%d)", str, err);
914 	}
915 
916 	bt_uuid_to_str(&vnd1_uuid.uuid, str, sizeof(str));
917 	err = bt_gatt_service_register(&vnd1_svc);
918 	if (!err) {
919 		shell_print(sh, "Registered test vendor service %s", str);
920 	} else {
921 		shell_error(sh, "Failed to register test vendor service %s (%d)", str, err);
922 	}
923 
924 	return 0;
925 }
926 
cmd_unregister_test_svc(const struct shell * sh,size_t argc,char * argv[])927 static int cmd_unregister_test_svc(const struct shell *sh,
928 				    size_t argc, char *argv[])
929 {
930 	char str[BT_UUID_STR_LEN];
931 	int err;
932 
933 	bt_uuid_to_str(&vnd_uuid.uuid, str, sizeof(str));
934 	err = bt_gatt_service_unregister(&vnd_svc);
935 	if (!err) {
936 		shell_print(sh, "Unregistered test vendor service %s", str);
937 	} else {
938 		shell_error(sh, "Failed to unregister test vendor service %s (%d)", str, err);
939 	}
940 
941 	bt_uuid_to_str(&vnd1_uuid.uuid, str, sizeof(str));
942 	err = bt_gatt_service_unregister(&vnd1_svc);
943 	if (!err) {
944 		shell_print(sh, "Unregistered test vendor service %s", str);
945 	} else {
946 		shell_error(sh, "Failed to unregister test vendor service %s (%d)", str, err);
947 	}
948 
949 	return 0;
950 }
951 
found_attr(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)952 static uint8_t found_attr(const struct bt_gatt_attr *attr, uint16_t handle,
953 			  void *user_data)
954 {
955 	const struct bt_gatt_attr **found = user_data;
956 
957 	*found = attr;
958 
959 	return BT_GATT_ITER_STOP;
960 }
961 
find_attr(uint16_t handle)962 static const struct bt_gatt_attr *find_attr(uint16_t handle)
963 {
964 	const struct bt_gatt_attr *attr = NULL;
965 
966 	bt_gatt_foreach_attr(handle, handle, found_attr, &attr);
967 
968 	return attr;
969 }
970 
cmd_notify(const struct shell * sh,size_t argc,char * argv[])971 static int cmd_notify(const struct shell *sh, size_t argc, char *argv[])
972 {
973 	const struct bt_gatt_attr *attr;
974 	int err;
975 	size_t data_len;
976 	unsigned long handle;
977 	static char data[BT_ATT_MAX_ATTRIBUTE_LEN];
978 
979 	const char *arg_handle = argv[1];
980 	const char *arg_data = argv[2];
981 	size_t arg_data_len = strlen(arg_data);
982 
983 	err = 0;
984 	handle = shell_strtoul(arg_handle, 16, &err);
985 	if (err) {
986 		shell_error(sh, "Handle '%s': Not a valid hex number.", arg_handle);
987 		return -EINVAL;
988 	}
989 
990 	if (!IN_RANGE(handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE)) {
991 		shell_error(sh, "Handle 0x%lx: Impossible value.", handle);
992 		return -EINVAL;
993 	}
994 
995 	if ((arg_data_len / 2) > BT_ATT_MAX_ATTRIBUTE_LEN) {
996 		shell_error(sh, "Data: Size exceeds legal attribute size.");
997 		return -EINVAL;
998 	}
999 
1000 	data_len = hex2bin(arg_data, arg_data_len, data, sizeof(data));
1001 	if (data_len == 0 && arg_data_len != 0) {
1002 		shell_error(sh, "Data: Bad hex.");
1003 		return -EINVAL;
1004 	}
1005 
1006 	attr = find_attr(handle);
1007 	if (!attr) {
1008 		shell_error(sh, "Handle 0x%lx: Local attribute not found.", handle);
1009 		return -EINVAL;
1010 	}
1011 
1012 	err = bt_gatt_notify(NULL, attr, data, data_len);
1013 	if (err) {
1014 		shell_error(sh, "bt_gatt_notify errno %d (%s)", -err, strerror(-err));
1015 	}
1016 
1017 	return err;
1018 }
1019 
1020 #if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
notify_cb(struct bt_conn * conn,void * user_data)1021 static void notify_cb(struct bt_conn *conn, void *user_data)
1022 {
1023 	const struct shell *sh = user_data;
1024 
1025 	shell_print(sh, "Nofication sent to conn %p", conn);
1026 }
cmd_notify_mult(const struct shell * sh,size_t argc,char * argv[])1027 static int cmd_notify_mult(const struct shell *sh, size_t argc, char *argv[])
1028 {
1029 	const size_t max_cnt = CONFIG_BT_L2CAP_TX_BUF_COUNT;
1030 	struct bt_gatt_notify_params params[max_cnt];
1031 	const size_t min_cnt = 1U;
1032 	unsigned long data;
1033 	unsigned long cnt;
1034 	uint16_t cnt_u16;
1035 	int err = 0;
1036 
1037 	if (!default_conn) {
1038 		shell_error(sh, "Not connected.");
1039 
1040 		return -ENOEXEC;
1041 	}
1042 
1043 	if (!echo_enabled) {
1044 		shell_error(sh, "No clients have enabled notifications for the vnd1_echo CCC.");
1045 
1046 		return -ENOEXEC;
1047 	}
1048 
1049 	cnt = shell_strtoul(argv[1], 10, &err);
1050 	if (err != 0) {
1051 		shell_error(sh, "Invalid count parameter: %s", argv[1]);
1052 
1053 		return -err;
1054 	}
1055 
1056 	if (!IN_RANGE(cnt, min_cnt, max_cnt)) {
1057 		shell_error(sh, "Invalid count value %lu (range %zu to %zu)",
1058 			    cnt, min_cnt, max_cnt);
1059 
1060 		return -ENOEXEC;
1061 	}
1062 
1063 	cnt_u16 = (uint16_t)cnt;
1064 
1065 	if (argc > 2) {
1066 		data = shell_strtoul(argv[2], 16, &err);
1067 		if (err != 0) {
1068 			shell_error(sh, "Invalid data parameter: %s", argv[1]);
1069 
1070 			return -err;
1071 		}
1072 	}
1073 
1074 	(void)memset(params, 0, sizeof(params));
1075 
1076 	for (uint16_t i = 0U; i < cnt_u16; i++) {
1077 		params[i].uuid = 0;
1078 		params[i].attr = vnd1_attrs;
1079 		params[i].data = &data;
1080 		params[i].len = sizeof(data);
1081 		params[i].func = notify_cb;
1082 		params[i].user_data = (void *)sh;
1083 	}
1084 
1085 	err = bt_gatt_notify_multiple(default_conn, cnt_u16, params);
1086 	if (err != 0) {
1087 		shell_error(sh, "bt_gatt_notify_multiple failed: %d", err);
1088 	} else {
1089 		shell_print(sh, "Send %u notifications", cnt_u16);
1090 	}
1091 
1092 	return err;
1093 }
1094 #endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
1095 
1096 static const struct bt_uuid_128 met_svc_uuid = BT_UUID_INIT_128(
1097 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcde01));
1098 static const struct bt_uuid_128 met_char_uuid = BT_UUID_INIT_128(
1099 	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcde02));
1100 
1101 static uint8_t met_char_value[BT_ATT_MAX_ATTRIBUTE_LEN] = {
1102 	'M', 'e', 't', 'r', 'i', 'c', 's' };
1103 
read_met(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)1104 static ssize_t read_met(struct bt_conn *conn, const struct bt_gatt_attr *attr,
1105 			void *buf, uint16_t len, uint16_t offset)
1106 {
1107 	const char *value = attr->user_data;
1108 	uint16_t value_len;
1109 
1110 	value_len = MIN(strlen(value), BT_ATT_MAX_ATTRIBUTE_LEN);
1111 
1112 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
1113 				 value_len);
1114 }
1115 
write_met(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)1116 static ssize_t write_met(struct bt_conn *conn, const struct bt_gatt_attr *attr,
1117 			 const void *buf, uint16_t len, uint16_t offset,
1118 			 uint8_t flags)
1119 {
1120 	uint8_t *value = attr->user_data;
1121 
1122 	if (offset + len > sizeof(met_char_value)) {
1123 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
1124 	}
1125 
1126 	memcpy(value + offset, buf, len);
1127 
1128 	update_write_stats(len);
1129 
1130 	return len;
1131 }
1132 
1133 static struct bt_gatt_attr met_attrs[] = {
1134 	BT_GATT_PRIMARY_SERVICE(&met_svc_uuid),
1135 
1136 	BT_GATT_CHARACTERISTIC(&met_char_uuid.uuid,
1137 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
1138 			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
1139 			       read_met, write_met, met_char_value),
1140 };
1141 
1142 static struct bt_gatt_service met_svc = BT_GATT_SERVICE(met_attrs);
1143 
cmd_metrics(const struct shell * sh,size_t argc,char * argv[])1144 static int cmd_metrics(const struct shell *sh, size_t argc, char *argv[])
1145 {
1146 	int err = 0;
1147 
1148 	if (argc < 2) {
1149 		print_write_stats();
1150 		return 0;
1151 	}
1152 
1153 	if (!strcmp(argv[1], "on")) {
1154 		shell_print(sh, "Registering GATT metrics test Service.");
1155 		err = bt_gatt_service_register(&met_svc);
1156 	} else if (!strcmp(argv[1], "off")) {
1157 		shell_print(sh, "Unregistering GATT metrics test Service.");
1158 		err = bt_gatt_service_unregister(&met_svc);
1159 	} else {
1160 		shell_error(sh, "Incorrect value: %s", argv[1]);
1161 		shell_help(sh);
1162 		return -ENOEXEC;
1163 	}
1164 
1165 	if (!err) {
1166 		shell_print(sh, "GATT write cmd metrics %s.", argv[1]);
1167 	}
1168 
1169 	return err;
1170 }
1171 #endif /* CONFIG_BT_GATT_DYNAMIC_DB */
1172 
get_cb(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)1173 static uint8_t get_cb(const struct bt_gatt_attr *attr, uint16_t handle,
1174 		      void *user_data)
1175 {
1176 	struct shell *sh = user_data;
1177 	uint8_t buf[256];
1178 	ssize_t ret;
1179 	char str[BT_UUID_STR_LEN];
1180 
1181 	bt_uuid_to_str(attr->uuid, str, sizeof(str));
1182 	shell_print(sh, "attr %p uuid %s perm 0x%02x", attr, str,
1183 		    attr->perm);
1184 
1185 	if (!attr->read) {
1186 		return BT_GATT_ITER_CONTINUE;
1187 	}
1188 
1189 	ret = attr->read(NULL, attr, (void *)buf, sizeof(buf), 0);
1190 	if (ret < 0) {
1191 		shell_print(sh, "Failed to read: %zd", ret);
1192 		return BT_GATT_ITER_STOP;
1193 	}
1194 
1195 	shell_hexdump(sh, buf, ret);
1196 
1197 	return BT_GATT_ITER_CONTINUE;
1198 }
1199 
cmd_get(const struct shell * sh,size_t argc,char * argv[])1200 static int cmd_get(const struct shell *sh, size_t argc, char *argv[])
1201 {
1202 	uint16_t start, end;
1203 
1204 	start = strtoul(argv[1], NULL, 16);
1205 	end = start;
1206 
1207 	if (argc > 2) {
1208 		end = strtoul(argv[2], NULL, 16);
1209 	}
1210 
1211 	bt_gatt_foreach_attr(start, end, get_cb, (void *)sh);
1212 
1213 	return 0;
1214 }
1215 
1216 struct set_data {
1217 	const struct shell *sh;
1218 	size_t argc;
1219 	char **argv;
1220 	int err;
1221 };
1222 
set_cb(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)1223 static uint8_t set_cb(const struct bt_gatt_attr *attr, uint16_t handle,
1224 		      void *user_data)
1225 {
1226 	struct set_data *data = user_data;
1227 	uint8_t buf[256];
1228 	size_t i;
1229 	ssize_t ret;
1230 
1231 	if (!attr->write) {
1232 		shell_error(data->sh, "Write not supported");
1233 		data->err = -ENOENT;
1234 		return BT_GATT_ITER_CONTINUE;
1235 	}
1236 
1237 	for (i = 0; i < data->argc; i++) {
1238 		buf[i] = strtoul(data->argv[i], NULL, 16);
1239 	}
1240 
1241 	ret = attr->write(NULL, attr, (void *)buf, i, 0, 0);
1242 	if (ret < 0) {
1243 		data->err = ret;
1244 		shell_error(data->sh, "Failed to write: %zd", ret);
1245 		return BT_GATT_ITER_STOP;
1246 	}
1247 
1248 	return BT_GATT_ITER_CONTINUE;
1249 }
1250 
cmd_set(const struct shell * sh,size_t argc,char * argv[])1251 static int cmd_set(const struct shell *sh, size_t argc, char *argv[])
1252 {
1253 	uint16_t handle;
1254 	struct set_data data;
1255 
1256 	handle = strtoul(argv[1], NULL, 16);
1257 
1258 	data.sh = sh;
1259 	data.argc = argc - 2;
1260 	data.argv = argv + 2;
1261 	data.err = 0;
1262 
1263 	bt_gatt_foreach_attr(handle, handle, set_cb, &data);
1264 
1265 	if (data.err < 0) {
1266 		return -ENOEXEC;
1267 	}
1268 
1269 	bt_gatt_foreach_attr(handle, handle, get_cb, (void *)sh);
1270 
1271 	return 0;
1272 }
1273 
cmd_att_mtu(const struct shell * sh,size_t argc,char * argv[])1274 int cmd_att_mtu(const struct shell *sh, size_t argc, char *argv[])
1275 {
1276 	uint16_t mtu;
1277 
1278 	if (default_conn) {
1279 		mtu = bt_gatt_get_mtu(default_conn);
1280 		shell_print(sh, "MTU size: %u", mtu);
1281 	} else {
1282 		shell_print(sh, "No default connection");
1283 	}
1284 
1285 	return 0;
1286 }
1287 
1288 #define HELP_NONE "[none]"
1289 #define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"
1290 
1291 SHELL_STATIC_SUBCMD_SET_CREATE(gatt_cmds,
1292 #if defined(CONFIG_BT_GATT_CLIENT)
1293 	SHELL_CMD_ARG(discover, NULL,
1294 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1295 	SHELL_CMD_ARG(discover-characteristic, NULL,
1296 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1297 	SHELL_CMD_ARG(discover-descriptor, NULL,
1298 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1299 	SHELL_CMD_ARG(discover-include, NULL,
1300 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1301 	SHELL_CMD_ARG(discover-primary, NULL,
1302 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1303 	SHELL_CMD_ARG(discover-secondary, NULL,
1304 		      "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1305 	SHELL_CMD_ARG(exchange-mtu, NULL, HELP_NONE, cmd_exchange_mtu, 1, 0),
1306 	SHELL_CMD_ARG(read, NULL, "<handle> [offset]", cmd_read, 2, 1),
1307 	SHELL_CMD_ARG(read-uuid, NULL, "<UUID> [start handle] [end handle]",
1308 		      cmd_read_uuid, 2, 2),
1309 	SHELL_CMD_ARG(read-multiple, NULL, "<handle 1> <handle 2> ...",
1310 		      cmd_mread, 2, -1),
1311 	SHELL_CMD_ARG(signed-write, NULL, "<handle> <data> [length] [repeat]",
1312 		      cmd_write_without_rsp, 3, 2),
1313 	SHELL_CMD_ARG(subscribe, NULL, "<CCC handle> <value handle> [ind]",
1314 		      cmd_subscribe, 3, 1),
1315 	SHELL_CMD_ARG(resubscribe, NULL, HELP_ADDR_LE" <CCC handle> "
1316 		      "<value handle> [ind]", cmd_resubscribe, 5, 1),
1317 	SHELL_CMD_ARG(write, NULL, "<handle> <offset> <data>", cmd_write, 4, 0),
1318 	SHELL_CMD_ARG(write-without-response, NULL,
1319 		      "<handle> <data> [length] [repeat]",
1320 		      cmd_write_without_rsp, 3, 2),
1321 	SHELL_CMD_ARG(write-without-response-cb, NULL,
1322 		      "<handle> <data> [length] [repeat]",
1323 		      cmd_write_without_rsp, 3, 2),
1324 	SHELL_CMD_ARG(unsubscribe, NULL, HELP_NONE, cmd_unsubscribe, 1, 0),
1325 #endif /* CONFIG_BT_GATT_CLIENT */
1326 	SHELL_CMD_ARG(get, NULL, "<start handle> [end handle]", cmd_get, 2, 1),
1327 	SHELL_CMD_ARG(set, NULL, "<handle> [data...]", cmd_set, 2, 255),
1328 	SHELL_CMD_ARG(show-db, NULL, "[uuid] [num_matches]", cmd_show_db, 1, 2),
1329 	SHELL_CMD_ARG(att_mtu, NULL, "Output ATT MTU size", cmd_att_mtu, 1, 0),
1330 #if defined(CONFIG_BT_GATT_DYNAMIC_DB)
1331 	SHELL_CMD_ARG(metrics, NULL, "[value: on, off]", cmd_metrics, 1, 1),
1332 	SHELL_CMD_ARG(register, NULL,
1333 		      "register pre-predefined test service",
1334 		      cmd_register_test_svc, 1, 0),
1335 	SHELL_CMD_ARG(unregister, NULL,
1336 		      "unregister pre-predefined test service",
1337 		      cmd_unregister_test_svc, 1, 0),
1338 	SHELL_CMD_ARG(notify, NULL, "<handle> <data>", cmd_notify, 3, 0),
1339 #if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
1340 	SHELL_CMD_ARG(notify-mult, NULL, "count [data]", cmd_notify_mult, 2, 1),
1341 #endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
1342 #endif /* CONFIG_BT_GATT_DYNAMIC_DB */
1343 	SHELL_SUBCMD_SET_END
1344 );
1345 
cmd_gatt(const struct shell * sh,size_t argc,char ** argv)1346 static int cmd_gatt(const struct shell *sh, size_t argc, char **argv)
1347 {
1348 	if (argc == 1) {
1349 		shell_help(sh);
1350 		/* shell returns 1 when help is printed */
1351 		return 1;
1352 	}
1353 
1354 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
1355 
1356 	return -EINVAL;
1357 }
1358 
1359 SHELL_CMD_ARG_REGISTER(gatt, &gatt_cmds, "Bluetooth GATT shell commands",
1360 		       cmd_gatt, 1, 1);
1361