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)
This commit is contained in:
Andrew Leech 2019-04-24 10:24:24 +10:00 committed by Damien George
parent 6b4666f8cf
commit 2397b44062
3 changed files with 34 additions and 11 deletions

View File

@ -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) { void usb_vcp_attach_to_repl(const pyb_usb_vcp_obj_t *self, bool attached) {
self->cdc_itf->attached_to_repl = 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() /// \classmethod \constructor()
@ -791,6 +797,7 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
// class constants // class constants
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) }, { 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); 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) { 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); pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->cdc_itf->attached_to_repl) { int ret = usbd_cdc_tx_flow(self->cdc_itf, (const byte*)buf, size);
usbd_cdc_tx_always(self->cdc_itf, (const byte*)buf, size); if (ret == 0) {
return size; // return EAGAIN error to indicate non-blocking
} else { *errcode = MP_EAGAIN;
int ret = usbd_cdc_tx(self->cdc_itf, (const byte*)buf, size, 0); return MP_STREAM_ERROR;
if (ret == 0) {
// return EAGAIN error to indicate non-blocking
*errcode = MP_EAGAIN;
return MP_STREAM_ERROR;
}
return ret;
} }
return ret;
} }
STATIC mp_uint_t pyb_usb_vcp_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { STATIC mp_uint_t pyb_usb_vcp_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {

View File

@ -74,6 +74,12 @@ uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) {
cdc->rx_buf_full = false; cdc->rx_buf_full = false;
cdc->tx_need_empty_packet = 0; cdc->tx_need_empty_packet = 0;
cdc->connect_state = USBD_CDC_CONNECT_STATE_DISCONNECTED; 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 the buffer to place the first USB OUT packet
return cdc->rx_packet_buf; 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; 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. // timout in milliseconds.
// Returns number of bytes written to the device. // 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) { int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t timeout) {

View File

@ -46,6 +46,7 @@
// Flow control settings // Flow control settings
#define USBD_CDC_FLOWCONTROL_NONE (0) #define USBD_CDC_FLOWCONTROL_NONE (0)
#define USBD_CDC_FLOWCONTROL_RTS (1) #define USBD_CDC_FLOWCONTROL_RTS (1)
#define USBD_CDC_FLOWCONTROL_CTS (2)
typedef struct _usbd_cdc_itf_t { typedef struct _usbd_cdc_itf_t {
usbd_cdc_state_t base; // state for the base CDC layer 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_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); 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); void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len);