1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/printk.h>
9
10 #include <zephyr/bluetooth/bluetooth.h>
11 #include <zephyr/bluetooth/conn.h>
12 #include <zephyr/bluetooth/gatt.h>
13 #include <zephyr/bluetooth/hci.h>
14
15 /* Count down number of write commands after all PHY and connection updates */
16 #define COUNT_THROUGHPUT 1000U
17
18 /* Count down number of metrics intervals before performing a PHY update */
19 #define PHY_UPDATE_COUNTDOWN 5U
20 static uint32_t phy_update_countdown;
21
22 /* Current index of the parameters array to initiate PHY Update */
23 static uint8_t phy_param_idx;
24
25 /* Count down number of metrics intervals before performing a param update */
26 #define PARAM_UPDATE_COUNTDOWN PHY_UPDATE_COUNTDOWN
27 static uint32_t param_update_countdown;
28
29 /* Current index of the parameters array to initiate Connection Update */
30 static uint8_t param_update_idx;
31
32 /* If testing PHY Update then perform one iteration of Connection Updates otherwise when testing
33 * Connection Updates perform 20 iterations.
34 */
35 #define PARAM_UPDATE_ITERATION_MAX COND_CODE_1(CONFIG_USE_PHY_UPDATE_ITERATION_ONCE, (1U), (20U))
36 static uint32_t param_update_iteration;
37
38 /* Total number of Connection Updates performed
39 *
40 * Used for logging purposes only
41 */
42 static uint32_t param_update_count;
43
44 /* Calculate the Supervision Timeout to a Rounded up 10 ms unit
45 *
46 * Conform to required BT Specifiction defined minimum Supervision Timeout of 100 ms
47 */
48 #define CONN_TIMEOUT(_timeout) \
49 BT_GAP_US_TO_CONN_TIMEOUT(DIV_ROUND_UP(MAX(100U * USEC_PER_MSEC, (_timeout)), \
50 10U * USEC_PER_MSEC) * 10U * USEC_PER_MSEC)
51
52 /* Relaxed number of Connection Interval to set the Supervision Timeout.
53 * Shall be >= 2U.
54 *
55 * Refer to BT Spec v6.1, Vol 6, Part B, Section 4.5.2 Supervision timeout
56 *
57 * `(1 + connPeripheralLatency) × connSubrateFactor × connInterval × 2`
58 */
59 #define CONN_INTERVAL_MULTIPLIER (6U)
60
phy_update_iterate(struct bt_conn * conn)61 static void phy_update_iterate(struct bt_conn *conn)
62 {
63 const struct bt_conn_le_phy_param phy_param[] = {
64 /* List of 1M Tx with Rx on other PHYs */
65 {
66 .options = BT_CONN_LE_PHY_OPT_NONE,
67 .pref_tx_phy = BT_GAP_LE_PHY_1M,
68 .pref_rx_phy = BT_GAP_LE_PHY_1M,
69 }, {
70 .options = BT_CONN_LE_PHY_OPT_NONE,
71 .pref_tx_phy = BT_GAP_LE_PHY_1M,
72 .pref_rx_phy = BT_GAP_LE_PHY_2M,
73 }, {
74 .options = BT_CONN_LE_PHY_OPT_NONE,
75 .pref_tx_phy = BT_GAP_LE_PHY_1M,
76 .pref_rx_phy = BT_GAP_LE_PHY_CODED,
77 },
78
79 /* List of 2M Tx with Rx on other PHYs */
80 {
81 .options = BT_CONN_LE_PHY_OPT_NONE,
82 .pref_tx_phy = BT_GAP_LE_PHY_2M,
83 .pref_rx_phy = BT_GAP_LE_PHY_1M,
84 }, {
85 .options = BT_CONN_LE_PHY_OPT_NONE,
86 .pref_tx_phy = BT_GAP_LE_PHY_2M,
87 .pref_rx_phy = BT_GAP_LE_PHY_2M,
88 }, {
89 .options = BT_CONN_LE_PHY_OPT_NONE,
90 .pref_tx_phy = BT_GAP_LE_PHY_2M,
91 .pref_rx_phy = BT_GAP_LE_PHY_CODED,
92 },
93
94 /* List of Coded PHY S8 Tx with Rx on other PHYs */
95 {
96 .options = BT_CONN_LE_PHY_OPT_CODED_S8,
97 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
98 .pref_rx_phy = BT_GAP_LE_PHY_1M,
99 }, {
100 .options = BT_CONN_LE_PHY_OPT_CODED_S8,
101 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
102 .pref_rx_phy = BT_GAP_LE_PHY_2M,
103 }, {
104 .options = BT_CONN_LE_PHY_OPT_CODED_S8,
105 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
106 .pref_rx_phy = BT_GAP_LE_PHY_CODED,
107 },
108
109 /* List of Coded PHY S2 Tx with Rx on other PHYs */
110 {
111 .options = BT_CONN_LE_PHY_OPT_CODED_S2,
112 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
113 .pref_rx_phy = BT_GAP_LE_PHY_1M,
114 }, {
115 .options = BT_CONN_LE_PHY_OPT_CODED_S2,
116 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
117 .pref_rx_phy = BT_GAP_LE_PHY_2M,
118 }, {
119 .options = BT_CONN_LE_PHY_OPT_CODED_S2,
120 .pref_tx_phy = BT_GAP_LE_PHY_CODED,
121 .pref_rx_phy = BT_GAP_LE_PHY_CODED,
122 },
123
124 /* Finally stop at 2M Tx with Rx on 2M */
125 {
126 .options = BT_CONN_LE_PHY_OPT_NONE,
127 .pref_tx_phy = BT_GAP_LE_PHY_2M,
128 .pref_rx_phy = BT_GAP_LE_PHY_2M,
129 },
130 };
131 int err;
132
133 if (phy_update_countdown--) {
134 return;
135 }
136
137 phy_update_countdown = PHY_UPDATE_COUNTDOWN;
138
139 if (phy_param_idx >= ARRAY_SIZE(phy_param)) {
140 if (IS_ENABLED(CONFIG_USE_PHY_UPDATE_ITERATION_ONCE)) {
141 /* No more PHY updates, stay at the last index */
142 return;
143 }
144
145 /* Test PHY Update not enabled, lets continue with connection update iterations
146 * forever.
147 */
148 phy_param_idx = 0U;
149 }
150
151 struct bt_conn_info conn_info;
152
153 err = bt_conn_get_info(conn, &conn_info);
154 if (err) {
155 printk("Failed to get connection info (%d).\n", err);
156 return;
157 }
158
159 struct bt_conn_le_phy_param conn_phy_param;
160
161 if (conn_info.role == BT_CONN_ROLE_CENTRAL) {
162 conn_phy_param.options = phy_param[phy_param_idx].options;
163 conn_phy_param.pref_tx_phy = phy_param[phy_param_idx].pref_tx_phy;
164 conn_phy_param.pref_rx_phy = phy_param[phy_param_idx].pref_rx_phy;
165 } else {
166 conn_phy_param.options = phy_param[phy_param_idx].options;
167 conn_phy_param.pref_tx_phy = phy_param[phy_param_idx].pref_rx_phy;
168 conn_phy_param.pref_rx_phy = phy_param[phy_param_idx].pref_tx_phy;
169 }
170
171 printk("%s: PHY Update requested %u %u (%u)\n", __func__,
172 conn_phy_param.pref_tx_phy,
173 conn_phy_param.pref_rx_phy,
174 conn_phy_param.options);
175
176 err = bt_conn_le_phy_update(conn, &conn_phy_param);
177 if (err) {
178 printk("Failed to update PHY (%d).\n", err);
179 return;
180 }
181
182 phy_param_idx++;
183 }
184
185 /* Interval between storing the measured write rate */
186 #define METRICS_INTERVAL 1U /* seconds */
187
188 static struct bt_gatt_exchange_params mtu_exchange_params;
189 static uint32_t write_count;
190 static uint32_t write_len;
191 static uint32_t write_rate;
192
193 /* Globals, reused by central_gatt_write and peripheral_gatt_write samples */
194 /* Connection context used by the Write Cmd calls */
195 struct bt_conn *conn_connected;
196 /* Stores the latest calculated write rate, bits per second */
197 uint32_t last_write_rate;
198 /* Number of Write Commands used to record the latest write rate.
199 * Has to be large enough to be transmitting packets for METRICS_INTERVAL duration.
200 * Assign 0 to continue calculating latest write rate, forever or until disconnection.
201 */
202 uint32_t *write_countdown;
203 /* Function pointer used to restart scanning on ACL disconnect */
204 void (*start_scan_func)(void);
205
write_cmd_cb(struct bt_conn * conn,void * user_data)206 static void write_cmd_cb(struct bt_conn *conn, void *user_data)
207 {
208 static uint32_t cycle_stamp;
209 uint64_t delta;
210
211 delta = k_cycle_get_32() - cycle_stamp;
212 delta = k_cyc_to_ns_floor64(delta);
213
214 if (delta == 0) {
215 /* Skip division by zero */
216 return;
217 }
218
219 /* if last data rx-ed was greater than 1 second in the past,
220 * reset the metrics.
221 */
222 if (delta > (METRICS_INTERVAL * NSEC_PER_SEC)) {
223 printk("%s: count= %u, len= %u, rate= %u bps.\n", __func__,
224 write_count, write_len, write_rate);
225
226 last_write_rate = write_rate;
227
228 write_count = 0U;
229 write_len = 0U;
230 write_rate = 0U;
231 cycle_stamp = k_cycle_get_32();
232
233 if (IS_ENABLED(CONFIG_BT_USER_PHY_UPDATE)) {
234 phy_update_iterate(conn);
235 }
236
237 /* NOTE: Though minimum connection timeout permitted is 100 ms, to avoid supervision
238 * timeout when observer role is enabled in the sample, keep the timeout for
239 * smaller connection interval be large enough due to repeated overlaps by the
240 * scan window.
241 */
242 const struct bt_le_conn_param update_params[] = {{
243 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(51250U),
244 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(51250U),
245 .latency = 0,
246 .timeout = CONN_TIMEOUT(51250U * CONN_INTERVAL_MULTIPLIER),
247 }, {
248 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(50000U),
249 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(50000U),
250 .latency = 0,
251 .timeout = CONN_TIMEOUT(50000U * CONN_INTERVAL_MULTIPLIER),
252 }, {
253 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(8750U),
254 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(8750U),
255 .latency = 0,
256 .timeout = CONN_TIMEOUT(720000U),
257 }, {
258 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(7500U),
259 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(7500U),
260 .latency = 0,
261 .timeout = CONN_TIMEOUT(720000U),
262 }, {
263 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(50000U),
264 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(50000U),
265 .latency = 0,
266 .timeout = CONN_TIMEOUT(50000U * CONN_INTERVAL_MULTIPLIER),
267 }, {
268 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(51250U),
269 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(51250U),
270 .latency = 0,
271 .timeout = CONN_TIMEOUT(51250U * CONN_INTERVAL_MULTIPLIER),
272 }, {
273 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(7500U),
274 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(7500U),
275 .latency = 0,
276 .timeout = CONN_TIMEOUT(720000U),
277 }, {
278 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(8750U),
279 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(8750U),
280 .latency = 0,
281 .timeout = CONN_TIMEOUT(720000U),
282 }, {
283 .interval_min = BT_GAP_US_TO_CONN_INTERVAL(50000U),
284 .interval_max = BT_GAP_US_TO_CONN_INTERVAL(50000U),
285 .latency = 0,
286 .timeout = CONN_TIMEOUT(50000U * CONN_INTERVAL_MULTIPLIER),
287 },
288 };
289 int err;
290
291 if ((param_update_countdown--) != 0U) {
292 return;
293 }
294
295 param_update_countdown = PARAM_UPDATE_COUNTDOWN;
296
297 if (param_update_idx >= ARRAY_SIZE(update_params)) {
298 if (IS_ENABLED(CONFIG_USE_CONN_UPDATE_ITERATION_ONCE) &&
299 (--param_update_iteration == 0U)) {
300 /* No more conn updates, stay at the last index */
301 param_update_iteration = 1U;
302
303 /* As this function is re-used by the peripheral; on target, users
304 * can enable simultaneous (background) scanning but by default do
305 * not have the scanning enabled.
306 * If both Central plus Peripheral role is built together then
307 * Peripheral is scanning (on 1M and Coded PHY windows) while there
308 * is simultaneous Write Commands.
309 *
310 * We stop scanning if we are stopping after one iteration of
311 * Connection Updates.
312 */
313 if (IS_ENABLED(CONFIG_BT_OBSERVER) &&
314 !IS_ENABLED(CONFIG_USE_PHY_UPDATE_ITERATION_ONCE)) {
315 /* Stop scanning. We will keep calling on every complete
316 * countdown. This is ok, for implementation simplicity,
317 * i.e. not adding addition design.
318 */
319 err = bt_le_scan_stop();
320 if (err != 0) {
321 printk("Failed to stop scanning (%d).\n", err);
322 }
323
324 printk("Scanning stopped.\n");
325
326 *write_countdown = COUNT_THROUGHPUT;
327 }
328
329 return;
330 }
331
332 /* Test Connection Update not enabled, lets continue with connection update
333 * iterations forever.
334 */
335 param_update_idx = 0U;
336 }
337
338 param_update_count++;
339
340 printk("Parameter Update Count: %u. %u: 0x%x 0x%x %u %u\n", param_update_count,
341 param_update_idx,
342 update_params[param_update_idx].interval_min,
343 update_params[param_update_idx].interval_max,
344 update_params[param_update_idx].latency,
345 update_params[param_update_idx].timeout);
346 err = bt_conn_le_param_update(conn, &update_params[param_update_idx]);
347 if (err != 0) {
348 printk("Parameter update failed (err %d)\n", err);
349 }
350
351 param_update_idx++;
352
353 } else {
354 uint16_t len;
355
356 write_count++;
357
358 /* Extract the 16-bit data length stored in user_data */
359 len = (uint32_t)user_data & 0xFFFF;
360
361 write_len += len;
362 write_rate = ((uint64_t)write_len << 3) * (METRICS_INTERVAL * NSEC_PER_SEC) /
363 delta;
364 }
365 }
366
mtu_exchange_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)367 static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err,
368 struct bt_gatt_exchange_params *params)
369 {
370 printk("%s: MTU exchange %s (%u)\n", __func__,
371 err == 0U ? "successful" : "failed",
372 bt_gatt_get_mtu(conn));
373 }
374
mtu_exchange(struct bt_conn * conn)375 static int mtu_exchange(struct bt_conn *conn)
376 {
377 int err;
378
379 printk("%s: Current MTU = %u\n", __func__, bt_gatt_get_mtu(conn));
380
381 mtu_exchange_params.func = mtu_exchange_cb;
382
383 printk("%s: Exchange MTU...\n", __func__);
384 err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params);
385 if (err) {
386 printk("%s: MTU exchange failed (err %d)", __func__, err);
387 }
388
389 return err;
390 }
391
connected(struct bt_conn * conn,uint8_t conn_err)392 static void connected(struct bt_conn *conn, uint8_t conn_err)
393 {
394 struct bt_conn_info conn_info;
395 char addr[BT_ADDR_LE_STR_LEN];
396 int err;
397
398 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
399
400 if (conn_err) {
401 printk("%s: Failed to connect to %s (0x%02x)\n", __func__, addr,
402 conn_err);
403 return;
404 }
405
406 err = bt_conn_get_info(conn, &conn_info);
407 if (err) {
408 printk("Failed to get connection info (%d).\n", err);
409 return;
410 }
411
412 printk("%s: %s role %u\n", __func__, addr, conn_info.role);
413
414 conn_connected = bt_conn_ref(conn);
415
416 (void)mtu_exchange(conn);
417
418 #if defined(CONFIG_BT_SMP)
419 if (conn_info.role == BT_CONN_ROLE_CENTRAL) {
420 err = bt_conn_set_security(conn, BT_SECURITY_L2);
421 if (err) {
422 printk("Failed to set security (%d).\n", err);
423 }
424 }
425 #endif
426
427 if (IS_ENABLED(CONFIG_BT_USER_PHY_UPDATE)) {
428 phy_update_countdown = PHY_UPDATE_COUNTDOWN;
429 phy_param_idx = 0U;
430 }
431
432 /* Every 1 second the acknowledged total GATT Write without Response data size is used for
433 * the throughput calculation.
434 * PHY update is performed in reference to this calculation interval, and connection update
435 * is offset by 1 of this interval so that connection update is initiated one such interval
436 * after PHY update was requested.
437 */
438 param_update_countdown = PARAM_UPDATE_COUNTDOWN + 1U;
439 param_update_iteration = PARAM_UPDATE_ITERATION_MAX;
440 param_update_count = 0U;
441 param_update_idx = 0U;
442 }
443
disconnected(struct bt_conn * conn,uint8_t reason)444 static void disconnected(struct bt_conn *conn, uint8_t reason)
445 {
446 struct bt_conn_info conn_info;
447 char addr[BT_ADDR_LE_STR_LEN];
448 int err;
449
450 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
451
452 err = bt_conn_get_info(conn, &conn_info);
453 if (err) {
454 printk("Failed to get connection info (%d).\n", err);
455 return;
456 }
457
458 printk("%s: %s role %u, reason 0x%02x %s\n", __func__, addr, conn_info.role,
459 reason, bt_hci_err_to_str(reason));
460
461 conn_connected = NULL;
462
463 bt_conn_unref(conn);
464
465 if (conn_info.role == BT_CONN_ROLE_CENTRAL) {
466 start_scan_func();
467 }
468 }
469
le_param_req(struct bt_conn * conn,struct bt_le_conn_param * param)470 static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
471 {
472 printk("%s: int (0x%04x, 0x%04x) lat %u to %u\n", __func__,
473 param->interval_min, param->interval_max, param->latency,
474 param->timeout);
475
476 return true;
477 }
478
le_param_updated(struct bt_conn * conn,uint16_t interval,uint16_t latency,uint16_t timeout)479 static void le_param_updated(struct bt_conn *conn, uint16_t interval,
480 uint16_t latency, uint16_t timeout)
481 {
482 printk("%s: int 0x%04x lat %u to %u\n", __func__, interval,
483 latency, timeout);
484 }
485
486 #if defined(CONFIG_BT_SMP)
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)487 static void security_changed(struct bt_conn *conn, bt_security_t level,
488 enum bt_security_err err)
489 {
490 printk("%s: to level %u, err %s(%u)\n", __func__, level, bt_security_err_to_str(err), err);
491 }
492 #endif
493
494 #if defined(CONFIG_BT_USER_PHY_UPDATE)
le_phy_updated(struct bt_conn * conn,struct bt_conn_le_phy_info * param)495 static void le_phy_updated(struct bt_conn *conn,
496 struct bt_conn_le_phy_info *param)
497 {
498 char addr[BT_ADDR_LE_STR_LEN];
499
500 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
501
502 printk("LE PHY Updated: %s Tx 0x%x, Rx 0x%x\n", addr, param->tx_phy,
503 param->rx_phy);
504 }
505 #endif /* CONFIG_BT_USER_PHY_UPDATE */
506
507 #if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
le_data_len_updated(struct bt_conn * conn,struct bt_conn_le_data_len_info * info)508 static void le_data_len_updated(struct bt_conn *conn,
509 struct bt_conn_le_data_len_info *info)
510 {
511 char addr[BT_ADDR_LE_STR_LEN];
512
513 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
514
515 printk("Data length updated: %s max tx %u (%u us) max rx %u (%u us)\n",
516 addr, info->tx_max_len, info->tx_max_time, info->rx_max_len,
517 info->rx_max_time);
518 }
519 #endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */
520
521 BT_CONN_CB_DEFINE(conn_callbacks) = {
522 .connected = connected,
523 .disconnected = disconnected,
524 .le_param_req = le_param_req,
525 .le_param_updated = le_param_updated,
526
527 #if defined(CONFIG_BT_SMP)
528 .security_changed = security_changed,
529 #endif
530
531 #if defined(CONFIG_BT_USER_PHY_UPDATE)
532 .le_phy_updated = le_phy_updated,
533 #endif /* CONFIG_BT_USER_PHY_UPDATE */
534
535 #if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
536 .le_data_len_updated = le_data_len_updated,
537 #endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */
538 };
539
write_cmd(struct bt_conn * conn)540 int write_cmd(struct bt_conn *conn)
541 {
542 static uint8_t data[BT_ATT_MAX_ATTRIBUTE_LEN] = {0, };
543 static uint16_t data_len;
544 uint16_t data_len_max;
545 int err;
546
547 data_len_max = bt_gatt_get_mtu(conn) - 3;
548 if (data_len_max > BT_ATT_MAX_ATTRIBUTE_LEN) {
549 data_len_max = BT_ATT_MAX_ATTRIBUTE_LEN;
550 }
551
552 if (IS_ENABLED(CONFIG_USE_VARIABLE_LENGTH_DATA)) {
553 /* Use incremental length data for every write command */
554 /* TODO: Include test case in BabbleSim tests */
555 static bool decrement;
556
557 if (decrement) {
558 data_len--;
559 if (data_len <= 1) {
560 data_len = 1;
561 decrement = false;
562 }
563 } else {
564 data_len++;
565 if (data_len >= data_len_max) {
566 data_len = data_len_max;
567 decrement = true;
568 }
569 }
570 } else {
571 /* Use fixed length data for every write command */
572 data_len = data_len_max;
573 }
574
575 /* Pass the 16-bit data length value (instead of reference) in
576 * user_data so that unique value is pass for each write callback.
577 * Using handle 0x0001, we do not care if it is writable, we just want
578 * to transmit the data across.
579 */
580 err = bt_gatt_write_without_response_cb(conn, 0x0001, data, data_len,
581 false, write_cmd_cb,
582 (void *)((uint32_t)data_len));
583 if (err) {
584 printk("%s: Write cmd failed (%d).\n", __func__, err);
585 }
586
587 return err;
588 }
589