From 2397b44062899cfa6fc39d5f57976c4ec62c3cf8 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 24 Apr 2019 10:24:24 +1000 Subject: [PATCH] stm32/usbd_cdc_interface: Add CTS flow control option for USB VCP. Enabled by default, but disabled when REPL is connected to the VCP (this is the existing behaviour). Can be configured at run-time with, eg: pyb.USB_VCP().init(flow=pyb.USB_VCP.RTS | pyb.USB_VCP.CTS) --- ports/stm32/usb.c | 24 +++++++++++++----------- ports/stm32/usbd_cdc_interface.c | 19 +++++++++++++++++++ ports/stm32/usbd_cdc_interface.h | 2 ++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index ca5cc682e..f2f3d7458 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -634,6 +634,12 @@ STATIC void pyb_usb_vcp_print(const mp_print_t *print, mp_obj_t self_in, mp_prin void usb_vcp_attach_to_repl(const pyb_usb_vcp_obj_t *self, bool attached) { self->cdc_itf->attached_to_repl = attached; + if (attached) { + // Default behavior is non-blocking when attached to repl + self->cdc_itf->flow &= ~USBD_CDC_FLOWCONTROL_CTS; + } else { + self->cdc_itf->flow |= USBD_CDC_FLOWCONTROL_CTS; + } } /// \classmethod \constructor() @@ -791,6 +797,7 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = { // class constants { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) }, + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_CTS) }, }; STATIC MP_DEFINE_CONST_DICT(pyb_usb_vcp_locals_dict, pyb_usb_vcp_locals_dict_table); @@ -808,18 +815,13 @@ STATIC mp_uint_t pyb_usb_vcp_read(mp_obj_t self_in, void *buf, mp_uint_t size, i STATIC mp_uint_t pyb_usb_vcp_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->cdc_itf->attached_to_repl) { - usbd_cdc_tx_always(self->cdc_itf, (const byte*)buf, size); - return size; - } else { - int ret = usbd_cdc_tx(self->cdc_itf, (const byte*)buf, size, 0); - if (ret == 0) { - // return EAGAIN error to indicate non-blocking - *errcode = MP_EAGAIN; - return MP_STREAM_ERROR; - } - return ret; + int ret = usbd_cdc_tx_flow(self->cdc_itf, (const byte*)buf, size); + if (ret == 0) { + // return EAGAIN error to indicate non-blocking + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; } + return ret; } STATIC mp_uint_t pyb_usb_vcp_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index 51d29b30f..65f60e37c 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -74,6 +74,12 @@ uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) { cdc->rx_buf_full = false; cdc->tx_need_empty_packet = 0; cdc->connect_state = USBD_CDC_CONNECT_STATE_DISCONNECTED; + if (cdc->attached_to_repl) { + // Default behavior is non-blocking when attached to repl + cdc->flow &= ~USBD_CDC_FLOWCONTROL_CTS; + } else { + cdc->flow |= USBD_CDC_FLOWCONTROL_CTS; + } // Return the buffer to place the first USB OUT packet return cdc->rx_packet_buf; @@ -292,6 +298,19 @@ int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2; } +// Writes only the data that fits if flow & CTS, else writes all data +// Returns number of bytes actually written to the device +int usbd_cdc_tx_flow(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { + if (cdc->flow & USBD_CDC_FLOWCONTROL_CTS) { + // Only write as much as can fit in tx buffer + return usbd_cdc_tx(cdc, buf, len, 0); + } else { + // Never block, keep most recent data in rolling buffer + usbd_cdc_tx_always(cdc, buf, len); + return len; + } +} + // timout in milliseconds. // Returns number of bytes written to the device. int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t timeout) { diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index a48516b1f..1847b3a6b 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -46,6 +46,7 @@ // Flow control settings #define USBD_CDC_FLOWCONTROL_NONE (0) #define USBD_CDC_FLOWCONTROL_RTS (1) +#define USBD_CDC_FLOWCONTROL_CTS (2) typedef struct _usbd_cdc_itf_t { usbd_cdc_state_t base; // state for the base CDC layer @@ -75,6 +76,7 @@ static inline int usbd_cdc_is_connected(usbd_cdc_itf_t *cdc) { } int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc); +int usbd_cdc_tx_flow(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len); int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t timeout); void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len);