Skip to content

Commit 279aba9

Browse files
committed
pbio/drv/bluetooth: Fix peripheral notifications not handled after reconnect.
The finalizer is usually called _after_ a new Remote object is made (when it is reassigned to the same variable), so we would unset the memory reference for storing notifications. The Powered Up Remote or Xbox Controller would connect but notification would appear to never arrive, even though the message did arrive. They were just not processed.
1 parent 938eefb commit 279aba9

8 files changed

Lines changed: 66 additions & 47 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
- Fixed Powered Up remote raising the wrong exception on timeout ([support#2521]).
3131
- Fixed Xbox Controller connection waiting indefinitely if it isn't in pairing mode ([support#2521]).
3232
- Fixed Xbox Controller attempting to pair with host ([support#2522]).
33+
- Fixed Powered Up remote and Xbox Controller working only after the first
34+
connection. All of the above Remote and Xbox Controller were introduced in
35+
the previous beta release, so did not affect any stable release ([support#2521]).
3336

3437
[support#1962]: https://github.com/pybricks/support/issues/1962
3538
[support#2468]: https://github.com/pybricks/support/issues/2468

lib/pbio/drv/bluetooth/bluetooth.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,34 @@ pbio_error_t pbdrv_bluetooth_send_event_notification(pbio_os_state_t *state, pbi
149149

150150
pbdrv_bluetooth_peripheral_t peripheral_singleton;
151151

152-
pbio_error_t pbdrv_bluetooth_peripheral_get_available(pbdrv_bluetooth_peripheral_t **peripheral) {
152+
pbio_error_t pbdrv_bluetooth_peripheral_get_available(pbdrv_bluetooth_peripheral_t **peripheral, void *user) {
153153
// Only a single peripheral instance supported for now.
154-
if (peripheral_singleton.func || pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_PERIPHERAL)) {
154+
*peripheral = &peripheral_singleton;
155+
156+
if (/* Already connected. */
157+
pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_PERIPHERAL) ||
158+
/* A different user is still claiming this resource. */
159+
(*peripheral)->user ||
160+
/* Busy. Could be connecting but not connected yet. */
161+
(*peripheral)->func) {
155162
return PBIO_ERROR_BUSY;
156163
}
157-
*peripheral = &peripheral_singleton;
164+
165+
// Claim this device for new user.
166+
(*peripheral)->user = user;
167+
158168
return PBIO_SUCCESS;
159169
}
160170

171+
void pbdrv_bluetooth_peripheral_release(pbdrv_bluetooth_peripheral_t *peripheral, void *user) {
172+
// Only release if the user matches. A new user may have already safely
173+
// claimed it, and this call to release may come from an orphaned user.
174+
if (peripheral->user != user) {
175+
return;
176+
}
177+
peripheral->user = NULL;
178+
}
179+
161180
const char *pbdrv_bluetooth_peripheral_get_name(void) {
162181
pbdrv_bluetooth_peripheral_t *peri = &peripheral_singleton;
163182
return peri->name;
@@ -176,8 +195,12 @@ pbio_error_t pbdrv_bluetooth_peripheral_scan_and_connect(pbdrv_bluetooth_periphe
176195
return PBIO_ERROR_BUSY;
177196
}
178197

179-
// Initialize operation for handling on the main thread.
198+
// Reset peripheral instance but keep user reference.
199+
void *user = peri->user;
180200
memset(peri, 0, sizeof(pbdrv_bluetooth_peripheral_t));
201+
peri->user = user;
202+
203+
// Initialize operation for handling on the main thread.
181204
peri->config = config;
182205
peri->func = pbdrv_bluetooth_peripheral_scan_and_connect_func;
183206
peri->err = PBIO_ERROR_AGAIN;

lib/pbio/drv/bluetooth/bluetooth_btstack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
361361
if (peri->config->notification_handler) {
362362
uint16_t length = gatt_event_notification_get_value_length(packet);
363363
const uint8_t *value = gatt_event_notification_get_value(packet);
364-
peri->config->notification_handler(peri, value, length);
364+
peri->config->notification_handler(peri->user, value, length);
365365
}
366366
break;
367367
}

lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ static void handle_event(hci_event_pckt *event) {
952952
evt_gatt_attr_notification *subevt = (void *)evt->data;
953953
pbdrv_bluetooth_peripheral_t *peri = &peripheral_singleton;
954954
if (peri->config->notification_handler) {
955-
peri->config->notification_handler(peri, subevt->attr_value, subevt->event_data_length - 2);
955+
peri->config->notification_handler(peri->user, subevt->attr_value, subevt->event_data_length - 2);
956956
}
957957
}
958958
break;

lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ static void handle_event(uint8_t *packet) {
13101310
// uint8_t attr_handle = (data[7] << 8) | data[6];
13111311
pbdrv_bluetooth_peripheral_t *peri = &peripheral_singleton;
13121312
if (peri->config->notification_handler) {
1313-
peri->config->notification_handler(peri, &data[8], pdu_len - 2);
1313+
peri->config->notification_handler(peri->user, &data[8], pdu_len - 2);
13141314
}
13151315
}
13161316
break;

lib/pbio/include/pbdrv/bluetooth.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ typedef enum {
135135
typedef struct _pbdrv_bluetooth_peripheral_t pbdrv_bluetooth_peripheral_t;
136136

137137
/**
138-
* Callback that is called when peripheral sends a notification.
138+
* Callback that is called when a peripheral sends a notification.
139139
*
140-
* @param [in] peripheral The peripheral that sent the notification.
140+
* @param [in] user The addressee, usually a high-level object.
141141
* @param [in] data The data that was received.
142142
* @param [in] size The size of @p data in bytes.
143143
*/
144-
typedef void (*pbdrv_bluetooth_peripheral_notification_handler_t)(pbdrv_bluetooth_peripheral_t *peripheral, const uint8_t *data, uint32_t size);
144+
typedef void (*pbdrv_bluetooth_peripheral_notification_handler_t)(void *user, const uint8_t *data, uint32_t size);
145145

146146
/** Peripheral scan and connection configuration */
147147
typedef struct {
@@ -161,6 +161,10 @@ typedef struct {
161161
* State of a peripheral that the hub may be connected to, such as a remote.
162162
*/
163163
struct _pbdrv_bluetooth_peripheral_t {
164+
/**
165+
* Optional reference to higher-level user of this peripheral.
166+
*/
167+
void *user;
164168
uint16_t con_handle;
165169
uint8_t status;
166170
uint8_t bdaddr_type;
@@ -356,11 +360,20 @@ pbio_error_t pbdrv_bluetooth_send_event_notification(pbio_os_state_t *state, pbi
356360
* Gets an available peripheral instance.
357361
*
358362
* @param [out] peripheral Pointer to the peripheral instance if found.
363+
* @param [in] user Optional user reference to associate with the peripheral.
359364
* @return ::PBIO_SUCCESS if a peripheral instance is available.
360365
* ::PBIO_ERROR_NO_DEV if no peripheral instance is available.
361366
* ::PBIO_ERROR_BUSY if all peripheral instances are in use.
362367
*/
363-
pbio_error_t pbdrv_bluetooth_peripheral_get_available(pbdrv_bluetooth_peripheral_t **peripheral);
368+
pbio_error_t pbdrv_bluetooth_peripheral_get_available(pbdrv_bluetooth_peripheral_t **peripheral, void *user);
369+
370+
/**
371+
* Releases a peripheral instance for reuse by another user.
372+
*
373+
* @param [in] peripheral The peripheral instance to free.
374+
* @param [in] user The user reference that was used to claim the peripheral.
375+
*/
376+
void pbdrv_bluetooth_peripheral_release(pbdrv_bluetooth_peripheral_t *peripheral, void *user);
364377

365378
/**
366379
* Gets the name of the connected peripheral.

pybricks/iodevices/pb_type_iodevices_lwp3device.c

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,10 @@ typedef struct {
152152
} pb_lwp3device_obj_t;
153153

154154
// Handles LEGO Wireless protocol messages from the Powered Up Remote.
155-
static void handle_remote_notification(pbdrv_bluetooth_peripheral_t *peripheral, const uint8_t *value, uint32_t size) {
155+
static void handle_remote_notification(void *user, const uint8_t *value, uint32_t size) {
156156

157-
// No object registered for processing notifcations.
158-
if (self_obj == MP_OBJ_NULL) {
159-
return;
160-
}
161-
162-
pb_lwp3device_obj_t *self = MP_OBJ_TO_PTR(self_obj);
163-
164-
// It's not for us.
165-
if (self->peripheral != peripheral) {
157+
pb_lwp3device_obj_t *self = user;
158+
if (!self) {
166159
return;
167160
}
168161

@@ -306,7 +299,7 @@ static pbio_error_t pb_lwp3device_connect_thread(pbio_os_state_t *state, mp_obj_
306299
PBIO_OS_ASYNC_BEGIN(state);
307300

308301
// Get available peripheral instance.
309-
pb_assert(pbdrv_bluetooth_peripheral_get_available(&self->peripheral));
302+
pb_assert(pbdrv_bluetooth_peripheral_get_available(&self->peripheral, self));
310303

311304
// Scan and connect with timeout.
312305
pb_assert(pbdrv_bluetooth_peripheral_scan_and_connect(&scan_config));
@@ -484,8 +477,10 @@ mp_obj_t pb_type_remote_button_pressed(mp_obj_t self_in) {
484477
#endif
485478
}
486479

487-
mp_obj_t pb_lwp3device_close(mp_obj_t self_in) {
488-
self_obj = MP_OBJ_NULL;
480+
static mp_obj_t pb_lwp3device_close(mp_obj_t self_in) {
481+
pb_lwp3device_obj_t *self = MP_OBJ_TO_PTR(self_in);
482+
// Disables notification handler from accessing allocated memory.
483+
pbdrv_bluetooth_peripheral_release(self->peripheral, self);
489484
return mp_const_none;
490485
}
491486
MP_DEFINE_CONST_FUN_OBJ_1(pb_lwp3device_close_obj, pb_lwp3device_close);
@@ -497,10 +492,6 @@ static mp_obj_t pb_type_pupdevices_Remote_make_new(const mp_obj_type_t *type, si
497492

498493
pb_module_tools_assert_blocking();
499494

500-
if (self_obj) {
501-
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Can use only one Remote"));
502-
}
503-
504495
pb_lwp3device_obj_t *self = mp_obj_malloc_with_finaliser(pb_lwp3device_obj_t, type);
505496
self_obj = MP_OBJ_FROM_PTR(self);
506497

@@ -557,6 +548,10 @@ static mp_obj_t pb_lwp3device_name(size_t n_args, const mp_obj_t *args) {
557548
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pb_lwp3device_name_obj, 1, 2, pb_lwp3device_name);
558549

559550
static mp_obj_t pb_lwp3device_disconnect(mp_obj_t self_in) {
551+
// Needed to release claim on allocated data so we can make a new
552+
// connection later.
553+
pb_lwp3device_close(self_in);
554+
560555
pb_assert(pbdrv_bluetooth_peripheral_disconnect());
561556
return wait_or_await_operation(self_in);
562557
}
@@ -588,21 +583,11 @@ MP_DEFINE_CONST_OBJ_TYPE(pb_type_pupdevices_Remote,
588583
/**
589584
* Handles LEGO Wireless protocol messages from generic LWP3 devices.
590585
*/
591-
static void handle_lwp3_generic_notification(pbdrv_bluetooth_peripheral_t *peripheral, const uint8_t *value, uint32_t size) {
592-
593-
// No object registered for processing notifcations.
594-
if (self_obj == MP_OBJ_NULL) {
595-
return;
596-
}
586+
static void handle_lwp3_generic_notification(void *user, const uint8_t *value, uint32_t size) {
597587

598-
pb_lwp3device_obj_t *self = MP_OBJ_TO_PTR(self_obj);
588+
pb_lwp3device_obj_t *self = user;
599589

600-
// It's not for us.
601-
if (self->peripheral != peripheral) {
602-
return;
603-
}
604-
605-
if (!self->noti_num) {
590+
if (!self || !self->noti_num) {
606591
// Allocated data not ready.
607592
return;
608593
}
@@ -622,7 +607,6 @@ static void handle_lwp3_generic_notification(pbdrv_bluetooth_peripheral_t *perip
622607
return;
623608
}
624609

625-
626610
static mp_obj_t pb_type_iodevices_LWP3Device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
627611
PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
628612
PB_ARG_REQUIRED(hub_kind),
@@ -631,10 +615,6 @@ static mp_obj_t pb_type_iodevices_LWP3Device_make_new(const mp_obj_type_t *type,
631615
PB_ARG_DEFAULT_FALSE(pair),
632616
PB_ARG_DEFAULT_INT(num_notifications, 8));
633617

634-
if (self_obj) {
635-
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Can use only one LWP3Device"));
636-
}
637-
638618
uint8_t hub_kind = pb_obj_get_positive_int(hub_kind_in);
639619
bool pair = mp_obj_is_true(pair_in);
640620

pybricks/iodevices/pb_type_iodevices_xbox_controller.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ typedef struct {
8282
static pb_xbox_t pb_xbox_singleton;
8383

8484
// Handles LEGO Wireless protocol messages from the XBOX Device.
85-
static void handle_notification(pbdrv_bluetooth_peripheral_t *peripheral, const uint8_t *value, uint32_t size) {
85+
static void handle_notification(void *user, const uint8_t *value, uint32_t size) {
8686
pb_xbox_t *xbox = &pb_xbox_singleton;
8787
if (size <= sizeof(xbox_input_map_t)) {
8888
memcpy(&xbox->state, &value[0], size);

0 commit comments

Comments
 (0)