Skip to content

Commit ea91926

Browse files
authored
Merge pull request #11578 from mikeysklar/uart-write-ctrl-c
Let Ctrl-C stop a UART write that is stuck on flow control
2 parents 9c78d1e + 25f1057 commit ea91926

6 files changed

Lines changed: 69 additions & 11 deletions

File tree

‎ports/espressif/common-hal/busio/UART.c‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,16 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data,
374374
left_to_write -= count;
375375
data += count;
376376
RUN_BACKGROUND_TASKS;
377+
// Allow user to break out of a write blocked by flow control.
378+
if (mp_hal_is_interrupted()) {
379+
break;
380+
}
377381
}
378-
while (uart_wait_tx_done(self->uart_num, 0) == ESP_ERR_TIMEOUT) {
382+
while (uart_wait_tx_done(self->uart_num, 0) == ESP_ERR_TIMEOUT && !mp_hal_is_interrupted()) {
379383
RUN_BACKGROUND_TASKS;
380384
}
381385

382-
return len;
386+
return len - left_to_write;
383387
}
384388

385389
uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {

‎ports/raspberrypi/common-hal/busio/UART.c‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,16 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data,
231231
left_to_write--;
232232
}
233233
RUN_BACKGROUND_TASKS;
234+
// Allow user to break out of a write blocked by flow control.
235+
if (mp_hal_is_interrupted()) {
236+
break;
237+
}
234238
}
235239
if (self->rs485_dir_pin != NO_PIN) {
236240
uart_tx_wait_blocking(self->uart);
237241
gpio_put(self->rs485_dir_pin, self->rs485_invert);
238242
}
239-
return len;
243+
return len - left_to_write;
240244
}
241245

242246
// Read characters.

‎ports/zephyr-cp/common-hal/busio/UART.c‎

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ static void resume_rx(busio_uart_obj_t *self) {
3636
}
3737

3838
/*
39-
* Read characters from UART until line end is detected. Afterwards push the
40-
* data to the message queue.
39+
* Feed pending TX bytes to the driver, then move received characters into
40+
* the message queue.
4141
*/
4242
static void serial_cb(const struct device *dev, void *user_data) {
4343
busio_uart_obj_t *self = (busio_uart_obj_t *)user_data;
@@ -46,6 +46,18 @@ static void serial_cb(const struct device *dev, void *user_data) {
4646

4747
uart_irq_update(dev);
4848

49+
if (self->tx_len > 0 && uart_irq_tx_ready(dev)) {
50+
int sent = uart_fifo_fill(dev, self->tx_data, self->tx_len);
51+
if (sent > 0) {
52+
self->tx_data += sent;
53+
self->tx_len -= sent;
54+
}
55+
if (self->tx_len == 0) {
56+
uart_irq_tx_disable(dev);
57+
k_sem_give(&self->tx_done);
58+
}
59+
}
60+
4961
if (!uart_irq_rx_ready(dev)) {
5062
return;
5163
}
@@ -102,6 +114,8 @@ mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, con
102114

103115
self->timeout = K_FOREVER;
104116
self->write_timeout = K_FOREVER;
117+
self->tx_len = 0;
118+
k_sem_init(&self->tx_done, 0, 1);
105119
self->rx_paused = false;
106120
self->flow_control = false;
107121
self->sigint_enabled = sigint_enabled;
@@ -233,13 +247,36 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t
233247
return count;
234248
}
235249

236-
// Write characters.
250+
// Write characters. serial_cb() feeds the driver from the TX interrupt, so
251+
// this thread only waits and can stop on Ctrl-C.
237252
size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
238-
for (int i = 0; i < len; i++) {
239-
uart_poll_out(self->uart_device, data[i]);
253+
if (len == 0) {
254+
return 0;
240255
}
256+
k_sem_reset(&self->tx_done);
257+
self->tx_data = data;
258+
self->tx_len = len;
259+
uart_irq_tx_enable(self->uart_device);
260+
while (k_sem_take(&self->tx_done, K_MSEC(10)) != 0) {
261+
RUN_BACKGROUND_TASKS;
262+
if (mp_hal_is_interrupted()) {
263+
break;
264+
}
265+
}
266+
uart_irq_tx_disable(self->uart_device);
267+
268+
unsigned int key = irq_lock();
269+
size_t not_sent = self->tx_len;
270+
self->tx_len = 0;
271+
irq_unlock(key);
272+
273+
return len - not_sent;
274+
}
241275

242-
return len;
276+
void zephyr_uart_write_blocking(busio_uart_obj_t *self, const uint8_t *data, size_t len) {
277+
for (size_t i = 0; i < len; i++) {
278+
uart_poll_out(self->uart_device, data[i]);
279+
}
243280
}
244281

245282
uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {

‎ports/zephyr-cp/common-hal/busio/UART.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ typedef struct {
2020

2121
k_timeout_t timeout;
2222
k_timeout_t write_timeout;
23+
// Bytes not yet handed to the driver by serial_cb(), and a semaphore it
24+
// gives when they are all sent.
25+
const uint8_t *tx_data;
26+
size_t tx_len;
27+
struct k_sem tx_done;
2328
byte *receiver_buffer;
2429
const mcu_pin_obj_t *tx;
2530
const mcu_pin_obj_t *rx;
@@ -45,6 +50,13 @@ mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, con
4550
// Internal helper for clearing buffer
4651
void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self);
4752

53+
// Write all of data with uart_poll_out(). Used by the console and usb_cdc
54+
// instead of common_hal_busio_uart_write(), which stops early on a pending
55+
// Ctrl-C or reload and would cut console output short. With the host port
56+
// closed, the CDC ACM driver drops this output instead of blocking, as on
57+
// TinyUSB ports.
58+
void zephyr_uart_write_blocking(busio_uart_obj_t *self, const uint8_t *data, size_t len);
59+
4860
// Zephyr-port-specific write-timeout accessors, used by usb_cdc/Serial.c.
4961
mp_float_t common_hal_busio_uart_get_write_timeout(busio_uart_obj_t *self);
5062
void common_hal_busio_uart_set_write_timeout(busio_uart_obj_t *self, mp_float_t write_timeout);

‎ports/zephyr-cp/common-hal/usb_cdc/Serial.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data,
2222
}
2323

2424
size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
25-
return common_hal_busio_uart_write(self, data, len, errcode);
25+
zephyr_uart_write_blocking(self, data, len);
26+
return len;
2627
}
2728

2829
uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) {

‎ports/zephyr-cp/supervisor/serial.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,6 @@ void port_serial_write_substring(const char *text, uint32_t length) {
8080
}
8181
common_hal_usb_cdc_serial_write(usb_console, text, length, NULL);
8282
#else
83-
common_hal_busio_uart_write(&uart_console, text, length, NULL);
83+
zephyr_uart_write_blocking(&uart_console, (const uint8_t *)text, length);
8484
#endif
8585
}

0 commit comments

Comments
 (0)