Skip to content

Commit 40106da

Browse files
committed
stm32/usb_cdc: Fix tx ringbuffer to remove full == size-1 limitation.
1 parent 5093597 commit 40106da

File tree

2 files changed

+47
-29
lines changed

2 files changed

+47
-29
lines changed

ports/stm32/usbd_cdc_interface.c

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -171,40 +171,64 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui
171171
return USBD_OK;
172172
}
173173

174+
static uint16_t inline usbd_cdc_tx_buffer_mask(uint16_t val) {
175+
return val & (USBD_CDC_TX_DATA_SIZE - 1);
176+
}
177+
178+
static uint16_t inline usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) {
179+
return cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
180+
}
181+
182+
static bool inline usbd_cdc_tx_buffer_empty(usbd_cdc_itf_t *cdc) {
183+
return cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in;
184+
}
185+
186+
static bool inline usbd_cdc_tx_buffer_will_be_empty(usbd_cdc_itf_t *cdc) {
187+
return cdc->tx_buf_ptr_out_next == cdc->tx_buf_ptr_in;
188+
}
189+
190+
static bool inline usbd_cdc_tx_buffer_full(usbd_cdc_itf_t *cdc) {
191+
return usbd_cdc_tx_buffer_size(cdc) == USBD_CDC_TX_DATA_SIZE;
192+
}
193+
194+
static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) {
195+
uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out);
196+
return MIN(usbd_cdc_tx_buffer_size(cdc), to_end);
197+
}
198+
199+
static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) {
200+
cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data;
201+
cdc->tx_buf_ptr_in++;
202+
}
203+
204+
static uint8_t *usbd_cdc_tx_buffer_getp(usbd_cdc_itf_t *cdc, uint16_t len) {
205+
cdc->tx_buf_ptr_out_next += len;
206+
return &cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out)];
207+
}
208+
174209
// Called when the USB IN endpoint is ready to receive more data
175210
// (cdc.base.tx_in_progress must be 0)
176211
void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) {
177212

178213
usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t *)cdc_in;
179-
cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow;
214+
cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_next;
180215

181-
if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) {
216+
if (usbd_cdc_tx_buffer_empty(cdc) && !cdc->tx_need_empty_packet) {
182217
// No outstanding data to send
183218
return;
184219
}
185-
186-
uint32_t len;
187-
if (cdc->tx_buf_ptr_out > cdc->tx_buf_ptr_in) { // rollback
188-
len = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out;
189-
} else {
190-
len = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
191-
}
192-
220+
uint16_t len = usbd_cdc_tx_send_length(cdc);
193221
// Should always succeed because cdc.base.tx_in_progress==0
194-
USBD_CDC_TransmitPacket(&cdc->base, len, &cdc->tx_buf[cdc->tx_buf_ptr_out]);
222+
USBD_CDC_TransmitPacket(&cdc->base, len, usbd_cdc_tx_buffer_getp(cdc, len));
195223

196-
cdc->tx_buf_ptr_out_shadow += len;
197-
if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) {
198-
cdc->tx_buf_ptr_out_shadow = 0;
199-
}
200224

201225
// According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE)
202226
// gets held at the USB host until the next packet is sent. This is because a
203227
// packet of maximum size is considered to be part of a longer chunk of data, and
204228
// the host waits for all data to arrive (ie, waits for a packet < max packet size).
205229
// To flush a packet of exactly max packet size, we need to send a zero-size packet.
206230
// See eg http://www.cypress.com/?id=4&rID=92719
207-
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);
231+
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));
208232
}
209233

210234
// 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) {
291315
}
292316

293317
int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) {
294-
int32_t tx_waiting = (int32_t)cdc->tx_buf_ptr_in - (int32_t)cdc->tx_buf_ptr_out;
295-
if (tx_waiting < 0) {
296-
tx_waiting += USBD_CDC_TX_DATA_SIZE;
297-
}
318+
int32_t tx_waiting = usbd_cdc_tx_buffer_size(cdc);
298319
return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2;
299320
}
300321

@@ -317,8 +338,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t
317338
for (uint32_t i = 0; i < len; i++) {
318339
// Wait until the device is connected and the buffer has space, with a given timeout
319340
uint32_t start = HAL_GetTick();
320-
while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED
321-
|| ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) {
341+
while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED || usbd_cdc_tx_buffer_full(cdc)) {
322342
usbd_cdc_try_tx(cdc);
323343
// Wraparound of tick is taken care of by 2's complement arithmetic.
324344
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
333353
}
334354

335355
// Write data to device buffer
336-
cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i];
337-
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
356+
usbd_cdc_tx_buffer_put(cdc, buf[i]);
338357
}
339358

340359
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) {
357376
// If the buffer is full, wait until it gets drained, with a timeout of 500ms
358377
// (wraparound of tick is taken care of by 2's complement arithmetic).
359378
uint32_t start = HAL_GetTick();
360-
while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) {
379+
while (usbd_cdc_tx_buffer_full(cdc) && HAL_GetTick() - start <= 500) {
361380
usbd_cdc_try_tx(cdc);
362381
if (query_irq() == IRQ_STATE_DISABLED) {
363382
// 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) {
367386
}
368387
}
369388

370-
cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i];
371-
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
389+
usbd_cdc_tx_buffer_put(cdc, buf[i]);
372390
}
373391
usbd_cdc_try_tx(cdc);
374392
}

ports/stm32/usbd_cdc_interface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2
3636
#endif
3737
#ifndef USBD_CDC_TX_DATA_SIZE
38-
#define USBD_CDC_TX_DATA_SIZE (1024) // I think this can be any value (was 2048)
38+
#define USBD_CDC_TX_DATA_SIZE (1024) // This must be a power of 2 and no greater than 16384
3939
#endif
4040

4141
// Values for connect_state
@@ -60,7 +60,7 @@ typedef struct _usbd_cdc_itf_t {
6060
uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer
6161
uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available
6262
volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained
63-
uint16_t tx_buf_ptr_out_shadow; // shadow of above
63+
uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished
6464
uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size
6565

6666
volatile uint8_t connect_state; // indicates if we are connected

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy