@@ -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
7160bool 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
395399static 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
469479mp_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
0 commit comments