@@ -105,9 +105,42 @@ char pbdrv_bluetooth_hub_name[16] = "Pybricks Hub";
105105
106106static uint8_t * event_packet ;
107107static const pbdrv_bluetooth_btstack_platform_data_t * pdata = & pbdrv_bluetooth_btstack_platform_data ;
108- static hci_con_handle_t le_con_handle = HCI_CON_HANDLE_INVALID ;
109- static hci_con_handle_t pybricks_con_handle = HCI_CON_HANDLE_INVALID ;
110- static hci_con_handle_t uart_con_handle = HCI_CON_HANDLE_INVALID ;
108+
109+ /**
110+ * State of a connected host (Pybricks Code or similar).
111+ */
112+ typedef struct {
113+ /** Connection handle. */
114+ hci_con_handle_t con_handle ;
115+ /** Pybricks service is configured. */
116+ bool pybricks_configured ;
117+ /** UART service is configured. */
118+ bool uart_configured ;
119+ /** Notification to send when ready. */
120+ btstack_context_callback_registration_t send_request ;
121+ /** Notification to send. */
122+ const uint8_t * notification_data ;
123+ /** Notification size to send. */
124+ uint16_t notification_size ;
125+ /** Notification has been sent. */
126+ bool notification_done ;
127+ } pbdrv_bluetooth_btstack_host_connection_t ;
128+
129+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
130+ static pbdrv_bluetooth_btstack_host_connection_t host_connections [PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ];
131+ #endif
132+
133+ static pbdrv_bluetooth_btstack_host_connection_t * pbdrv_bluetooth_btstack_get_host_connection (hci_con_handle_t con_handle ) {
134+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
135+ for (size_t i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
136+ pbdrv_bluetooth_btstack_host_connection_t * host = & host_connections [i ];
137+ if (host -> con_handle == con_handle ) {
138+ return host ;
139+ }
140+ }
141+ #endif
142+ return NULL ;
143+ }
111144
112145/**
113146 * Converts BTStack error to most appropriate PBIO error.
@@ -132,6 +165,7 @@ static pbio_error_t att_error_to_pbio_error(uint8_t status) {
132165}
133166
134167static pbio_pybricks_error_t pybricks_data_received (hci_con_handle_t tx_con_handle , const uint8_t * data , uint16_t size ) {
168+ // Treating all incoming host data the same.
135169 if (pbdrv_bluetooth_receive_handler ) {
136170 return pbdrv_bluetooth_receive_handler (data , size );
137171 }
@@ -140,7 +174,11 @@ static pbio_pybricks_error_t pybricks_data_received(hci_con_handle_t tx_con_hand
140174}
141175
142176static void pybricks_configured (hci_con_handle_t tx_con_handle , uint16_t value ) {
143- pybricks_con_handle = value ? tx_con_handle : HCI_CON_HANDLE_INVALID ;
177+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection (tx_con_handle );
178+ if (host == NULL ) {
179+ return ;
180+ }
181+ host -> pybricks_configured = !!value ;
144182 pbdrv_bluetooth_host_connection_changed ();
145183}
146184
@@ -213,7 +251,15 @@ bool pbdrv_bluetooth_peripheral_is_connected(pbdrv_bluetooth_peripheral_t *peri)
213251}
214252
215253bool pbdrv_bluetooth_host_is_connected (void ) {
216- return pybricks_con_handle != HCI_CON_HANDLE_INVALID ;
254+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
255+ for (size_t i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
256+ pbdrv_bluetooth_btstack_host_connection_t * host = & host_connections [i ];
257+ if (host -> con_handle != HCI_CON_HANDLE_INVALID ) {
258+ return true;
259+ }
260+ }
261+ #endif
262+ return false;
217263}
218264
219265bool pbdrv_bluetooth_hci_is_enabled (void ) {
@@ -228,12 +274,22 @@ static void nordic_spp_packet_handler(uint8_t packet_type, uint16_t channel, uin
228274 }
229275
230276 switch (hci_event_gattservice_meta_get_subevent_code (packet )) {
231- case GATTSERVICE_SUBEVENT_SPP_SERVICE_CONNECTED :
232- uart_con_handle = gattservice_subevent_spp_service_connected_get_con_handle (packet );
277+ case GATTSERVICE_SUBEVENT_SPP_SERVICE_CONNECTED : {
278+ uint16_t handle = gattservice_subevent_spp_service_connected_get_con_handle (packet );
279+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection (handle );
280+ if (host ) {
281+ host -> uart_configured = true;
282+ }
233283 break ;
234- case GATTSERVICE_SUBEVENT_SPP_SERVICE_DISCONNECTED :
235- uart_con_handle = HCI_CON_HANDLE_INVALID ;
284+ }
285+ case GATTSERVICE_SUBEVENT_SPP_SERVICE_DISCONNECTED : {
286+ uint16_t handle = gattservice_subevent_spp_service_disconnected_get_con_handle (packet );
287+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection (handle );
288+ if (host ) {
289+ host -> uart_configured = false;
290+ }
236291 break ;
292+ }
237293 default :
238294 break ;
239295 }
@@ -338,32 +394,42 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
338394 // HCI_ROLE_SLAVE means the connecting device is the central and the hub is the peripheral
339395 // HCI_ROLE_MASTER means the connecting device is the peripheral and the hub is the central.
340396 if (hci_subevent_le_connection_complete_get_role (packet ) == HCI_ROLE_SLAVE ) {
341- le_con_handle = hci_subevent_le_connection_complete_get_connection_handle (packet );
397+ uint16_t handle = hci_subevent_le_connection_complete_get_connection_handle (packet );
398+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection (HCI_CON_HANDLE_INVALID );
399+ if (host == NULL ) {
400+ DEBUG_PRINT ("Warning: more than %d LE connections established.\n" , PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS );
401+ break ;
402+ }
403+ host -> con_handle = handle ;
342404
343405 // don't start advertising again on disconnect
344406 gap_advertisements_enable (false);
345407 pbdrv_bluetooth_advertising_state = PBDRV_BLUETOOTH_ADVERTISING_STATE_NONE ;
346408 }
347409 break ;
348- case HCI_EVENT_DISCONNECTION_COMPLETE :
410+ case HCI_EVENT_DISCONNECTION_COMPLETE : {
349411 DEBUG_PRINT ("HCI_EVENT_DISCONNECTION_COMPLETE\n" );
350- if (hci_event_disconnection_complete_get_connection_handle (packet ) == le_con_handle ) {
351- le_con_handle = HCI_CON_HANDLE_INVALID ;
352- pybricks_con_handle = HCI_CON_HANDLE_INVALID ;
353- uart_con_handle = HCI_CON_HANDLE_INVALID ;
412+ uint16_t handle = hci_event_disconnection_complete_get_connection_handle (packet );
413+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection (handle );
414+ if (host ) {
415+ host -> con_handle = HCI_CON_HANDLE_INVALID ;
416+ host -> pybricks_configured = false;
417+ host -> uart_configured = false;
354418 pbdrv_bluetooth_host_connection_changed ();
419+ DEBUG_PRINT ("Host with handle %u disconnected\n" , handle );
355420 } else {
356421 for (uint8_t i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_NUM_PERIPHERALS ; i ++ ) {
357422 pbdrv_bluetooth_peripheral_t * peri = pbdrv_bluetooth_peripheral_get_by_index (i );
358- if (peri && hci_event_disconnection_complete_get_connection_handle ( packet ) == peri -> con_handle ) {
423+ if (peri && handle == peri -> con_handle ) {
359424 DEBUG_PRINT ("Peripheral %u with handle %u disconnected\n" , i , peri -> con_handle );
360425 gatt_client_stop_listening_for_characteristic_value_updates (& peri -> platform_state -> notification );
361426 peri -> con_handle = HCI_CON_HANDLE_INVALID ;
427+ break ;
362428 }
363429 }
364430 }
365431 break ;
366-
432+ }
367433 case GAP_EVENT_ADVERTISING_REPORT : {
368434 uint8_t event_type = gap_event_advertising_report_get_advertising_event_type (packet );
369435 uint8_t data_length = gap_event_advertising_report_get_data_length (packet );
@@ -508,7 +574,7 @@ static void init_advertising_data(void) {
508574}
509575
510576pbio_error_t pbdrv_bluetooth_start_advertising_func (pbio_os_state_t * state , void * context ) {
511- #if !PBDRV_CONFIG_BLUETOOTH_BTSTACK_LE_SERVER
577+ #if !PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
512578 return PBIO_ERROR_NOT_SUPPORTED ;
513579 #endif
514580
@@ -518,10 +584,10 @@ pbio_error_t pbdrv_bluetooth_start_advertising_func(pbio_os_state_t *state, void
518584
519585 PBIO_OS_ASYNC_BEGIN (state );
520586
521- // Don't advertise if already connected. BTstack also protects against this,
522- // but it means we never get the HCI event complete command because it is
523- // never given.
524- if ( le_con_handle != HCI_CON_HANDLE_INVALID ) {
587+ pbdrv_bluetooth_btstack_host_connection_t * host = pbdrv_bluetooth_btstack_get_host_connection ( HCI_CON_HANDLE_INVALID );
588+ if ( host == NULL ) {
589+ // There should be at least one available host connection. Otherwise
590+ // we will never receive the advertise completion event below.
525591 return PBIO_ERROR_INVALID_OP ;
526592 }
527593
@@ -551,43 +617,50 @@ pbio_error_t pbdrv_bluetooth_stop_advertising_func(pbio_os_state_t *state, void
551617 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
552618}
553619
554- typedef struct {
555- const uint8_t * data ;
556- uint16_t size ;
557- bool done ;
558- } send_data_t ;
559-
620+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
560621static void pybricks_on_ready_to_send (void * context ) {
561- send_data_t * send = context ;
562- pybricks_service_server_send (pybricks_con_handle , send -> data , send -> size );
563- send -> done = true;
622+ pbdrv_bluetooth_btstack_host_connection_t * host = context ;
623+ pybricks_service_server_send (host -> con_handle , host -> notification_data , host -> notification_size );
624+ host -> notification_done = true;
564625 pbio_os_request_poll ();
565626}
627+ #endif
566628
567629pbio_error_t pbdrv_bluetooth_send_pybricks_value_notification (pbio_os_state_t * state , const uint8_t * data , uint16_t size ) {
568- #if !PBDRV_CONFIG_BLUETOOTH_BTSTACK_LE_SERVER
569- return PBIO_ERROR_NOT_SUPPORTED ;
570- #endif
571-
572630 if (!pbdrv_bluetooth_btstack_ble_supported ()) {
573631 return PBIO_ERROR_NOT_SUPPORTED ;
574632 }
575633
576- static send_data_t send_data ;
634+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
635+ static size_t i ;
636+ static pbdrv_bluetooth_btstack_host_connection_t * host ;
577637
578- static btstack_context_callback_registration_t send_request = {
579- .callback = pybricks_on_ready_to_send ,
580- .context = & send_data ,
581- };
582638 PBIO_OS_ASYNC_BEGIN (state );
583639
584- send_data .data = data ;
585- send_data .size = size ;
586- send_data .done = false;
587- pybricks_service_server_request_can_send_now (& send_request , pybricks_con_handle );
588- PBIO_OS_AWAIT_UNTIL (state , send_data .done );
640+ for (i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
641+ host = & host_connections [i ];
642+ if (host -> con_handle != HCI_CON_HANDLE_INVALID && host -> pybricks_configured ) {
643+ host -> notification_data = data ;
644+ host -> notification_size = size ;
645+ host -> notification_done = false;
646+ pybricks_service_server_request_can_send_now (& host -> send_request , host -> con_handle );
647+ } else {
648+ // No connection or not configured, don't hold up wait loop below.
649+ host -> notification_done = true;
650+ }
651+ }
652+
653+ // Wait for all notifications to be sent. BTstack handles sending
654+ // asynchronously. This loop completes when the slowest one is done.
655+ for (i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
656+ host = & host_connections [i ];
657+ PBIO_OS_AWAIT_UNTIL (state , host -> notification_done );
658+ }
589659
590660 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
661+ #else
662+ return PBIO_ERROR_NOT_SUPPORTED ;
663+ #endif
591664}
592665
593666pbio_error_t pbdrv_bluetooth_peripheral_scan_and_connect_func (pbio_os_state_t * state , void * context ) {
@@ -1192,10 +1265,18 @@ pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_ti
11921265 PBIO_OS_ASYNC_BEGIN (state );
11931266
11941267 // Disconnect gracefully if connected to host.
1195- if (le_con_handle != HCI_CON_HANDLE_INVALID ) {
1196- gap_disconnect (le_con_handle );
1197- PBIO_OS_AWAIT_UNTIL (state , le_con_handle == HCI_CON_HANDLE_INVALID );
1268+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
1269+ static size_t i ;
1270+ static pbdrv_bluetooth_btstack_host_connection_t * host ;
1271+ for (i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
1272+ host = & host_connections [i ];
1273+ if (host -> con_handle == HCI_CON_HANDLE_INVALID ) {
1274+ continue ;
1275+ }
1276+ gap_disconnect (host -> con_handle );
1277+ PBIO_OS_AWAIT_UNTIL (state , host -> con_handle == HCI_CON_HANDLE_INVALID );
11981278 }
1279+ #endif
11991280
12001281 // Wait for power off.
12011282 PBIO_OS_AWAIT (state , & sub , bluetooth_btstack_handle_power_control (& sub , HCI_POWER_OFF , HCI_STATE_OFF ));
@@ -1341,7 +1422,7 @@ void pbdrv_bluetooth_init_hci(void) {
13411422 // GATT Client setup
13421423 gatt_client_init ();
13431424
1344- #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_LE_SERVER
1425+ #if PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS
13451426 // setup ATT server
13461427 att_server_init (profile_data , att_read_callback , NULL );
13471428
@@ -1352,6 +1433,13 @@ void pbdrv_bluetooth_init_hci(void) {
13521433
13531434 pybricks_service_server_init (pybricks_data_received , pybricks_configured );
13541435 nordic_spp_service_server_init (nordic_spp_packet_handler );
1436+
1437+ for (size_t i = 0 ; i < PBDRV_CONFIG_BLUETOOTH_BTSTACK_NUM_LE_HOSTS ; i ++ ) {
1438+ pbdrv_bluetooth_btstack_host_connection_t * host = & host_connections [i ];
1439+ host -> con_handle = HCI_CON_HANDLE_INVALID ;
1440+ host -> send_request .callback = pybricks_on_ready_to_send ;
1441+ host -> send_request .context = host ;
1442+ }
13551443 #else
13561444 (void )pybricks_data_received ;
13571445 (void )att_read_callback ;
0 commit comments