1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/gatt.h>
8 #include <zephyr/kernel.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <zephyr/sys/printk.h>
12 
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/sys/util.h>
17 
18 #define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0)
19 
20 /* Overhead: opcode (u8) + handle (u16) */
21 #define ATT_NTF_SIZE(payload_len) (1 + 2 + payload_len)
22 
23 static const struct bt_uuid_128 mtu_test_service = BT_UUID_INIT_128(MTU_TEST_SERVICE_TYPE);
24 
25 static const struct bt_uuid_128 notify_characteristic_uuid =
26 	BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f3));
27 
28 static const struct bt_data adv_ad_data[] = {
29 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
30 	BT_DATA_BYTES(BT_DATA_UUID128_ALL, MTU_TEST_SERVICE_TYPE),
31 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
32 };
33 
34 static struct bt_conn *default_conn;
35 static struct k_sem conn_sem;
36 static struct k_work advertise_work;
37 
advertising_work_handler(struct k_work * work)38 void advertising_work_handler(struct k_work *work)
39 {
40 	bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, adv_ad_data, ARRAY_SIZE(adv_ad_data), NULL, 0);
41 }
42 
ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)43 static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
44 {
45 	ARG_UNUSED(attr);
46 
47 	bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
48 
49 	printk("MTU Test Update: notifications %s\n", notif_enabled ? "enabled" : "disabled");
50 }
51 
52 BT_GATT_SERVICE_DEFINE(mtu_test, BT_GATT_PRIMARY_SERVICE(&mtu_test_service),
53 		       BT_GATT_CHARACTERISTIC(&notify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
54 					      BT_GATT_PERM_NONE, NULL, NULL, NULL),
55 		       BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
56 
mtu_updated(struct bt_conn * conn,uint16_t tx,uint16_t rx)57 void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
58 {
59 	printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
60 }
61 
62 static struct bt_gatt_cb gatt_callbacks = {
63 	.att_mtu_updated = mtu_updated,
64 };
65 
connected(struct bt_conn * conn,uint8_t err)66 static void connected(struct bt_conn *conn, uint8_t err)
67 {
68 	if (err != 0) {
69 		return;
70 	}
71 
72 	default_conn = bt_conn_ref(conn);
73 	k_sem_give(&conn_sem);
74 }
75 
disconnected(struct bt_conn * conn,uint8_t reason)76 static void disconnected(struct bt_conn *conn, uint8_t reason)
77 {
78 	bt_conn_unref(conn);
79 	default_conn = NULL;
80 	k_work_submit(&advertise_work);
81 }
82 
83 BT_CONN_CB_DEFINE(conn_callbacks) = {
84 	.connected = connected,
85 	.disconnected = disconnected,
86 };
87 
run_peripheral_sample(uint8_t * notify_data,size_t notify_data_size,uint16_t seconds)88 void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds)
89 {
90 	int err;
91 
92 	err = bt_enable(NULL);
93 	if (err) {
94 		printk("Bluetooth init failed (err %d)\n", err);
95 		return;
96 	}
97 
98 	k_sem_init(&conn_sem, 0, 1);
99 	bt_gatt_cb_register(&gatt_callbacks);
100 
101 	k_work_init(&advertise_work, advertising_work_handler);
102 
103 	struct bt_gatt_attr *notify_crch =
104 		bt_gatt_find_by_uuid(mtu_test.attrs, 0xffff, &notify_characteristic_uuid.uuid);
105 
106 	bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, adv_ad_data, ARRAY_SIZE(adv_ad_data), NULL, 0);
107 
108 	bool infinite = seconds == 0;
109 
110 	for (int i = 0; (i < seconds) || infinite; i++) {
111 		if (default_conn == NULL) {
112 			k_sem_take(&conn_sem, K_FOREVER);
113 		}
114 
115 		k_sleep(K_SECONDS(1));
116 		if (default_conn == NULL) {
117 			printk("Skipping notification since connection is not yet established\n");
118 		/* Only send the notification if the UATT MTU supports the required length */
119 		} else if (bt_gatt_get_uatt_mtu(default_conn) >= ATT_NTF_SIZE(notify_data_size)) {
120 			bt_gatt_notify(default_conn, notify_crch, notify_data, notify_data_size);
121 		} else {
122 			printk("Skipping notification since UATT MTU is not sufficient."
123 			       "Required: %d, Actual: %d\n",
124 			       ATT_NTF_SIZE(notify_data_size),
125 			       bt_gatt_get_uatt_mtu(default_conn));
126 		}
127 	}
128 }
129