Skip to content

Commit 28cfd8a

Browse files
committed
CharacteristicBuffer: make it be a stream class; add locking
1 parent 7a33e58 commit 28cfd8a

File tree

13 files changed

+371
-66
lines changed

13 files changed

+371
-66
lines changed

ports/nrf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ SRC_C += \
142142
peripherals/nrf/$(MCU_CHIP)/pins.c \
143143
peripherals/nrf/$(MCU_CHIP)/power.c \
144144
peripherals/nrf/timers.c \
145+
sd_mutex.c \
145146
supervisor/shared/memory.c
146147

147148

ports/nrf/common-hal/bleio/Characteristic.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636
#include "common-hal/bleio/Characteristic.h"
3737
#include "shared-module/bleio/Characteristic.h"
3838

39-
// TODO - should these be per object?? *****
4039
STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
4140
STATIC volatile uint8_t m_tx_in_progress;
41+
// Serialize gattc writes that send a response. This might be done per object?
4242
STATIC nrf_mutex_t *m_write_mutex;
4343

4444
STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {

ports/nrf/common-hal/bleio/CharacteristicBuffer.c

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* The MIT License (MIT)
55
*
6-
* Copyright (c) 2018 Artur Pacholec
6+
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
77
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
@@ -29,9 +29,13 @@
2929

3030
#include "ble_drv.h"
3131
#include "ble_gatts.h"
32-
#include "nrf_soc.h"
32+
#include "sd_mutex.h"
3333

34+
#include "lib/utils/interrupt_char.h"
3435
#include "py/runtime.h"
36+
#include "py/stream.h"
37+
38+
#include "tick.h"
3539

3640
#include "common-hal/bleio/__init__.h"
3741
#include "common-hal/bleio/CharacteristicBuffer.h"
@@ -43,33 +47,89 @@ STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
4347
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
4448
// Event handle must match the handle for my characteristic.
4549
if (evt_write->handle == self->characteristic->handle) {
46-
// Push all the data onto the ring buffer.
50+
// Push all the data onto the ring buffer, but wait for any reads to finish.
51+
sd_mutex_acquire_wait_no_vm(&self->ringbuf_mutex);
4752
for (size_t i = 0; i < evt_write->len; i++) {
4853
ringbuf_put(&self->ringbuf, evt_write->data[i]);
4954
}
55+
// Don't check for errors: we're in an event handler.
56+
sd_mutex_release(&self->ringbuf_mutex);
5057
break;
5158
}
5259
}
5360
}
5461

5562
}
5663

57-
// Assumes that buffer_size has been validated before call.
58-
void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size) {
64+
// Assumes that timeout and buffer_size have been validated before call.
65+
void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self,
66+
bleio_characteristic_obj_t *characteristic,
67+
mp_float_t timeout,
68+
size_t buffer_size) {
5969

6070
self->characteristic = characteristic;
71+
self->timeout_ms = timeout * 1000;
6172
// This is a macro.
62-
ringbuf_alloc(&self->ringbuf, buffer_size);
73+
// true means long-lived, so it won't be moved.
74+
ringbuf_alloc(&self->ringbuf, buffer_size, true);
75+
sd_mutex_new(&self->ringbuf_mutex);
6376

6477
ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self);
6578

6679
}
6780

68-
// Returns a uint8_t byte value, or -1 if no data is available.
69-
int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self) {
70-
return ringbuf_get(&self->ringbuf);
81+
int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) {
82+
uint64_t start_ticks = ticks_ms;
83+
84+
// Wait for all bytes received or timeout
85+
while ( (ringbuf_count(&self->ringbuf) < len) && (ticks_ms - start_ticks < self->timeout_ms) ) {
86+
#ifdef MICROPY_VM_HOOK_LOOP
87+
MICROPY_VM_HOOK_LOOP ;
88+
// Allow user to break out of a timeout with a KeyboardInterrupt.
89+
if ( mp_hal_is_interrupted() ) {
90+
return 0;
91+
}
92+
#endif
93+
}
94+
95+
// Copy received data. Lock out writes while copying.
96+
sd_mutex_acquire_wait(&self->ringbuf_mutex);
97+
98+
size_t rx_bytes = MIN(ringbuf_count(&self->ringbuf), len);
99+
for ( size_t i = 0; i < rx_bytes; i++ ) {
100+
data[i] = ringbuf_get(&self->ringbuf);
101+
}
102+
103+
// Writes now OK.
104+
sd_mutex_release_check(&self->ringbuf_mutex);
105+
106+
return rx_bytes;
107+
}
108+
109+
uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
110+
return ringbuf_count(&self->ringbuf);
111+
}
112+
113+
void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
114+
// prevent conflict with uart irq
115+
sd_mutex_acquire_wait(&self->ringbuf_mutex);
116+
ringbuf_clear(&self->ringbuf);
117+
sd_mutex_release_check(&self->ringbuf_mutex);
118+
}
119+
120+
bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) {
121+
return self->characteristic == NULL;
71122
}
72123

73124
void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) {
74-
ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self);
125+
if (!common_hal_bleio_characteristic_buffer_deinited(self)) {
126+
ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self);
127+
}
128+
}
129+
130+
bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) {
131+
return self->characteristic != NULL &&
132+
self->characteristic->service != NULL &&
133+
self->characteristic->service->device != NULL &&
134+
common_hal_bleio_device_get_conn_handle(self->characteristic->service->device) != BLE_CONN_HANDLE_INVALID;
75135
}

ports/nrf/common-hal/bleio/CharacteristicBuffer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@
2727
#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H
2828
#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H
2929

30-
#include "py/ringbuf.h"
30+
#include "nrf_soc.h"
3131

32+
#include "py/ringbuf.h"
3233
#include "shared-bindings/bleio/Characteristic.h"
3334

3435
typedef struct {
3536
mp_obj_base_t base;
3637
bleio_characteristic_obj_t *characteristic;
38+
uint32_t timeout_ms;
3739
// Ring buffer storing consecutive incoming values.
3840
ringbuf_t ringbuf;
41+
nrf_mutex_t ringbuf_mutex;
3942
} bleio_characteristic_buffer_obj_t;
4043

4144
#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H

ports/nrf/common-hal/bleio/Device.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,13 @@ STATIC bool discover_services(bleio_device_obj_t *device, uint16_t start_handle)
262262
mp_raise_OSError_msg(translate("Failed to discover services"));
263263
}
264264

265+
// Serialize discovery.
265266
err_code = sd_mutex_acquire(m_discovery_mutex);
266267
if (err_code != NRF_SUCCESS) {
267268
mp_raise_OSError_msg(translate("Failed to acquire mutex"));
268269
}
269270

271+
// Wait for someone else to release m_discovery_mutex.
270272
while (sd_mutex_acquire(m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
271273
#ifdef MICROPY_VM_HOOK_LOOP
272274
MICROPY_VM_HOOK_LOOP

ports/nrf/common-hal/busio/UART.c

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,33 +52,6 @@
5252

5353
static uint32_t get_nrf_baud (uint32_t baudrate);
5454

55-
static uint16_t ringbuf_count(ringbuf_t *r)
56-
{
57-
volatile int count = r->iput - r->iget;
58-
if ( count < 0 ) {
59-
count += r->size;
60-
}
61-
62-
return (uint16_t) count;
63-
}
64-
65-
static void ringbuf_clear(ringbuf_t *r)
66-
{
67-
r->iput = r->iget = 0;
68-
}
69-
70-
// will overwrite old data
71-
static void ringbuf_put_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
72-
{
73-
for(uint8_t i=0; i < bufsize; i++) {
74-
if ( ringbuf_put(r, buf[i]) < 0 ) {
75-
// if full overwrite old data
76-
(void) ringbuf_get(r);
77-
ringbuf_put(r, buf[i]);
78-
}
79-
}
80-
}
81-
8255
static void uart_callback_irq (const nrfx_uarte_event_t * event, void * context) {
8356
busio_uart_obj_t* self = (busio_uart_obj_t*) context;
8457

@@ -145,16 +118,20 @@ void common_hal_busio_uart_construct (busio_uart_obj_t *self,
145118

146119
// Init buffer for rx
147120
if ( rx != mp_const_none ) {
148-
self->rbuf.buf = (uint8_t *) gc_alloc(receiver_buffer_size, false, false);
121+
// Initially allocate the UART's buffer in the long-lived part of the
122+
// heap. UARTs are generally long-lived objects, but the "make long-
123+
// lived" machinery is incapable of moving internal pointers like
124+
// self->buffer, so do it manually. (However, as long as internal
125+
// pointers like this are NOT moved, allocating the buffer
126+
// in the long-lived pool is not strictly necessary)
127+
// (This is a macro.)
128+
ringbuf_alloc(&self->rbuf, receiver_buffer_size, true);
149129

150130
if ( !self->rbuf.buf ) {
151131
nrfx_uarte_uninit(&self->uarte);
152132
mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer"));
153133
}
154134

155-
self->rbuf.size = receiver_buffer_size;
156-
self->rbuf.iget = self->rbuf.iput = 0;
157-
158135
self->rx_pin_number = rx->number;
159136
claim_pin(rx);
160137
}

ports/nrf/sd_mutex.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/mpconfig.h"
28+
#include "py/runtime.h"
29+
#include "nrf_soc.h"
30+
31+
void sd_mutex_acquire_check(nrf_mutex_t* p_mutex) {
32+
uint32_t err_code = sd_mutex_acquire(p_mutex);
33+
if (err_code != NRF_SUCCESS) {
34+
mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code);
35+
}
36+
}
37+
38+
void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex) {
39+
while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
40+
#ifdef MICROPY_VM_HOOK_LOOP
41+
MICROPY_VM_HOOK_LOOP
42+
#endif
43+
}
44+
}
45+
46+
void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex) {
47+
while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
48+
}
49+
}
50+
51+
void sd_mutex_release_check(nrf_mutex_t* p_mutex) {
52+
uint32_t err_code = sd_mutex_release(p_mutex);
53+
if (err_code != NRF_SUCCESS) {
54+
mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code);
55+
}
56+
}

ports/nrf/sd_mutex.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#ifndef MICROPY_INCLUDED_NRF_SD_MUTEX_H
28+
#define MICROPY_INCLUDED_NRF_SD_MUTEX_H
29+
30+
#include "nrf_soc.h"
31+
32+
// Helpers for common usage of nrf_mutex.
33+
34+
// Try to acquire a mutex right now. Raise exception if we can't get it.
35+
void sd_mutex_acquire_check(nrf_mutex_t* p_mutex);
36+
37+
// Wait for a mutex to become available. Run VM background tasks while waiting.
38+
void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex);
39+
40+
// Wait for a mutex to become available.. Block VM while waiting.
41+
void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex);
42+
43+
// Release a mutex, and raise exception on error.
44+
void sd_mutex_release_check(nrf_mutex_t* p_mutex);
45+
46+
#endif // MICROPY_INCLUDED_NRF_SD_MUTEX_H

py/ringbuf.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#ifndef MICROPY_INCLUDED_PY_RINGBUF_H
2727
#define MICROPY_INCLUDED_PY_RINGBUF_H
2828

29+
#include "py/gc.h"
30+
2931
#include <stdint.h>
3032

3133
typedef struct _ringbuf_t {
@@ -40,9 +42,9 @@ typedef struct _ringbuf_t {
4042
// ringbuf_t buf = {buf_array, sizeof(buf_array)};
4143

4244
// Dynamic initialization. This creates root pointer!
43-
#define ringbuf_alloc(r, sz) \
45+
#define ringbuf_alloc(r, sz, long_lived) \
4446
{ \
45-
(r)->buf = m_new(uint8_t, sz); \
47+
(r)->buf = gc_alloc(sz, false, long_lived); \
4648
(r)->size = sz; \
4749
(r)->iget = (r)->iput = 0; \
4850
}
@@ -71,4 +73,30 @@ static inline int ringbuf_put(ringbuf_t *r, uint8_t v) {
7173
return 0;
7274
}
7375

76+
static inline uint16_t ringbuf_count(ringbuf_t *r)
77+
{
78+
volatile int count = r->iput - r->iget;
79+
if ( count < 0 ) {
80+
count += r->size;
81+
}
82+
83+
return (uint16_t) count;
84+
}
85+
86+
static inline void ringbuf_clear(ringbuf_t *r)
87+
{
88+
r->iput = r->iget = 0;
89+
}
90+
91+
// will overwrite old data
92+
static inline void ringbuf_put_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
93+
{
94+
for(uint8_t i=0; i < bufsize; i++) {
95+
if ( ringbuf_put(r, buf[i]) < 0 ) {
96+
// if full overwrite old data
97+
(void) ringbuf_get(r);
98+
ringbuf_put(r, buf[i]);
99+
}
100+
}
101+
}
74102
#endif // MICROPY_INCLUDED_PY_RINGBUF_H

py/stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl
103103
// CPython does a readall, but here we silently let negatives through,
104104
// and they will cause a MemoryError.
105105
mp_int_t sz;
106-
if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
106+
if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) {
107107
return stream_readall(args[0]);
108108
}
109109

0 commit comments

Comments
 (0)