Skip to content

Commit 9f771b3

Browse files
committed
Free dma capable temporary buffer immediately
This reduces memory use and prevents long term fragmentation.
1 parent a2c4dfa commit 9f771b3

File tree

2 files changed

+56
-46
lines changed

2 files changed

+56
-46
lines changed

shared-module/usb/core/Device.c

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,19 @@ static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *b
4343
return (uint8_t *)buffer; // Already DMA-capable, use directly
4444
}
4545

46-
// Need to allocate/reallocate temporary buffer in DMA-capable memory
47-
if (self->temp_buffer_size < len) {
48-
self->temp_buffer = port_realloc(self->temp_buffer, len, true); // true = DMA capable
49-
if (self->temp_buffer == NULL) {
50-
self->temp_buffer_size = 0;
51-
return NULL; // Allocation failed
52-
}
53-
self->temp_buffer_size = len;
46+
uint8_t *dma_buffer = port_malloc(len, true); // true = DMA capable
47+
if (dma_buffer == NULL) {
48+
return NULL; // Allocation failed
5449
}
5550

5651
// Copy data to DMA buffer if writing
5752
if (for_write && buffer != NULL) {
58-
memcpy(self->temp_buffer, buffer, len);
53+
memcpy(dma_buffer, buffer, len);
5954
}
6055

61-
return self->temp_buffer;
56+
return dma_buffer;
6257
}
6358

64-
// Copy data back from DMA buffer to original buffer after read
65-
static void _copy_from_dma_buffer(usb_core_device_obj_t *self, uint8_t *original_buffer, size_t len) {
66-
if (!port_buffer_is_dma_capable(original_buffer) && self->temp_buffer != NULL) {
67-
memcpy(original_buffer, self->temp_buffer, len);
68-
}
69-
}
7059
#endif
7160
bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) {
7261
if (!tuh_inited()) {
@@ -81,10 +70,6 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d
8170
}
8271
self->device_address = device_address;
8372
self->first_langid = 0;
84-
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
85-
self->temp_buffer = NULL;
86-
self->temp_buffer_size = 0;
87-
#endif
8873
_xfer_result = XFER_RESULT_INVALID;
8974
return true;
9075
}
@@ -104,14 +89,6 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) {
10489
self->open_endpoints[i] = 0;
10590
}
10691
}
107-
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
108-
// Clean up temporary buffer
109-
if (self->temp_buffer != NULL) {
110-
port_free(self->temp_buffer);
111-
self->temp_buffer = NULL;
112-
self->temp_buffer_size = 0;
113-
}
114-
#endif
11592
self->device_address = 0;
11693
}
11794

@@ -180,7 +157,24 @@ static void _prepare_for_transfer(void) {
180157
_actual_len = 0;
181158
}
182159

183-
static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout) {
160+
static void _abort_transfer(tuh_xfer_t *xfer) {
161+
bool aborted = tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
162+
if (aborted) {
163+
// If the transfer was aborted, then we can continue.
164+
return;
165+
}
166+
uint32_t start_time = supervisor_ticks_ms32();
167+
// If not, we need to wait for it to finish, otherwise we may free memory out from under it.
168+
// Limit the wait time to 10 milliseconds to avoid blocking indefinitely.
169+
while (_xfer_result == XFER_RESULT_INVALID && (supervisor_ticks_ms32() - start_time < 10)) {
170+
// The background tasks include TinyUSB which will call the function
171+
// we provided above. In other words, the callback isn't in an interrupt.
172+
RUN_BACKGROUND_TASKS;
173+
}
174+
}
175+
176+
// Only frees the transfer buffer on error.
177+
static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) {
184178
if (xfer == NULL) {
185179
mp_raise_usb_core_USBError(NULL);
186180
return 0;
@@ -195,12 +189,15 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
195189
}
196190
if (mp_hal_is_interrupted()) {
197191
// Handle case of VM being interrupted by Ctrl-C or autoreload
198-
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
192+
_abort_transfer(xfer);
199193
return 0;
200194
}
201195
// Handle transfer result code from TinyUSB
202196
xfer_result_t result = _xfer_result;
203197
_xfer_result = XFER_RESULT_INVALID;
198+
if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID) {
199+
port_free(xfer->buffer);
200+
}
204201
switch (result) {
205202
case XFER_RESULT_SUCCESS:
206203
return _actual_len;
@@ -217,8 +214,11 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
217214
break;
218215
case XFER_RESULT_INVALID:
219216
// This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB
220-
// to stop the transfer
221-
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
217+
// to stop the transfer and then wait to free the buffer.
218+
_abort_transfer(xfer);
219+
if (our_buffer) {
220+
port_free(xfer->buffer);
221+
}
222222
mp_raise_usb_core_USBTimeoutError();
223223
break;
224224
}
@@ -382,14 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m
382382
_wait_for_callback();
383383
}
384384

385-
static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) {
385+
// Raises an exception on failure. Returns the number of bytes transferred (maybe zero) on success.
386+
static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) {
386387
_prepare_for_transfer();
387388
xfer->complete_cb = _transfer_done_cb;
388389
if (!tuh_edpt_xfer(xfer)) {
390+
if (our_buffer) {
391+
port_free(xfer->buffer);
392+
}
389393
mp_raise_usb_core_USBError(NULL);
390394
return 0;
391395
}
392-
return _handle_timed_transfer_callback(xfer, timeout);
396+
return _handle_timed_transfer_callback(xfer, timeout, our_buffer);
393397
}
394398

395399
static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) {
@@ -463,7 +467,13 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t
463467
xfer.ep_addr = endpoint;
464468
xfer.buffer = dma_buffer;
465469
xfer.buflen = len;
466-
return _xfer(&xfer, timeout);
470+
size_t result = _xfer(&xfer, timeout, dma_buffer != buffer);
471+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
472+
if (dma_buffer != buffer) {
473+
port_free(dma_buffer);
474+
}
475+
#endif
476+
return result;
467477
}
468478

469479
mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout) {
@@ -488,12 +498,13 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e
488498
xfer.ep_addr = endpoint;
489499
xfer.buffer = dma_buffer;
490500
xfer.buflen = len;
491-
mp_int_t result = _xfer(&xfer, timeout);
501+
mp_int_t result = _xfer(&xfer, timeout, dma_buffer != buffer);
492502

493503
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
494504
// Copy data back to original buffer if needed
495-
if (result > 0) {
496-
_copy_from_dma_buffer(self, buffer, result);
505+
if (dma_buffer != buffer) {
506+
memcpy(buffer, dma_buffer, result);
507+
port_free(dma_buffer);
497508
}
498509
#endif
499510

@@ -543,12 +554,15 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
543554
mp_raise_usb_core_USBError(NULL);
544555
return 0;
545556
}
546-
mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout);
557+
mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout, dma_buffer != buffer);
547558

548559
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
549-
// Copy data back to original buffer if this was a read transfer and we got data
550-
if ((bmRequestType & 0x80) != 0 && result > 0 && buffer != NULL) { // Read transfer (device-to-host)
551-
_copy_from_dma_buffer(self, buffer, result);
560+
if (dma_buffer != buffer) {
561+
// Copy data back to original buffer if this was a read transfer
562+
if (buffer != NULL && !is_write) {
563+
memcpy(buffer, dma_buffer, result);
564+
}
565+
port_free(dma_buffer);
552566
}
553567
#endif
554568

shared-module/usb/core/Device.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,4 @@ typedef struct {
1515
uint8_t *configuration_descriptor; // Contains the length of the all descriptors.
1616
uint8_t open_endpoints[8];
1717
uint16_t first_langid;
18-
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
19-
uint8_t *temp_buffer; // Temporary buffer for PSRAM data
20-
size_t temp_buffer_size; // Size of temporary buffer
21-
#endif
2218
} usb_core_device_obj_t;

0 commit comments

Comments
 (0)