Skip to content

Commit 7e32dae

Browse files
committed
async_spi: report Zephyr transfer errors, wait without background tasks
The driver's result reaches the await as an exception. cancel() and deinit() wait for the transfer without running background tasks, since they may run from a finalizer during GC. async_spi needs asyncio, not only async/await, so raspberrypi enables it with MICROPY_PY_ASYNCIO.
1 parent 8bbdf9b commit 7e32dae

3 files changed

Lines changed: 16 additions & 6 deletions

File tree

‎ports/raspberrypi/mpconfigport.mk‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS = 0
77

88
CIRCUITPY_RP2PIO ?= 1
99
CIRCUITPY_BUSIO_SPI_ASYNC ?= 1
10-
CIRCUITPY_ASYNC_SPI ?= $(call enable-if-all,$(MICROPY_PY_ASYNC_AWAIT) $(CIRCUITPY_BUSIO_SPI) $(CIRCUITPY_BUSIO_SPI_ASYNC))
10+
CIRCUITPY_ASYNC_SPI ?= $(call enable-if-all,$(MICROPY_PY_ASYNCIO) $(CIRCUITPY_BUSIO_SPI) $(CIRCUITPY_BUSIO_SPI_ASYNC))
1111
# The 24 KB stack has room for a larger display refresh buffer.
1212
CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE ?= 2048
1313
CIRCUITPY_NEOPIXEL_WRITE ?= $(CIRCUITPY_RP2PIO)

‎ports/zephyr-cp/common-hal/async_spi/SPI.c‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ static void apply_config(async_spi_spi_obj_t *self, uint32_t frequency, uint16_t
2828

2929
static void transfer_done(const struct device *dev, int result, void *userdata) {
3030
ARG_UNUSED(dev);
31-
ARG_UNUSED(result);
3231
async_spi_spi_obj_t *self = userdata;
32+
self->result = result;
3333
CIRCUITPY_ASYNC_FLAG_SET(self->done);
3434
// A configure() that waited for this transfer.
3535
if (self->idle_done != NULL) {
@@ -40,13 +40,14 @@ static void transfer_done(const struct device *dev, int result, void *userdata)
4040
port_wake_main_task_from_isr();
4141
}
4242

43-
// Zephyr cannot stop a transfer, so this waits for it; deinit() waits too. Does not allocate.
43+
// Zephyr cannot stop a transfer, so cancel and deinit wait for it. No background tasks: this
44+
// also runs from a finalizer during GC.
4445
static void wait_done(async_spi_spi_obj_t *self) {
4546
if (self->done == NULL) {
4647
return;
4748
}
4849
while (!CIRCUITPY_ASYNC_FLAG_IS_SET(self->done)) {
49-
RUN_BACKGROUND_TASKS;
50+
port_task_sleep_ms(1);
5051
}
5152
self->done = NULL;
5253
}
@@ -154,6 +155,7 @@ static void start(async_spi_spi_obj_t *self, const uint8_t *data_out, uint8_t *d
154155
wait_done(self);
155156
CIRCUITPY_ASYNC_FLAG_INIT(done);
156157
self->done = done;
158+
self->result = 0;
157159
if (len == 0) {
158160
CIRCUITPY_ASYNC_FLAG_SET(done);
159161
return;
@@ -203,14 +205,21 @@ void common_hal_async_spi_spi_write_readinto_start(async_spi_spi_obj_t *self,
203205
start(self, data_out, data_in, len, done);
204206
}
205207

208+
// Called once done is set.
206209
mp_obj_t common_hal_async_spi_spi_transfer_end(void *context, circuitpy_async_flag_t *done) {
207210
async_spi_spi_obj_t *self = context;
208211
if (self->done == done) {
209-
wait_done(self);
212+
self->done = NULL;
213+
if (self->result != 0) {
214+
raise_zephyr_error(self->result);
215+
}
210216
}
211217
return mp_const_none;
212218
}
213219

214220
void common_hal_async_spi_spi_transfer_cancel(void *context, circuitpy_async_flag_t *done) {
215-
common_hal_async_spi_spi_transfer_end(context, done);
221+
async_spi_spi_obj_t *self = context;
222+
if (self->done == done) {
223+
wait_done(self);
224+
}
216225
}

‎ports/zephyr-cp/common-hal/async_spi/SPI.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct {
2323
const mcu_pin_obj_t *mosi;
2424
const mcu_pin_obj_t *miso;
2525
circuitpy_async_flag_t *done; // of the running transfer, NULL when none
26+
int result; // of the last transfer, from the driver
2627
struct spi_buf tx_buf; // the running transfer's buffers
2728
struct spi_buf rx_buf;
2829
struct spi_buf_set tx;

0 commit comments

Comments
 (0)