33 *
44 * SPDX-License-Identifier: Apache-2.0
55 *
6- * Subrating central: connect to the peripheral at a fixed interval, let the
7- * peripheral negotiate subrating (factor > 1), then probe the link with timed
8- * GATT reads while the peripheral is idle. Because a subrated peripheral only
9- * listens on subrated events, a read issued while it sleeps is answered only
10- * after it next wakes, so the observed read latency reveals the skip cadence:
11- * latencies far above one connection interval prove the peripheral skipped
12- * events. The central itself (not yet subrating-aware) is present on every
13- * event, so this isolates the peripheral skipping (Phase 2).
6+ * Subrating central. Two scenarios:
7+ * "central" - let the peripheral negotiate subrating (factor > 1)
8+ * and verify via timed GATT reads that the peripheral
9+ * skips connection events (Phase 2 / 3a).
10+ * "central_transitions" - peripheral negotiates factor M, central re-negotiates
11+ * to factor N (M->N transition), verify both re-skip at
12+ * N; then change the connection interval while subrated
13+ * and verify subrating resets to factor 1 (gate handles
14+ * the update, no crash, both present every event).
15+ *
16+ * A subrated peer only listens on subrated events, so a read issued while it
17+ * sleeps is answered only after the next subrated event: large read latency
18+ * proves skipping, small latency proves no skipping.
1419 */
1520#include <zephyr/kernel.h>
1621
@@ -52,6 +57,7 @@ static struct bt_conn *default_conn;
5257static struct bt_gatt_read_params read_params ;
5358
5459static volatile uint16_t subrate_factor ;
60+ static volatile uint16_t conn_interval ;
5561static volatile int read_err ;
5662static K_SEM_DEFINE (read_done , 0 , 1 ) ;
5763
@@ -81,6 +87,14 @@ static void subrate_changed(struct bt_conn *conn,
8187 subrate_factor = params -> factor ;
8288}
8389
90+ static void le_param_updated (struct bt_conn * conn , uint16_t interval ,
91+ uint16_t latency , uint16_t timeout )
92+ {
93+ printk ("Central conn params updated: interval %u latency %u timeout %u\n" ,
94+ interval , latency , timeout );
95+ conn_interval = interval ;
96+ }
97+
8498static void connected (struct bt_conn * conn , uint8_t conn_err )
8599{
86100 if (conn_err ) {
@@ -105,6 +119,7 @@ static struct bt_conn_cb conn_callbacks = {
105119 .connected = connected ,
106120 .disconnected = disconnected ,
107121 .subrate_changed = subrate_changed ,
122+ .le_param_updated = le_param_updated ,
108123};
109124
110125static void device_found (const bt_addr_le_t * addr , int8_t rssi , uint8_t type ,
@@ -179,7 +194,30 @@ static int64_t probe_read_latency(void)
179194 return k_uptime_get () - t0 ;
180195}
181196
182- static void test_central_main (void )
197+ /* Run `count` probes spaced by READ_GAP_MS and return the worst-case latency,
198+ * or -1 on failure. The gap exceeds the skip period so the peripheral has gone
199+ * back to sleep before each probe.
200+ */
201+ static int64_t probe_max_latency (int count )
202+ {
203+ int64_t max_latency = 0 ;
204+
205+ for (int i = 0 ; i < count ; i ++ ) {
206+ int64_t latency = probe_read_latency ();
207+
208+ if (latency < 0 ) {
209+ return -1 ;
210+ }
211+
212+ printk ("Central read %d latency %lld ms\n" , i , latency );
213+ max_latency = MAX (max_latency , latency );
214+ k_sleep (K_MSEC (READ_GAP_MS ));
215+ }
216+
217+ return max_latency ;
218+ }
219+
220+ static int central_start (void )
183221{
184222 struct bt_conn_le_subrate_param defaults = {
185223 .subrate_min = 1U ,
@@ -188,81 +226,80 @@ static void test_central_main(void)
188226 .continuation_number = 0U ,
189227 .supervision_timeout = CONN_TIMEOUT_UNITS ,
190228 };
191- struct bt_conn_info info ;
192- int64_t max_latency = 0 ;
193- uint32_t interval_ms ;
194- uint32_t threshold_ms ;
195229 int err ;
196230
197231 bt_conn_cb_register (& conn_callbacks );
198232
199233 err = bt_enable (NULL );
200234 if (err ) {
201235 FAIL ("Bluetooth init failed (err %d)\n" , err );
202- return ;
236+ return err ;
203237 }
204238
205239 printk ("Central Bluetooth initialized\n" );
206240
207- /* Allow the peripheral's request to be granted with a large factor. */
241+ /* Allow the peripheral's requests to be granted with a large factor. */
208242 err = bt_conn_le_subrate_set_defaults (& defaults );
209243 if (err ) {
210244 FAIL ("Set default subrate failed (err %d)\n" , err );
211- return ;
245+ return err ;
212246 }
213247
214248 err = bt_le_scan_start (BT_LE_SCAN_ACTIVE , device_found );
215249 if (err ) {
216250 FAIL ("Scanning failed to start (err %d)\n" , err );
217- return ;
251+ return err ;
218252 }
219253
220- /* Wait until the link is up and subrating has been negotiated (factor>1). */
221- while (!default_conn || subrate_factor < 2U ) {
222- k_sleep (K_MSEC (100 ));
254+ return 0 ;
255+ }
223256
224- if (bst_result == Failed ) {
225- return ;
226- }
227- }
257+ /* Wait for a condition with the WAIT_TIME tick as the backstop, bailing on
258+ * an already-failed result.
259+ */
260+ #define WAIT_FOR (_cond ) \
261+ do { \
262+ while (!(_cond)) { \
263+ k_sleep(K_MSEC(100)); \
264+ if (bst_result == Failed) { \
265+ return; \
266+ } \
267+ } \
268+ } while (0)
228269
229- err = bt_conn_get_info (default_conn , & info );
230- if (err ) {
231- FAIL ("Central conn info failed (err %d)\n" , err );
270+ static uint32_t interval_to_ms (uint16_t units )
271+ {
272+ return (units * 5U ) / 4U ; /* 1.25 ms units -> ms */
273+ }
274+
275+ static void test_central_main (void )
276+ {
277+ int64_t max_latency ;
278+ uint32_t interval_ms ;
279+ uint32_t threshold_ms ;
280+
281+ if (central_start ()) {
232282 return ;
233283 }
234- interval_ms = (info .le .interval * 5U ) / 4U ; /* 1.25 ms units -> ms */
235284
236- /* A subrated, idle peripheral listens once every (factor * interval); a
237- * probe issued while it sleeps waits up to that long. Require the worst
238- * observed latency to clear half the skip period - unreachable unless
239- * the peripheral actually skipped events.
240- */
241- threshold_ms = (subrate_factor * interval_ms ) / 2U ;
285+ /* Wait for the link and a negotiated factor > 1. */
286+ WAIT_FOR (default_conn && subrate_factor >= 2U );
242287
288+ interval_ms = interval_to_ms (CONN_INTERVAL_UNITS );
289+ threshold_ms = (subrate_factor * interval_ms ) / 2U ;
243290 printk ("Central probing: factor %u, interval %u ms, threshold %u ms\n" ,
244291 subrate_factor , interval_ms , threshold_ms );
245292
246- for (int i = 0 ; i < NUM_READS ; i ++ ) {
247- int64_t latency = probe_read_latency ();
248-
249- if (latency < 0 ) {
250- return ; /* FAIL already set */
251- }
252-
253- printk ("Central read %d latency %lld ms\n" , i , latency );
254- max_latency = MAX (max_latency , latency );
255-
256- /* Let the peripheral go back to sleep before the next probe. */
257- k_sleep (K_MSEC (READ_GAP_MS ));
293+ max_latency = probe_max_latency (NUM_READS );
294+ if (max_latency < 0 ) {
295+ return ;
258296 }
259297
260298 printk ("Central max read latency %lld ms (threshold %u ms)\n" ,
261299 max_latency , threshold_ms );
262-
263300 if (max_latency < threshold_ms ) {
264- FAIL ("Peripheral did not skip events: max read latency %lld ms "
265- "below threshold %u ms\n" , max_latency , threshold_ms );
301+ FAIL ("Peripheral did not skip events: max latency %lld ms < %u ms\n" ,
302+ max_latency , threshold_ms );
266303 return ;
267304 }
268305
@@ -272,6 +309,82 @@ static void test_central_main(void)
272309 bs_trace_silent_exit (0 );
273310}
274311
312+ static void test_central_main_transitions (void )
313+ {
314+ struct bt_conn_le_subrate_param to_n = {
315+ .subrate_min = 1U ,
316+ .subrate_max = SUBRATE_MTON_N ,
317+ .max_latency = 0U ,
318+ .continuation_number = 0U ,
319+ .supervision_timeout = CONN_TIMEOUT_UNITS ,
320+ };
321+ struct bt_le_conn_param * upd ;
322+ uint32_t interval_ms ;
323+ int64_t max_latency ;
324+ int err ;
325+
326+ if (central_start ()) {
327+ return ;
328+ }
329+
330+ /* Peripheral negotiates factor M first. */
331+ WAIT_FOR (default_conn && subrate_factor == SUBRATE_MTON_M );
332+ printk ("Central: peripheral negotiated M=%u\n" , subrate_factor );
333+
334+ /* M->N: Central re-negotiates to factor N (central-initiated, 5.1.19). */
335+ err = bt_conn_le_subrate_request (default_conn , & to_n );
336+ if (err ) {
337+ FAIL ("Central M->N subrate request failed (err %d)\n" , err );
338+ return ;
339+ }
340+ WAIT_FOR (subrate_factor == SUBRATE_MTON_N );
341+ printk ("Central: transitioned to N=%u\n" , subrate_factor );
342+
343+ /* Both should now skip at factor N. */
344+ interval_ms = interval_to_ms (CONN_INTERVAL_UNITS );
345+ max_latency = probe_max_latency (NUM_READS );
346+ if (max_latency < 0 ) {
347+ return ;
348+ }
349+ printk ("Central post-M->N max latency %lld ms\n" , max_latency );
350+ if (max_latency < (SUBRATE_MTON_N * interval_ms ) / 2U ) {
351+ FAIL ("No skip after M->N: max latency %lld ms\n" , max_latency );
352+ return ;
353+ }
354+
355+ /* conn-update while subrated: change the interval, which resets subrating
356+ * to factor 1. Validates that the procedure completes (gate keeps both
357+ * present every event, #51 prevents the latency_upd crash) and that the
358+ * link survives.
359+ */
360+ conn_interval = 0U ;
361+ upd = BT_LE_CONN_PARAM (CONN_UPDATE_INTERVAL_UNITS , CONN_UPDATE_INTERVAL_UNITS ,
362+ 0 , CONN_TIMEOUT_UNITS );
363+ err = bt_conn_le_param_update (default_conn , upd );
364+ if (err ) {
365+ FAIL ("Central conn param update failed (err %d)\n" , err );
366+ return ;
367+ }
368+ WAIT_FOR (conn_interval == CONN_UPDATE_INTERVAL_UNITS );
369+ printk ("Central: interval updated to %u units while subrated\n" , conn_interval );
370+
371+ /* Subrating must have reset to factor 1 -> no more skipping -> low latency. */
372+ max_latency = probe_max_latency (NUM_READS / 2 );
373+ if (max_latency < 0 ) {
374+ return ;
375+ }
376+ printk ("Central post-update max latency %lld ms\n" , max_latency );
377+ if (max_latency >= (SUBRATE_MTON_N * interval_to_ms (CONN_UPDATE_INTERVAL_UNITS )) / 2U ) {
378+ FAIL ("Subrating not reset after interval change: max latency %lld ms\n" ,
379+ max_latency );
380+ return ;
381+ }
382+
383+ (void )bt_conn_disconnect (default_conn , BT_HCI_ERR_REMOTE_USER_TERM_CONN );
384+ PASS ("Central M->N + conn-update validated\n" );
385+ bs_trace_silent_exit (0 );
386+ }
387+
275388static void test_central_init (void )
276389{
277390 bst_ticker_set_next_tick_absolute (WAIT_TIME * 1e6 );
@@ -288,13 +401,20 @@ static void test_central_tick(bs_time_t HW_device_time)
288401static const struct bst_test_instance test_central [] = {
289402 {
290403 .test_id = "central" ,
291- .test_descr = "Subrating central: connects, lets the peripheral "
292- "negotiate subrating, and verifies via timed reads "
293- "that the peripheral skips connection events." ,
404+ .test_descr = "Central: peripheral negotiates subrating; verify the "
405+ "peripheral skips connection events via timed reads." ,
294406 .test_pre_init_f = test_central_init ,
295407 .test_tick_f = test_central_tick ,
296408 .test_main_f = test_central_main ,
297409 },
410+ {
411+ .test_id = "central_transitions" ,
412+ .test_descr = "Central: M->N subrate transition then a conn-param "
413+ "interval change while subrated (resets to factor 1)." ,
414+ .test_pre_init_f = test_central_init ,
415+ .test_tick_f = test_central_tick ,
416+ .test_main_f = test_central_main_transitions ,
417+ },
298418 BSTEST_END_MARKER ,
299419};
300420
0 commit comments