stm32/usb: Add flow control option for USB VCP data received from host.

It's off by default and can be enabled at run-time with:

    pyb.USB_VCP().init(flow=pyb.USB_VCP.RTS)
This commit is contained in:
Andrew Leech 2018-12-14 17:07:12 +11:00 committed by Damien George
parent 63eae33b7b
commit 67689bfd7e
4 changed files with 65 additions and 8 deletions

View File

@ -410,6 +410,27 @@ STATIC mp_obj_t pyb_usb_vcp_make_new(const mp_obj_type_t *type, size_t n_args, s
} }
} }
// init(*, flow=-1)
STATIC mp_obj_t pyb_usb_vcp_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_flow };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// flow control
if (args[ARG_flow].u_int != -1) {
self->cdc_itf->flow = args[ARG_flow].u_int;
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_init_obj, 1, pyb_usb_vcp_init);
STATIC mp_obj_t pyb_usb_vcp_setinterrupt(mp_obj_t self_in, mp_obj_t int_chr_in) { STATIC mp_obj_t pyb_usb_vcp_setinterrupt(mp_obj_t self_in, mp_obj_t int_chr_in) {
mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in)); mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in));
return mp_const_none; return mp_const_none;
@ -510,6 +531,7 @@ mp_obj_t pyb_usb_vcp___exit__(size_t n_args, const mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_usb_vcp___exit___obj, 4, 4, pyb_usb_vcp___exit__); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_usb_vcp___exit___obj, 4, 4, pyb_usb_vcp___exit__);
STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = { STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_usb_vcp_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_setinterrupt), MP_ROM_PTR(&pyb_usb_vcp_setinterrupt_obj) }, { MP_ROM_QSTR(MP_QSTR_setinterrupt), MP_ROM_PTR(&pyb_usb_vcp_setinterrupt_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&pyb_usb_vcp_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&pyb_usb_vcp_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&pyb_usb_vcp_any_obj) }, { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&pyb_usb_vcp_any_obj) },
@ -524,6 +546,9 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pyb_usb_vcp___exit___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pyb_usb_vcp___exit___obj) },
// class constants
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) },
}; };
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);

View File

@ -69,6 +69,7 @@ uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) {
// be filled (by usbd_cdc_tx_always) before the USB device is connected. // be filled (by usbd_cdc_tx_always) before the USB device is connected.
cdc->rx_buf_put = 0; cdc->rx_buf_put = 0;
cdc->rx_buf_get = 0; cdc->rx_buf_get = 0;
cdc->rx_buf_full = false;
cdc->tx_buf_ptr_out = 0; cdc->tx_buf_ptr_out = 0;
cdc->tx_buf_ptr_out_shadow = 0; cdc->tx_buf_ptr_out_shadow = 0;
cdc->tx_need_empty_packet = 0; cdc->tx_need_empty_packet = 0;
@ -236,6 +237,25 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) {
} }
} }
bool usbd_cdc_rx_buffer_full(usbd_cdc_itf_t *cdc) {
int get = cdc->rx_buf_get, put = cdc->rx_buf_put;
int remaining = (get - put) + (-((int) (get <= put)) & USBD_CDC_RX_DATA_SIZE);
return remaining < CDC_DATA_MAX_PACKET_SIZE + 1;
}
void usbd_cdc_rx_check_resume(usbd_cdc_itf_t *cdc) {
uint32_t irq_state = disable_irq();
if (cdc->rx_buf_full) {
if (!usbd_cdc_rx_buffer_full(cdc)) {
cdc->rx_buf_full = false;
enable_irq(irq_state);
USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf);
return;
}
}
enable_irq(irq_state);
}
// Data received over USB OUT endpoint is processed here. // Data received over USB OUT endpoint is processed here.
// len: number of bytes received into the buffer we passed to USBD_CDC_ReceivePacket // len: number of bytes received into the buffer we passed to USBD_CDC_ReceivePacket
// Returns USBD_OK if all operations are OK else USBD_FAIL // Returns USBD_OK if all operations are OK else USBD_FAIL
@ -257,10 +277,14 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) {
} }
} }
if ((cdc->flow & USBD_CDC_FLOWCONTROL_RTS) && (usbd_cdc_rx_buffer_full(cdc))) {
cdc->rx_buf_full = true;
return USBD_BUSY;
} else {
// initiate next USB packet transfer // initiate next USB packet transfer
USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf); cdc->rx_buf_full = false;
return USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf);
return USBD_OK; }
} }
int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) {
@ -339,6 +363,7 @@ int usbd_cdc_rx_num(usbd_cdc_itf_t *cdc) {
if (rx_waiting < 0) { if (rx_waiting < 0) {
rx_waiting += USBD_CDC_RX_DATA_SIZE; rx_waiting += USBD_CDC_RX_DATA_SIZE;
} }
usbd_cdc_rx_check_resume(cdc);
return rx_waiting; return rx_waiting;
} }
@ -359,6 +384,7 @@ int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeou
// IRQs disabled so buffer will never be filled; return immediately // IRQs disabled so buffer will never be filled; return immediately
return i; return i;
} }
usbd_cdc_rx_check_resume(cdc);
__WFI(); // enter sleep mode, waiting for interrupt __WFI(); // enter sleep mode, waiting for interrupt
} }
@ -366,6 +392,7 @@ int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeou
buf[i] = cdc->rx_user_buf[cdc->rx_buf_get]; buf[i] = cdc->rx_user_buf[cdc->rx_buf_get];
cdc->rx_buf_get = (cdc->rx_buf_get + 1) & (USBD_CDC_RX_DATA_SIZE - 1); cdc->rx_buf_get = (cdc->rx_buf_get + 1) & (USBD_CDC_RX_DATA_SIZE - 1);
} }
usbd_cdc_rx_check_resume(cdc);
// Success, return number of bytes read // Success, return number of bytes read
return len; return len;

View File

@ -39,6 +39,10 @@
#define USBD_CDC_CONNECT_STATE_CONNECTING (1) #define USBD_CDC_CONNECT_STATE_CONNECTING (1)
#define USBD_CDC_CONNECT_STATE_CONNECTED (2) #define USBD_CDC_CONNECT_STATE_CONNECTED (2)
// Flow control settings
#define USBD_CDC_FLOWCONTROL_NONE (0)
#define USBD_CDC_FLOWCONTROL_RTS (1)
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
@ -46,6 +50,7 @@ typedef struct _usbd_cdc_itf_t {
uint8_t rx_user_buf[USBD_CDC_RX_DATA_SIZE]; // received data is buffered here until the user reads it uint8_t rx_user_buf[USBD_CDC_RX_DATA_SIZE]; // received data is buffered here until the user reads it
volatile uint16_t rx_buf_put; // circular buffer index volatile uint16_t rx_buf_put; // circular buffer index
uint16_t rx_buf_get; // circular buffer index uint16_t rx_buf_get; // circular buffer index
uint8_t rx_buf_full; // rx from host will be blocked while this is true
uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer
uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available
@ -55,6 +60,7 @@ typedef struct _usbd_cdc_itf_t {
volatile uint8_t connect_state; // indicates if we are connected volatile uint8_t connect_state; // indicates if we are connected
uint8_t attached_to_repl; // indicates if interface is connected to REPL uint8_t attached_to_repl; // indicates if interface is connected to REPL
uint8_t flow; // USBD_CDC_FLOWCONTROL_* setting flags
} usbd_cdc_itf_t; } usbd_cdc_itf_t;
// This is implemented in usb.c // This is implemented in usb.c

View File

@ -1128,14 +1128,13 @@ static uint8_t USBD_CDC_MSC_HID_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
/* USB data will be immediately processed, this allow next USB traffic being /* USB data will be immediately processed, this allow next USB traffic being
NAKed till the end of the application Xfer */ NAKed till the end of the application Xfer */
usbd_cdc_receive(usbd->cdc, len); return usbd_cdc_receive(usbd->cdc, len);
return USBD_OK;
#if MICROPY_HW_USB_ENABLE_CDC2 #if MICROPY_HW_USB_ENABLE_CDC2
} else if ((usbd->usbd_mode & USBD_MODE_CDC2) && epnum == (CDC2_OUT_EP & 0x7f)) { } else if ((usbd->usbd_mode & USBD_MODE_CDC2) && epnum == (CDC2_OUT_EP & 0x7f)) {
size_t len = USBD_LL_GetRxDataSize(pdev, epnum); size_t len = USBD_LL_GetRxDataSize(pdev, epnum);
usbd_cdc_receive(usbd->cdc2, len); return usbd_cdc_receive(usbd->cdc2, len);
return USBD_OK;
#endif #endif
} else if ((usbd->usbd_mode & USBD_MODE_MSC) && epnum == (MSC_OUT_EP & 0x7f)) { } else if ((usbd->usbd_mode & USBD_MODE_MSC) && epnum == (MSC_OUT_EP & 0x7f)) {
MSC_BOT_DataOut(pdev, epnum); MSC_BOT_DataOut(pdev, epnum);