@@ -43,30 +43,19 @@ static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *b
43
43
return (uint8_t * )buffer ; // Already DMA-capable, use directly
44
44
}
45
45
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
54
49
}
55
50
56
51
// Copy data to DMA buffer if writing
57
52
if (for_write && buffer != NULL ) {
58
- memcpy (self -> temp_buffer , buffer , len );
53
+ memcpy (dma_buffer , buffer , len );
59
54
}
60
55
61
- return self -> temp_buffer ;
56
+ return dma_buffer ;
62
57
}
63
58
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
- }
70
59
#endif
71
60
bool common_hal_usb_core_device_construct (usb_core_device_obj_t * self , uint8_t device_address ) {
72
61
if (!tuh_inited ()) {
@@ -81,10 +70,6 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d
81
70
}
82
71
self -> device_address = device_address ;
83
72
self -> first_langid = 0 ;
84
- #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
85
- self -> temp_buffer = NULL ;
86
- self -> temp_buffer_size = 0 ;
87
- #endif
88
73
_xfer_result = XFER_RESULT_INVALID ;
89
74
return true;
90
75
}
@@ -104,14 +89,6 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) {
104
89
self -> open_endpoints [i ] = 0 ;
105
90
}
106
91
}
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
115
92
self -> device_address = 0 ;
116
93
}
117
94
@@ -180,7 +157,24 @@ static void _prepare_for_transfer(void) {
180
157
_actual_len = 0 ;
181
158
}
182
159
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 ) {
184
178
if (xfer == NULL ) {
185
179
mp_raise_usb_core_USBError (NULL );
186
180
return 0 ;
@@ -195,12 +189,15 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
195
189
}
196
190
if (mp_hal_is_interrupted ()) {
197
191
// 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 );
199
193
return 0 ;
200
194
}
201
195
// Handle transfer result code from TinyUSB
202
196
xfer_result_t result = _xfer_result ;
203
197
_xfer_result = XFER_RESULT_INVALID ;
198
+ if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID ) {
199
+ port_free (xfer -> buffer );
200
+ }
204
201
switch (result ) {
205
202
case XFER_RESULT_SUCCESS :
206
203
return _actual_len ;
@@ -217,8 +214,11 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
217
214
break ;
218
215
case XFER_RESULT_INVALID :
219
216
// 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
+ }
222
222
mp_raise_usb_core_USBTimeoutError ();
223
223
break ;
224
224
}
@@ -382,14 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m
382
382
_wait_for_callback ();
383
383
}
384
384
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 ) {
386
387
_prepare_for_transfer ();
387
388
xfer -> complete_cb = _transfer_done_cb ;
388
389
if (!tuh_edpt_xfer (xfer )) {
390
+ if (our_buffer ) {
391
+ port_free (xfer -> buffer );
392
+ }
389
393
mp_raise_usb_core_USBError (NULL );
390
394
return 0 ;
391
395
}
392
- return _handle_timed_transfer_callback (xfer , timeout );
396
+ return _handle_timed_transfer_callback (xfer , timeout , our_buffer );
393
397
}
394
398
395
399
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
463
467
xfer .ep_addr = endpoint ;
464
468
xfer .buffer = dma_buffer ;
465
469
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 ;
467
477
}
468
478
469
479
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
488
498
xfer .ep_addr = endpoint ;
489
499
xfer .buffer = dma_buffer ;
490
500
xfer .buflen = len ;
491
- mp_int_t result = _xfer (& xfer , timeout );
501
+ mp_int_t result = _xfer (& xfer , timeout , dma_buffer != buffer );
492
502
493
503
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
494
504
// 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 );
497
508
}
498
509
#endif
499
510
@@ -543,12 +554,15 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
543
554
mp_raise_usb_core_USBError (NULL );
544
555
return 0 ;
545
556
}
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 );
547
558
548
559
#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 );
552
566
}
553
567
#endif
554
568
0 commit comments