Skip to content

Commit e152d0c

Browse files
jimmodpgeorge
authored andcommitted
extmod/btstack: Schedule notify/indicate/write ops for bg completion.
The goal of this commit is to allow using ble.gatts_notify() at any time, even if the stack is not ready to send the notification right now. It also addresses the same issue for ble.gatts_indicate() and ble.gattc_write() (without response). In addition this commit fixes the case where the buffer passed to write-with-response wasn't copied, meaning it could be modified by the caller, affecting the in-progress write. The changes are: - gatts_notify/indicate will now run in the background if the ACL buffer is currently full, meaning that notify/indicate can be called at any time. - gattc_write(mode=0) (no response) will now allow for one outstanding write. - gattc_write(mode=1) (with response) will now copy the buffer so that it can't be modified by the caller while the write is in progress. All four paths also now track the buffer while the operation is in progress, which prevents the GC free'ing the buffer while it's still needed.
1 parent 07aec46 commit e152d0c

File tree

7 files changed

+353
-60
lines changed

7 files changed

+353
-60
lines changed

extmod/btstack/modbluetooth_btstack.c

Lines changed: 261 additions & 23 deletions
Large diffs are not rendered by default.

extmod/btstack/modbluetooth_btstack.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
#include "lib/btstack/src/btstack.h"
3535

36+
typedef struct _mp_btstack_pending_op_t mp_btstack_pending_op_t;
37+
3638
typedef struct _mp_bluetooth_btstack_root_pointers_t {
3739
// This stores both the advertising data and the scan response data, concatenated together.
3840
uint8_t *adv_data;
@@ -45,6 +47,7 @@ typedef struct _mp_bluetooth_btstack_root_pointers_t {
4547
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
4648
// Registration for notify/indicate events.
4749
gatt_client_notification_t notification;
50+
btstack_linked_list_t pending_ops;
4851
#endif
4952
} mp_bluetooth_btstack_root_pointers_t;
5053

extmod/modbluetooth.c

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
321321
case MP_QSTR_gap_name: {
322322
mp_buffer_info_t bufinfo;
323323
mp_get_buffer_raise(e->value, &bufinfo, MP_BUFFER_READ);
324-
int ret = mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len);
325-
bluetooth_handle_errno(ret);
324+
bluetooth_handle_errno(mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len));
326325
break;
327326
}
328327
case MP_QSTR_rxbuf: {
@@ -663,17 +662,25 @@ STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args)
663662
if (n_args == 4) {
664663
mp_buffer_info_t bufinfo = {0};
665664
mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
666-
size_t len = bufinfo.len;
667-
int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, &len);
665+
int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, bufinfo.len);
668666
bluetooth_handle_errno(err);
669-
return MP_OBJ_NEW_SMALL_INT(len);
667+
return mp_const_none;
670668
} else {
671669
int err = mp_bluetooth_gatts_notify(conn_handle, value_handle);
672670
return bluetooth_handle_errno(err);
673671
}
674672
}
675673
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify);
676674

675+
STATIC mp_obj_t bluetooth_ble_gatts_indicate(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t value_handle_in) {
676+
mp_int_t conn_handle = mp_obj_get_int(conn_handle_in);
677+
mp_int_t value_handle = mp_obj_get_int(value_handle_in);
678+
679+
int err = mp_bluetooth_gatts_indicate(conn_handle, value_handle);
680+
return bluetooth_handle_errno(err);
681+
}
682+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_gatts_indicate_obj, bluetooth_ble_gatts_indicate);
683+
677684
STATIC mp_obj_t bluetooth_ble_gatts_set_buffer(size_t n_args, const mp_obj_t *args) {
678685
mp_int_t value_handle = mp_obj_get_int(args[1]);
679686
mp_int_t len = mp_obj_get_int(args[2]);
@@ -765,6 +772,7 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = {
765772
{ MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) },
766773
{ MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) },
767774
{ MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) },
775+
{ MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) },
768776
{ MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) },
769777
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
770778
// GATT Client (i.e. central/scanner role)
@@ -1147,47 +1155,58 @@ mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, ui
11471155
}
11481156

11491157
int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len) {
1158+
MICROPY_PY_BLUETOOTH_ENTER
11501159
mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle);
1151-
if (!entry) {
1152-
return MP_EINVAL;
1153-
}
1154-
1155-
*value = entry->data;
1156-
*value_len = entry->data_len;
1157-
if (entry->append) {
1158-
entry->data_len = 0;
1160+
if (entry) {
1161+
*value = entry->data;
1162+
*value_len = entry->data_len;
1163+
if (entry->append) {
1164+
entry->data_len = 0;
1165+
}
11591166
}
1160-
1161-
return 0;
1167+
MICROPY_PY_BLUETOOTH_EXIT
1168+
return entry ? 0 : MP_EINVAL;
11621169
}
11631170

11641171
int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len) {
1172+
MICROPY_PY_BLUETOOTH_ENTER
11651173
mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle);
1166-
if (!entry) {
1167-
return MP_EINVAL;
1168-
}
1174+
if (entry) {
1175+
if (value_len > entry->data_alloc) {
1176+
uint8_t *data = m_new_maybe(uint8_t, value_len);
1177+
if (data) {
1178+
entry->data = data;
1179+
entry->data_alloc = value_len;
1180+
} else {
1181+
MICROPY_PY_BLUETOOTH_EXIT
1182+
return MP_ENOMEM;
1183+
}
1184+
}
11691185

1170-
if (value_len > entry->data_alloc) {
1171-
entry->data = m_new(uint8_t, value_len);
1172-
entry->data_alloc = value_len;
1186+
memcpy(entry->data, value, value_len);
1187+
entry->data_len = value_len;
11731188
}
1174-
1175-
memcpy(entry->data, value, value_len);
1176-
entry->data_len = value_len;
1177-
1178-
return 0;
1189+
MICROPY_PY_BLUETOOTH_EXIT
1190+
return entry ? 0 : MP_EINVAL;
11791191
}
11801192

11811193
int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append) {
1194+
MICROPY_PY_BLUETOOTH_ENTER
11821195
mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle);
1183-
if (!entry) {
1184-
return MP_EINVAL;
1196+
if (entry) {
1197+
uint8_t *data = m_renew_maybe(uint8_t, entry->data, entry->data_alloc, len, true);
1198+
if (data) {
1199+
entry->data = data;
1200+
entry->data_alloc = len;
1201+
entry->data_len = 0;
1202+
entry->append = append;
1203+
} else {
1204+
MICROPY_PY_BLUETOOTH_EXIT
1205+
return MP_ENOMEM;
1206+
}
11851207
}
1186-
entry->data = m_renew(uint8_t, entry->data, entry->data_alloc, len);
1187-
entry->data_alloc = len;
1188-
entry->data_len = 0;
1189-
entry->append = append;
1190-
return 0;
1208+
MICROPY_PY_BLUETOOTH_EXIT
1209+
return entry ? 0 : MP_EINVAL;
11911210
}
11921211

11931212
#endif // MICROPY_PY_BLUETOOTH

extmod/modbluetooth.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
199199
// Notify the central that it should do a read.
200200
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle);
201201
// Notify the central, including a data payload. (Note: does not set the gatts db value).
202-
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len);
202+
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len);
203203
// Indicate the central.
204204
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle);
205205

extmod/nimble/modbluetooth_nimble.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,13 +587,13 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
587587
return ble_hs_err_to_errno(ble_gattc_notify(conn_handle, value_handle));
588588
}
589589

590-
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len) {
590+
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) {
591591
if (!mp_bluetooth_is_active()) {
592592
return ERRNO_BLUETOOTH_NOT_ACTIVE;
593593
}
594-
struct os_mbuf *om = ble_hs_mbuf_from_flat(value, *value_len);
594+
struct os_mbuf *om = ble_hs_mbuf_from_flat(value, value_len);
595595
if (om == NULL) {
596-
return -1;
596+
return MP_ENOMEM;
597597
}
598598
// TODO: check that notify_custom takes ownership of om, if not os_mbuf_free_chain(om).
599599
return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om));

tests/multi_bluetooth/ble_characteristic.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
_IRQ_GATTC_READ_DONE = const(16)
1717
_IRQ_GATTC_WRITE_DONE = const(17)
1818
_IRQ_GATTC_NOTIFY = const(18)
19+
_IRQ_GATTC_INDICATE = const(19)
1920

2021
SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A")
2122
CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444")
@@ -59,6 +60,8 @@ def irq(event, data):
5960
print("_IRQ_GATTC_WRITE_DONE", data[-1])
6061
elif event == _IRQ_GATTC_NOTIFY:
6162
print("_IRQ_GATTC_NOTIFY", data[-1])
63+
elif event == _IRQ_GATTC_INDICATE:
64+
print("_IRQ_GATTC_INDICATE", data[-1])
6265

6366
if waiting_event is not None:
6467
if (isinstance(waiting_event, int) and event == waiting_event) or (
@@ -115,6 +118,16 @@ def instance0():
115118
print("gatts_notify")
116119
ble.gatts_notify(conn_handle, char_handle, "periph2")
117120

121+
# Wait for a write to the characteristic from the central.
122+
wait_for_event(_IRQ_GATTS_WRITE, TIMEOUT_MS)
123+
124+
# Wait a bit, then notify a new value on the characteristic.
125+
time.sleep_ms(1000)
126+
print("gatts_write")
127+
ble.gatts_write(char_handle, "periph3")
128+
print("gatts_indicate")
129+
ble.gatts_indicate(conn_handle, char_handle)
130+
118131
# Wait for the central to disconnect.
119132
wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS)
120133
finally:
@@ -163,6 +176,17 @@ def instance1():
163176
ble.gattc_read(conn_handle, value_handle)
164177
wait_for_event(_IRQ_GATTC_READ_RESULT, TIMEOUT_MS)
165178

179+
# Write to the characteristic, and ask for a response.
180+
print("gattc_write")
181+
ble.gattc_write(conn_handle, value_handle, "central2", 1)
182+
wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS)
183+
184+
# Wait for a indicate (should have new data), then read new value.
185+
wait_for_event(_IRQ_GATTC_INDICATE, TIMEOUT_MS)
186+
print("gattc_read")
187+
ble.gattc_read(conn_handle, value_handle)
188+
wait_for_event(_IRQ_GATTC_READ_RESULT, TIMEOUT_MS)
189+
166190
# Disconnect from peripheral.
167191
print("gap_disconnect:", ble.gap_disconnect(conn_handle))
168192
wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS)

tests/multi_bluetooth/ble_characteristic.py.exp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ gatts_write
66
gatts_notify
77
_IRQ_GATTS_WRITE b'central1'
88
gatts_notify
9+
_IRQ_GATTS_WRITE b'central2'
10+
gatts_write
11+
gatts_indicate
912
_IRQ_CENTRAL_DISCONNECT
1013
--- instance1 ---
1114
gap_connect
@@ -26,5 +29,11 @@ _IRQ_GATTC_NOTIFY b'periph2'
2629
gattc_read
2730
_IRQ_GATTC_READ_RESULT b'central1'
2831
_IRQ_GATTC_READ_DONE 0
32+
gattc_write
33+
_IRQ_GATTC_WRITE_DONE 0
34+
_IRQ_GATTC_INDICATE b'periph3'
35+
gattc_read
36+
_IRQ_GATTC_READ_RESULT b'periph3'
37+
_IRQ_GATTC_READ_DONE 0
2938
gap_disconnect: True
3039
_IRQ_PERIPHERAL_DISCONNECT

0 commit comments

Comments
 (0)