diff options
-rw-r--r-- | ports/stm32/usbd_cdc_interface.c | 72 | ||||
-rw-r--r-- | ports/stm32/usbd_cdc_interface.h | 4 |
2 files changed, 47 insertions, 29 deletions
diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index a0e19de97..7a0912852 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -171,32 +171,56 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui return USBD_OK; } +static inline uint16_t usbd_cdc_tx_buffer_mask(uint16_t val) { + return val & (USBD_CDC_TX_DATA_SIZE - 1); +} + +static inline uint16_t usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out; +} + +static inline bool usbd_cdc_tx_buffer_empty(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in; +} + +static inline bool usbd_cdc_tx_buffer_will_be_empty(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_out_next == cdc->tx_buf_ptr_in; +} + +static inline bool usbd_cdc_tx_buffer_full(usbd_cdc_itf_t *cdc) { + return usbd_cdc_tx_buffer_size(cdc) == USBD_CDC_TX_DATA_SIZE; +} + +static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) { + uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out); + return MIN(usbd_cdc_tx_buffer_size(cdc), to_end); +} + +static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) { + cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data; + cdc->tx_buf_ptr_in++; +} + +static uint8_t *usbd_cdc_tx_buffer_getp(usbd_cdc_itf_t *cdc, uint16_t len) { + cdc->tx_buf_ptr_out_next += len; + return &cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out)]; +} + // Called when the USB IN endpoint is ready to receive more data // (cdc.base.tx_in_progress must be 0) void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t *)cdc_in; - cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow; + cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_next; - if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) { + if (usbd_cdc_tx_buffer_empty(cdc) && !cdc->tx_need_empty_packet) { // No outstanding data to send return; } - - uint32_t len; - if (cdc->tx_buf_ptr_out > cdc->tx_buf_ptr_in) { // rollback - len = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out; - } else { - len = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out; - } - + uint16_t len = usbd_cdc_tx_send_length(cdc); // Should always succeed because cdc.base.tx_in_progress==0 - USBD_CDC_TransmitPacket(&cdc->base, len, &cdc->tx_buf[cdc->tx_buf_ptr_out]); + USBD_CDC_TransmitPacket(&cdc->base, len, usbd_cdc_tx_buffer_getp(cdc, len)); - cdc->tx_buf_ptr_out_shadow += len; - if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) { - cdc->tx_buf_ptr_out_shadow = 0; - } // According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE) // gets held at the USB host until the next packet is sent. This is because a @@ -204,7 +228,7 @@ void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) { // the host waits for all data to arrive (ie, waits for a packet < max packet size). // To flush a packet of exactly max packet size, we need to send a zero-size packet. // See eg http://www.cypress.com/?id=4&rID=92719 - cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in); + cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && usbd_cdc_tx_buffer_will_be_empty(cdc)); } // Attempt to queue data on the USB IN endpoint @@ -291,10 +315,7 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) { } int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { - int32_t tx_waiting = (int32_t)cdc->tx_buf_ptr_in - (int32_t)cdc->tx_buf_ptr_out; - if (tx_waiting < 0) { - tx_waiting += USBD_CDC_TX_DATA_SIZE; - } + int32_t tx_waiting = usbd_cdc_tx_buffer_size(cdc); return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2; } @@ -317,8 +338,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t for (uint32_t i = 0; i < len; i++) { // Wait until the device is connected and the buffer has space, with a given timeout uint32_t start = HAL_GetTick(); - while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED - || ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) { + while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED || usbd_cdc_tx_buffer_full(cdc)) { usbd_cdc_try_tx(cdc); // Wraparound of tick is taken care of by 2's complement arithmetic. if (HAL_GetTick() - start >= timeout) { @@ -333,8 +353,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t } // Write data to device buffer - cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; - cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); + usbd_cdc_tx_buffer_put(cdc, buf[i]); } usbd_cdc_try_tx(cdc); @@ -357,7 +376,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { // If the buffer is full, wait until it gets drained, with a timeout of 500ms // (wraparound of tick is taken care of by 2's complement arithmetic). uint32_t start = HAL_GetTick(); - while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) { + while (usbd_cdc_tx_buffer_full(cdc) && HAL_GetTick() - start <= 500) { usbd_cdc_try_tx(cdc); if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be drained; exit loop @@ -367,8 +386,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { } } - cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; - cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); + usbd_cdc_tx_buffer_put(cdc, buf[i]); } usbd_cdc_try_tx(cdc); } diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index 1847b3a6b..d0509b09f 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -35,7 +35,7 @@ #define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2 #endif #ifndef USBD_CDC_TX_DATA_SIZE -#define USBD_CDC_TX_DATA_SIZE (1024) // I think this can be any value (was 2048) +#define USBD_CDC_TX_DATA_SIZE (1024) // This must be a power of 2 and no greater than 16384 #endif // Values for connect_state @@ -60,7 +60,7 @@ typedef struct _usbd_cdc_itf_t { 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 volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained - uint16_t tx_buf_ptr_out_shadow; // shadow of above + uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size volatile uint8_t connect_state; // indicates if we are connected |