1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <math.h>
8 #include <zephyr/console/console.h>
9 #include <zephyr/sys/byteorder.h>
10 #include <zephyr/bluetooth/bluetooth.h>
11 #include <zephyr/bluetooth/cs.h>
12 #include <zephyr/bluetooth/att.h>
13 #include <zephyr/bluetooth/gatt.h>
14 #include "distance_estimation.h"
15 #include "common.h"
16 #include "cs_test_params.h"
17 
18 static K_SEM_DEFINE(sem_results_available, 0, 1);
19 static K_SEM_DEFINE(sem_test_complete, 0, 1);
20 static K_SEM_DEFINE(sem_connected, 0, 1);
21 static K_SEM_DEFINE(sem_disconnected, 0, 1);
22 static K_SEM_DEFINE(sem_discovered, 0, 1);
23 static K_SEM_DEFINE(sem_written, 0, 1);
24 
25 static uint16_t step_data_attr_handle;
26 static struct bt_conn *connection;
27 static uint8_t latest_num_steps_reported;
28 static uint8_t latest_local_steps[STEP_DATA_BUF_LEN];
29 
30 static const char sample_str[] = "CS Test Sample";
31 static const struct bt_data ad[] = {
32 	BT_DATA(BT_DATA_NAME_COMPLETE, "CS Test Sample", sizeof(sample_str) - 1),
33 };
34 
subevent_result_cb(struct bt_conn_le_cs_subevent_result * result)35 static void subevent_result_cb(struct bt_conn_le_cs_subevent_result *result)
36 {
37 	latest_num_steps_reported = result->header.num_steps_reported;
38 
39 	if (result->step_data_buf) {
40 		if (result->step_data_buf->len <= STEP_DATA_BUF_LEN) {
41 			memcpy(latest_local_steps, result->step_data_buf->data,
42 			       result->step_data_buf->len);
43 		} else {
44 			printk("Not enough memory to store step data. (%d > %d)\n",
45 			       result->step_data_buf->len, STEP_DATA_BUF_LEN);
46 			latest_num_steps_reported = 0;
47 		}
48 	}
49 
50 	if (result->header.procedure_done_status == BT_CONN_LE_CS_PROCEDURE_COMPLETE ||
51 	    result->header.procedure_done_status == BT_CONN_LE_CS_PROCEDURE_ABORTED) {
52 		k_sem_give(&sem_results_available);
53 	}
54 }
55 
end_cb(void)56 static void end_cb(void)
57 {
58 	k_sem_give(&sem_test_complete);
59 }
60 
mtu_exchange_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)61 static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err,
62 			    struct bt_gatt_exchange_params *params)
63 {
64 	printk("MTU exchange %s (%u)\n", err == 0U ? "success" : "failed", bt_gatt_get_mtu(conn));
65 }
66 
connected_cb(struct bt_conn * conn,uint8_t err)67 static void connected_cb(struct bt_conn *conn, uint8_t err)
68 {
69 	char addr[BT_ADDR_LE_STR_LEN];
70 
71 	(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
72 	printk("Connected to %s (err 0x%02X)\n", addr, err);
73 
74 	__ASSERT(connection == conn, "Unexpected connected callback");
75 
76 	if (err) {
77 		bt_conn_unref(conn);
78 		connection = NULL;
79 	}
80 
81 	connection = bt_conn_ref(conn);
82 
83 	static struct bt_gatt_exchange_params mtu_exchange_params = {.func = mtu_exchange_cb};
84 
85 	err = bt_gatt_exchange_mtu(connection, &mtu_exchange_params);
86 	if (err) {
87 		printk("%s: MTU exchange failed (err %d)\n", __func__, err);
88 	}
89 
90 	k_sem_give(&sem_connected);
91 }
92 
disconnected_cb(struct bt_conn * conn,uint8_t reason)93 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
94 {
95 	printk("Disconnected (reason 0x%02X)\n", reason);
96 
97 	bt_conn_unref(conn);
98 	connection = NULL;
99 
100 	k_sem_give(&sem_disconnected);
101 }
102 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)103 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
104 			     struct bt_gatt_discover_params *params)
105 {
106 	struct bt_gatt_chrc *chrc;
107 	char str[BT_UUID_STR_LEN];
108 
109 	printk("Discovery: attr %p\n", attr);
110 
111 	if (!attr) {
112 		return BT_GATT_ITER_STOP;
113 	}
114 
115 	chrc = (struct bt_gatt_chrc *)attr->user_data;
116 
117 	bt_uuid_to_str(chrc->uuid, str, sizeof(str));
118 	printk("UUID %s\n", str);
119 
120 	if (!bt_uuid_cmp(chrc->uuid, &step_data_char_uuid.uuid)) {
121 		step_data_attr_handle = chrc->value_handle;
122 
123 		printk("Found expected UUID\n");
124 
125 		k_sem_give(&sem_discovered);
126 	}
127 
128 	return BT_GATT_ITER_STOP;
129 }
130 
write_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)131 static void write_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
132 {
133 	if (err) {
134 		printk("Write failed (err %d)\n", err);
135 
136 		return;
137 	}
138 
139 	k_sem_give(&sem_written);
140 }
141 
142 BT_CONN_CB_DEFINE(conn_cb) = {
143 	.connected = connected_cb,
144 	.disconnected = disconnected_cb,
145 };
146 
main(void)147 int main(void)
148 {
149 	int err;
150 	struct bt_le_cs_test_param test_params;
151 	struct bt_gatt_discover_params discover_params;
152 	struct bt_gatt_write_params write_params;
153 
154 	printk("Starting Channel Sounding Demo\n");
155 
156 	/* Initialize the Bluetooth Subsystem */
157 	err = bt_enable(NULL);
158 	if (err) {
159 		printk("Bluetooth init failed (err %d)\n", err);
160 		return 0;
161 	}
162 
163 	struct bt_le_cs_test_cb cs_test_cb = {
164 		.le_cs_test_subevent_data_available = subevent_result_cb,
165 		.le_cs_test_end_complete = end_cb,
166 	};
167 
168 	err = bt_le_cs_test_cb_register(cs_test_cb);
169 	if (err) {
170 		printk("Failed to register callbacks (err %d)\n", err);
171 		return 0;
172 	}
173 
174 	while (true) {
175 		while (true) {
176 			k_sleep(K_SECONDS(2));
177 
178 			test_params = test_params_get(BT_CONN_LE_CS_ROLE_REFLECTOR);
179 
180 			err = bt_le_cs_start_test(&test_params);
181 			if (err) {
182 				printk("Failed to start CS test (err %d)\n", err);
183 				return 0;
184 			}
185 
186 			k_sem_take(&sem_results_available, K_SECONDS(5));
187 
188 			err = bt_le_cs_stop_test();
189 			if (err) {
190 				printk("Failed to stop CS test (err %d)\n", err);
191 				return 0;
192 			}
193 
194 			k_sem_take(&sem_test_complete, K_FOREVER);
195 
196 			if (latest_num_steps_reported > NUM_MODE_0_STEPS) {
197 				break;
198 			}
199 		}
200 
201 		err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONN, BT_GAP_ADV_FAST_INT_MIN_1,
202 						      BT_GAP_ADV_FAST_INT_MAX_1, NULL),
203 				      ad, ARRAY_SIZE(ad), NULL, 0);
204 		if (err) {
205 			printk("Advertising failed to start (err %d)\n", err);
206 			return 0;
207 		}
208 
209 		k_sem_take(&sem_connected, K_FOREVER);
210 
211 		discover_params.uuid = &step_data_char_uuid.uuid;
212 		discover_params.func = discover_func;
213 		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
214 		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
215 		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
216 
217 		err = bt_gatt_discover(connection, &discover_params);
218 		if (err) {
219 			printk("Discovery failed (err %d)\n", err);
220 			return 0;
221 		}
222 
223 		err = k_sem_take(&sem_discovered, K_SECONDS(10));
224 		if (err) {
225 			printk("Timed out during GATT discovery\n");
226 			return 0;
227 		}
228 
229 		write_params.func = write_func;
230 		write_params.handle = step_data_attr_handle;
231 		write_params.length = STEP_DATA_BUF_LEN;
232 		write_params.data = latest_local_steps;
233 		write_params.offset = 0;
234 
235 		err = bt_gatt_write(connection, &write_params);
236 		if (err) {
237 			printk("Write failed (err %d)\n", err);
238 			return 0;
239 		}
240 
241 		k_sem_take(&sem_disconnected, K_FOREVER);
242 
243 		printk("Re-running CS test...\n");
244 	}
245 
246 	return 0;
247 }
248