@@ -40,10 +40,13 @@ struct ble_eatt {
4040 uint8_t chan_num ;
4141 uint8_t used_channels ;
4242 uint8_t accept_channels ;
43+ uint8_t collision_ctrl ;
44+ uint8_t retry_count ;
4345
4446 /* Packet transmit queue */
4547 STAILQ_HEAD (, os_mbuf_pkthdr ) eatt_tx_q ;
4648
49+ struct os_callout collision_co ;
4750 struct ble_npl_event setup_ev ;
4851 struct ble_npl_event wakeup_ev ;
4952
@@ -199,6 +202,18 @@ ble_eatt_wakeup_cb(struct ble_npl_event *ev)
199202 }
200203}
201204
205+ static void
206+ ble_eatt_collision_ev (struct os_event * ev )
207+ {
208+ struct os_callout * co = (struct os_callout * )ev ;
209+
210+ struct ble_eatt * eatt = CONTAINER_OF (co , struct ble_eatt , collision_co );
211+
212+ if (eatt -> retry_count < 2 ) {
213+ ble_eatt_connect (eatt -> conn_handle , eatt -> chan_num );
214+ }
215+ }
216+
202217#if (MYNEWT_VAL (BLE_EATT_AUTO_CONNECT ))
203218void
204219ble_eatt_auto_conn_cb (struct os_event * ev )
@@ -232,6 +247,9 @@ ble_eatt_alloc(void)
232247
233248 STAILQ_INIT (& eatt -> eatt_tx_q );
234249
250+ os_callout_init (& eatt -> collision_co , os_eventq_dflt_get (),
251+ ble_eatt_collision_ev , NULL );
252+
235253#if (MYNEWT_VAL (BLE_EATT_AUTO_CONNECT ))
236254 os_callout_init (& eatt -> auto_conn_delay , os_eventq_dflt_get (),
237255 ble_eatt_auto_conn_cb , NULL );
@@ -262,6 +280,8 @@ ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg)
262280 struct ble_eatt * eatt = arg ;
263281 struct ble_gap_conn_desc desc ;
264282 uint8_t free_channels ;
283+ uint8_t collision_delay ;
284+ uint8_t collision_rand_time ;
265285 uint8_t opcode ;
266286 int rc ;
267287
@@ -272,13 +292,39 @@ ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg)
272292 event -> connect .conn_handle , event -> connect .chan -> scid ,
273293 event -> connect .chan -> dcid , event -> connect .status );
274294
295+ if (event -> connect .status == BLE_HS_ENOMEM && eatt -> collision_ctrl ) {
296+ BLE_EATT_LOG_DEBUG ("eatt: Connect collision handle: %d\n" ,
297+ event -> connect .conn_handle );
298+
299+ rc = ble_gap_conn_find (event -> connect .conn_handle , & desc );
300+ assert (rc == 0 );
301+
302+ rc = ble_hs_hci_rand (& collision_rand_time , 1 );
303+ if (rc != 0 ) {
304+ return rc ;
305+ }
306+
307+ collision_delay = (collision_rand_time % 5 ) + 2 * (desc .conn_latency + 1 ) * desc .conn_itvl ;
308+
309+ os_callout_reset (& eatt -> collision_co , collision_delay );
310+
311+ eatt -> retry_count ++ ;
312+ eatt -> used_channels -- ;
313+
314+ return 0 ;
315+ }
316+
275317 if (event -> connect .status ) {
276- ble_eatt_free ( eatt ) ;
318+ eatt -> used_channels -- ;
277319 return 0 ;
278320 }
279321 eatt -> chan = event -> connect .chan ;
280322 eatt -> conn_handle = event -> connect .conn_handle ;
281323
324+ /* Delete collision callout on successful connection */
325+ os_callout_stop (& eatt -> collision_co );
326+ eatt -> collision_ctrl = false;
327+
282328 eatt -> used_channels ++ ;
283329 BLE_EATT_LOG_DEBUG ("eatt: Channels already used for this connection %d\n" ,
284330 eatt -> used_channels );
@@ -321,6 +367,7 @@ ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg)
321367 ble_eatt_used_channels (eatt -> conn_handle );
322368
323369 if (free_channels == 0 ) {
370+ eatt -> collision_ctrl = true;
324371 BLE_EATT_LOG_ERROR ("eatt: Accept event | No free channels for "
325372 "conn_handle: %d\n" , event -> accept .conn_handle );
326373 return BLE_HS_ENOMEM ;
@@ -420,6 +467,8 @@ ble_eatt_setup_cb(struct ble_npl_event *ev)
420467
421468 BLE_EATT_LOG_DEBUG ("eatt: connecting eatt on conn_handle 0x%04x\n" , eatt -> conn_handle );
422469
470+ eatt -> used_channels += eatt -> chan_num ;
471+
423472 rc = ble_l2cap_enhanced_connect (eatt -> conn_handle , BLE_EATT_PSM ,
424473 MYNEWT_VAL (BLE_EATT_MTU ),
425474 eatt -> chan_num , & om ,
@@ -654,6 +703,17 @@ ble_eatt_connect(uint16_t conn_handle, uint8_t chan_num)
654703 }
655704 }
656705
706+ /*
707+ * 5.3 Vol 3, Part G, Sec. 5.4 L2CAP collision mitigation
708+ * Peripheral shall wait some time before retrying connection.
709+ * Central may reconnect without any delay.
710+ * To reconnect user has to call ble_eatt_connect again.
711+ */
712+ if (desc .role == BLE_GAP_ROLE_SLAVE && os_callout_queued (& eatt -> collision_co )) {
713+ BLE_EATT_LOG_WARN ("ble_eatt_connect: Connection collision for handle %d\n" ,
714+ conn_handle );
715+ }
716+
657717 /*
658718 return BLE_HS_EALREADY;
659719 * Warn about exceeding the number
0 commit comments