extmod/modbluetooth: Change module-owned bytes objects to memoryview.

A read-only memoryview object is a better representation of the data, which
is owned by the ubluetooth module and may change between calls to the
user's irq callback function.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2020-09-23 23:18:16 +10:00
parent 50e34f979c
commit 81f2162ca0
5 changed files with 31 additions and 27 deletions

View File

@ -88,12 +88,22 @@ Event Handling
arguments, ``event`` (which will be one of the codes below) and ``data``
(which is an event-specific tuple of values).
**Note:** the ``addr``, ``adv_data``, ``char_data``, ``notify_data``, and
``uuid`` entries in the tuples are references to data managed by the
:mod:`ubluetooth` module (i.e. the same instance will be re-used across
multiple calls to the event handler). If your program wants to use this
data outside of the handler, then it must copy them first, e.g. by using
``bytes(addr)`` or ``bluetooth.UUID(uuid)``.
**Note:** As an optimisation to prevent unnecessary allocations, the ``addr``,
``adv_data``, ``char_data``, ``notify_data``, and ``uuid`` entries in the
tuples are read-only memoryview instances pointing to ubluetooth's internal
ringbuffer, and are only valid during the invocation of the IRQ handler
function. If your program needs to save one of these values to access after
the IRQ handler has returned (e.g. by saving it in a class instance or global
variable), then it needs to take a copy of the data, either by using ``bytes()``
or ``bluetooth.UUID()``, like this::
connected_addr = bytes(addr) # equivalently: adv_data, char_data, or notify_data
matched_uuid = bluetooth.UUID(uuid)
For example, the IRQ handler for a scan result might inspect the ``adv_data``
to decide if it's the correct device, and only then copy the address data to be
used elsewhere in the program. And to print data from within the IRQ handler,
``print(bytes(addr))`` will be needed.
An event handler showing all possible events::

View File

@ -32,7 +32,6 @@
#include "py/mphal.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "py/objstr.h"
#include "py/qstr.h"
#include "py/runtime.h"
#include "extmod/modbluetooth.h"
@ -62,9 +61,8 @@ typedef struct {
mp_obj_t irq_data_tuple;
uint8_t irq_data_addr_bytes[6];
uint16_t irq_data_data_alloc;
uint8_t *irq_data_data_bytes;
mp_obj_str_t irq_data_addr;
mp_obj_str_t irq_data_data;
mp_obj_array_t irq_data_addr;
mp_obj_array_t irq_data_data;
mp_obj_bluetooth_uuid_t irq_data_uuid;
ringbuf_t ringbuf;
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
@ -262,11 +260,9 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
#endif
// Pre-allocated buffers for address, payload and uuid.
o->irq_data_addr.base.type = &mp_type_bytes;
o->irq_data_addr.data = o->irq_data_addr_bytes;
mp_obj_memoryview_init(&o->irq_data_addr, 'B', 0, 0, o->irq_data_addr_bytes);
o->irq_data_data_alloc = MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN(MICROPY_PY_BLUETOOTH_RINGBUF_SIZE);
o->irq_data_data.base.type = &mp_type_bytes;
o->irq_data_data.data = m_new(uint8_t, o->irq_data_data_alloc);
mp_obj_memoryview_init(&o->irq_data_data, 'B', 0, 0, m_new(uint8_t, o->irq_data_data_alloc));
o->irq_data_uuid.base.type = &bluetooth_uuid_type;
// Allocate the default ringbuf.
@ -352,7 +348,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
// Get old buffer sizes and pointers
uint8_t *old_ringbuf_buf = self->ringbuf.buf;
size_t old_ringbuf_alloc = self->ringbuf.size;
uint8_t *old_irq_data_buf = (uint8_t *)self->irq_data_data.data;
uint8_t *old_irq_data_buf = (uint8_t *)self->irq_data_data.items;
size_t old_irq_data_alloc = self->irq_data_data_alloc;
// Atomically update the ringbuf and irq data
@ -362,7 +358,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
self->ringbuf.iget = 0;
self->ringbuf.iput = 0;
self->irq_data_data_alloc = irq_data_alloc;
self->irq_data_data.data = irq_data;
self->irq_data_data.items = irq_data;
MICROPY_PY_BLUETOOTH_EXIT
// Free old buffers
@ -850,7 +846,7 @@ const mp_obj_module_t mp_module_ubluetooth = {
#include <stdio.h>
STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size_t n_u16, size_t n_u8, mp_obj_str_t *bytes_addr, size_t n_i8, mp_obj_bluetooth_uuid_t *uuid, mp_obj_str_t *bytes_data) {
STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size_t n_u16, size_t n_u8, mp_obj_array_t *bytes_addr, size_t n_i8, mp_obj_bluetooth_uuid_t *uuid, mp_obj_array_t *bytes_data) {
assert(ringbuf_avail(ringbuf) >= n_u16 * 2 + n_u8 + (bytes_addr ? 6 : 0) + n_i8 + (uuid ? 1 : 0) + (bytes_data ? 1 : 0));
size_t j = 0;
@ -863,8 +859,7 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size
if (bytes_addr) {
bytes_addr->len = 6;
for (size_t i = 0; i < bytes_addr->len; ++i) {
// cast away const, this is actually bt->irq_addr_bytes.
((uint8_t *)bytes_addr->data)[i] = ringbuf_get(ringbuf);
((uint8_t *)bytes_addr->items)[i] = ringbuf_get(ringbuf);
}
data_tuple->items[j++] = MP_OBJ_FROM_PTR(bytes_addr);
}
@ -880,12 +875,11 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size
#endif
// The code that enqueues into the ringbuf should ensure that it doesn't
// put more than bt->irq_data_data_alloc bytes into the ringbuf, because
// that's what's available here in bt->irq_data_bytes.
// that's what's available here.
if (bytes_data) {
bytes_data->len = ringbuf_get16(ringbuf);
for (size_t i = 0; i < bytes_data->len; ++i) {
// cast away const, this is actually bt->irq_data_bytes.
((uint8_t *)bytes_data->data)[i] = ringbuf_get(ringbuf);
((uint8_t *)bytes_data->items)[i] = ringbuf_get(ringbuf);
}
data_tuple->items[j++] = MP_OBJ_FROM_PTR(bytes_data);
}

View File

@ -54,15 +54,15 @@ def irq(event, data):
print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1])
value_handle = data[2]
elif event == _IRQ_GATTC_READ_RESULT:
print("_IRQ_GATTC_READ_RESULT", data[-1])
print("_IRQ_GATTC_READ_RESULT", bytes(data[-1]))
elif event == _IRQ_GATTC_READ_DONE:
print("_IRQ_GATTC_READ_DONE", data[-1])
elif event == _IRQ_GATTC_WRITE_DONE:
print("_IRQ_GATTC_WRITE_DONE", data[-1])
elif event == _IRQ_GATTC_NOTIFY:
print("_IRQ_GATTC_NOTIFY", data[-1])
print("_IRQ_GATTC_NOTIFY", bytes(data[-1]))
elif event == _IRQ_GATTC_INDICATE:
print("_IRQ_GATTC_INDICATE", data[-1])
print("_IRQ_GATTC_INDICATE", bytes(data[-1]))
elif event == _IRQ_GATTS_INDICATE_DONE:
print("_IRQ_GATTS_INDICATE_DONE", data[-1])

View File

@ -36,7 +36,7 @@ def irq(event, data):
print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1])
value_handle = data[2]
elif event == _IRQ_GATTC_READ_RESULT:
print("_IRQ_GATTC_READ_RESULT", data[-1])
print("_IRQ_GATTC_READ_RESULT", bytes(data[-1]))
if waiting_event is not None:
if isinstance(waiting_event, int) and event == waiting_event:

View File

@ -67,7 +67,7 @@ def irq(event, data):
elif event == _IRQ_GATTC_WRITE_DONE:
print("_IRQ_GATTC_WRITE_DONE", data[-1])
elif event == _IRQ_GATTC_NOTIFY:
print("_IRQ_GATTC_NOTIFY", data[-1])
print("_IRQ_GATTC_NOTIFY", bytes(data[-1]))
if waiting_event is not None:
if (isinstance(waiting_event, int) and event == waiting_event) or (