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:
parent
63eae33b7b
commit
67689bfd7e
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initiate next USB packet transfer
|
if ((cdc->flow & USBD_CDC_FLOWCONTROL_RTS) && (usbd_cdc_rx_buffer_full(cdc))) {
|
||||||
USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf);
|
cdc->rx_buf_full = true;
|
||||||
|
return USBD_BUSY;
|
||||||
return USBD_OK;
|
} else {
|
||||||
|
// initiate next USB packet transfer
|
||||||
|
cdc->rx_buf_full = false;
|
||||||
|
return USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue