8
8
#include "shared-bindings/usb/core/Device.h"
9
9
10
10
#include "tusb_config.h"
11
+ #include "supervisor/port.h"
12
+ #include "supervisor/port_heap.h"
11
13
12
14
#include "lib/tinyusb/src/host/hcd.h"
13
15
#include "lib/tinyusb/src/host/usbh.h"
@@ -33,6 +35,28 @@ void tuh_umount_cb(uint8_t dev_addr) {
33
35
34
36
static xfer_result_t _xfer_result ;
35
37
static size_t _actual_len ;
38
+
39
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
40
+ // Helper to ensure buffer is DMA-capable for transfer operations
41
+ static uint8_t * _ensure_dma_buffer (usb_core_device_obj_t * self , const uint8_t * buffer , size_t len , bool for_write ) {
42
+ if (port_buffer_is_dma_capable (buffer )) {
43
+ return (uint8_t * )buffer ; // Already DMA-capable, use directly
44
+ }
45
+
46
+ uint8_t * dma_buffer = port_malloc (len , true); // true = DMA capable
47
+ if (dma_buffer == NULL ) {
48
+ return NULL ; // Allocation failed
49
+ }
50
+
51
+ // Copy data to DMA buffer if writing
52
+ if (for_write && buffer != NULL ) {
53
+ memcpy (dma_buffer , buffer , len );
54
+ }
55
+
56
+ return dma_buffer ;
57
+ }
58
+
59
+ #endif
36
60
bool common_hal_usb_core_device_construct (usb_core_device_obj_t * self , uint8_t device_address ) {
37
61
if (!tuh_inited ()) {
38
62
mp_raise_RuntimeError (MP_ERROR_TEXT ("No usb host port initialized" ));
@@ -133,7 +157,24 @@ static void _prepare_for_transfer(void) {
133
157
_actual_len = 0 ;
134
158
}
135
159
136
- 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 ) {
137
178
if (xfer == NULL ) {
138
179
mp_raise_usb_core_USBError (NULL );
139
180
return 0 ;
@@ -148,12 +189,15 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
148
189
}
149
190
if (mp_hal_is_interrupted ()) {
150
191
// Handle case of VM being interrupted by Ctrl-C or autoreload
151
- tuh_edpt_abort_xfer (xfer -> daddr , xfer -> ep_addr );
192
+ _abort_transfer (xfer );
152
193
return 0 ;
153
194
}
154
195
// Handle transfer result code from TinyUSB
155
196
xfer_result_t result = _xfer_result ;
156
197
_xfer_result = XFER_RESULT_INVALID ;
198
+ if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID ) {
199
+ port_free (xfer -> buffer );
200
+ }
157
201
switch (result ) {
158
202
case XFER_RESULT_SUCCESS :
159
203
return _actual_len ;
@@ -170,8 +214,11 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout
170
214
break ;
171
215
case XFER_RESULT_INVALID :
172
216
// This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB
173
- // to stop the transfer
174
- 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
+ }
175
222
mp_raise_usb_core_USBTimeoutError ();
176
223
break ;
177
224
}
@@ -335,14 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m
335
382
_wait_for_callback ();
336
383
}
337
384
338
- 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 ) {
339
387
_prepare_for_transfer ();
340
388
xfer -> complete_cb = _transfer_done_cb ;
341
389
if (!tuh_edpt_xfer (xfer )) {
390
+ if (our_buffer ) {
391
+ port_free (xfer -> buffer );
392
+ }
342
393
mp_raise_usb_core_USBError (NULL );
343
394
return 0 ;
344
395
}
345
- return _handle_timed_transfer_callback (xfer , timeout );
396
+ return _handle_timed_transfer_callback (xfer , timeout , our_buffer );
346
397
}
347
398
348
399
static bool _open_endpoint (usb_core_device_obj_t * self , mp_int_t endpoint ) {
@@ -399,25 +450,65 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t
399
450
mp_raise_usb_core_USBError (NULL );
400
451
return 0 ;
401
452
}
453
+
454
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
455
+ // Ensure buffer is in DMA-capable memory
456
+ uint8_t * dma_buffer = _ensure_dma_buffer (self , buffer , len , true); // true = for write
457
+ if (dma_buffer == NULL ) {
458
+ mp_raise_msg (& mp_type_MemoryError , MP_ERROR_TEXT ("Could not allocate DMA capable buffer" ));
459
+ return 0 ;
460
+ }
461
+ #else
462
+ uint8_t * dma_buffer = (uint8_t * )buffer ; // All memory is DMA-capable
463
+ #endif
464
+
402
465
tuh_xfer_t xfer ;
403
466
xfer .daddr = self -> device_address ;
404
467
xfer .ep_addr = endpoint ;
405
- xfer .buffer = ( uint8_t * ) buffer ;
468
+ xfer .buffer = dma_buffer ;
406
469
xfer .buflen = len ;
407
- 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 ;
408
477
}
409
478
410
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 ) {
411
480
if (!_open_endpoint (self , endpoint )) {
412
481
mp_raise_usb_core_USBError (NULL );
413
482
return 0 ;
414
483
}
484
+
485
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
486
+ // Ensure buffer is in DMA-capable memory
487
+ uint8_t * dma_buffer = _ensure_dma_buffer (self , buffer , len , false); // false = for read
488
+ if (dma_buffer == NULL ) {
489
+ mp_raise_msg (& mp_type_MemoryError , MP_ERROR_TEXT ("Could not allocate DMA capable buffer" ));
490
+ return 0 ;
491
+ }
492
+ #else
493
+ uint8_t * dma_buffer = buffer ; // All memory is DMA-capable
494
+ #endif
495
+
415
496
tuh_xfer_t xfer ;
416
497
xfer .daddr = self -> device_address ;
417
498
xfer .ep_addr = endpoint ;
418
- xfer .buffer = buffer ;
499
+ xfer .buffer = dma_buffer ;
419
500
xfer .buflen = len ;
420
- return _xfer (& xfer , timeout );
501
+ mp_int_t result = _xfer (& xfer , timeout , dma_buffer != buffer );
502
+
503
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
504
+ // Copy data back to original buffer if needed
505
+ if (dma_buffer != buffer ) {
506
+ memcpy (buffer , dma_buffer , result );
507
+ port_free (dma_buffer );
508
+ }
509
+ #endif
510
+
511
+ return result ;
421
512
}
422
513
423
514
mp_int_t common_hal_usb_core_device_ctrl_transfer (usb_core_device_obj_t * self ,
@@ -426,6 +517,23 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
426
517
uint8_t * buffer , mp_int_t len , mp_int_t timeout ) {
427
518
// Timeout is in ms.
428
519
520
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
521
+ // Determine if this is a write (host-to-device) or read (device-to-host) transfer
522
+ bool is_write = (bmRequestType & 0x80 ) == 0 ; // Bit 7: 0=host-to-device, 1=device-to-host
523
+
524
+ // Ensure buffer is in DMA-capable memory
525
+ uint8_t * dma_buffer = NULL ;
526
+ if (len > 0 && buffer != NULL ) {
527
+ dma_buffer = _ensure_dma_buffer (self , buffer , len , is_write );
528
+ if (dma_buffer == NULL ) {
529
+ mp_raise_msg (& mp_type_MemoryError , MP_ERROR_TEXT ("Could not allocate DMA capable buffer" ));
530
+ return 0 ;
531
+ }
532
+ }
533
+ #else
534
+ uint8_t * dma_buffer = buffer ; // All memory is DMA-capable
535
+ #endif
536
+
429
537
tusb_control_request_t request = {
430
538
.bmRequestType = bmRequestType ,
431
539
.bRequest = bRequest ,
@@ -437,7 +545,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
437
545
.daddr = self -> device_address ,
438
546
.ep_addr = 0 ,
439
547
.setup = & request ,
440
- .buffer = buffer ,
548
+ .buffer = dma_buffer ,
441
549
.complete_cb = _transfer_done_cb ,
442
550
};
443
551
@@ -446,7 +554,19 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
446
554
mp_raise_usb_core_USBError (NULL );
447
555
return 0 ;
448
556
}
449
- return (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 );
558
+
559
+ #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
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 );
566
+ }
567
+ #endif
568
+
569
+ return result ;
450
570
}
451
571
452
572
bool common_hal_usb_core_device_is_kernel_driver_active (usb_core_device_obj_t * self , mp_int_t interface ) {
0 commit comments