Skip to content

Commit a2c4dfa

Browse files
committed
Ensure USB host buffers are allocated in internal RAM
1 parent 2317aec commit a2c4dfa

File tree

7 files changed

+141
-5
lines changed

7 files changed

+141
-5
lines changed

locale/circuitpython.pot

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,10 @@ msgstr ""
844844
msgid "Coordinate arrays types have different sizes"
845845
msgstr ""
846846

847+
#: shared-module/usb/core/Device.c
848+
msgid "Could not allocate DMA capable buffer"
849+
msgstr ""
850+
847851
#: ports/espressif/common-hal/rclcpy/Publisher.c
848852
msgid "Could not publish to ROS topic"
849853
msgstr ""

ports/raspberrypi/mpconfigport.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ CIRCUITPY_CYW43_INIT_DELAY ?= 1000
5959
endif
6060

6161
ifeq ($(CHIP_VARIANT),RP2350)
62+
# RP2350 has PSRAM that is not DMA-capable
63+
CIRCUITPY_ALL_MEMORY_DMA_CAPABLE = 0
64+
6265
# This needs to be implemented.
6366
CIRCUITPY_ALARM = 0
6467
# Default PICODVI on because it doesn't require much code in RAM to talk to HSTX.

ports/raspberrypi/supervisor/port.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,14 @@ void *port_realloc(void *ptr, size_t size, bool dma_capable) {
302302
return new_ptr;
303303
}
304304

305+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
306+
bool port_buffer_is_dma_capable(const void *ptr) {
307+
// For RP2350, DMA can only access SRAM, not PSRAM
308+
// PSRAM addresses are below SRAM_BASE
309+
return ptr != NULL && ((size_t)ptr) >= SRAM_BASE;
310+
}
311+
#endif
312+
305313
static bool max_size_walker(void *ptr, size_t size, int used, void *user) {
306314
size_t *max_size = (size_t *)user;
307315
if (!used && *max_size < size) {

py/circuitpy_mpconfig.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ CFLAGS += -DCIRCUITPY_ALARM=$(CIRCUITPY_ALARM)
9898
CIRCUITPY_ALARM_TOUCH ?= $(CIRCUITPY_ALARM)
9999
CFLAGS += -DCIRCUITPY_ALARM_TOUCH=$(CIRCUITPY_ALARM_TOUCH)
100100

101+
# Enable DMA buffer management for platforms where not all memory is DMA-capable
102+
# Platforms with PSRAM or other non-DMA memory should set this to 0
103+
CIRCUITPY_ALL_MEMORY_DMA_CAPABLE ?= 1
104+
CFLAGS += -DCIRCUITPY_ALL_MEMORY_DMA_CAPABLE=$(CIRCUITPY_ALL_MEMORY_DMA_CAPABLE)
105+
101106
CIRCUITPY_ANALOGBUFIO ?= 0
102107
CFLAGS += -DCIRCUITPY_ANALOGBUFIO=$(CIRCUITPY_ANALOGBUFIO)
103108

shared-module/usb/core/Device.c

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "shared-bindings/usb/core/Device.h"
99

1010
#include "tusb_config.h"
11+
#include "supervisor/port.h"
12+
#include "supervisor/port_heap.h"
1113

1214
#include "lib/tinyusb/src/host/hcd.h"
1315
#include "lib/tinyusb/src/host/usbh.h"
@@ -33,6 +35,39 @@ void tuh_umount_cb(uint8_t dev_addr) {
3335

3436
static xfer_result_t _xfer_result;
3537
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+
// 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;
54+
}
55+
56+
// Copy data to DMA buffer if writing
57+
if (for_write && buffer != NULL) {
58+
memcpy(self->temp_buffer, buffer, len);
59+
}
60+
61+
return self->temp_buffer;
62+
}
63+
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+
#endif
3671
bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) {
3772
if (!tuh_inited()) {
3873
mp_raise_RuntimeError(MP_ERROR_TEXT("No usb host port initialized"));
@@ -46,6 +81,10 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d
4681
}
4782
self->device_address = device_address;
4883
self->first_langid = 0;
84+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
85+
self->temp_buffer = NULL;
86+
self->temp_buffer_size = 0;
87+
#endif
4988
_xfer_result = XFER_RESULT_INVALID;
5089
return true;
5190
}
@@ -65,6 +104,14 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) {
65104
self->open_endpoints[i] = 0;
66105
}
67106
}
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
68115
self->device_address = 0;
69116
}
70117

@@ -399,10 +446,22 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t
399446
mp_raise_usb_core_USBError(NULL);
400447
return 0;
401448
}
449+
450+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
451+
// Ensure buffer is in DMA-capable memory
452+
uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, true); // true = for write
453+
if (dma_buffer == NULL) {
454+
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer"));
455+
return 0;
456+
}
457+
#else
458+
uint8_t *dma_buffer = (uint8_t *)buffer; // All memory is DMA-capable
459+
#endif
460+
402461
tuh_xfer_t xfer;
403462
xfer.daddr = self->device_address;
404463
xfer.ep_addr = endpoint;
405-
xfer.buffer = (uint8_t *)buffer;
464+
xfer.buffer = dma_buffer;
406465
xfer.buflen = len;
407466
return _xfer(&xfer, timeout);
408467
}
@@ -412,12 +471,33 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e
412471
mp_raise_usb_core_USBError(NULL);
413472
return 0;
414473
}
474+
475+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
476+
// Ensure buffer is in DMA-capable memory
477+
uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, false); // false = for read
478+
if (dma_buffer == NULL) {
479+
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer"));
480+
return 0;
481+
}
482+
#else
483+
uint8_t *dma_buffer = buffer; // All memory is DMA-capable
484+
#endif
485+
415486
tuh_xfer_t xfer;
416487
xfer.daddr = self->device_address;
417488
xfer.ep_addr = endpoint;
418-
xfer.buffer = buffer;
489+
xfer.buffer = dma_buffer;
419490
xfer.buflen = len;
420-
return _xfer(&xfer, timeout);
491+
mp_int_t result = _xfer(&xfer, timeout);
492+
493+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
494+
// Copy data back to original buffer if needed
495+
if (result > 0) {
496+
_copy_from_dma_buffer(self, buffer, result);
497+
}
498+
#endif
499+
500+
return result;
421501
}
422502

423503
mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
@@ -426,6 +506,23 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
426506
uint8_t *buffer, mp_int_t len, mp_int_t timeout) {
427507
// Timeout is in ms.
428508

509+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
510+
// Determine if this is a write (host-to-device) or read (device-to-host) transfer
511+
bool is_write = (bmRequestType & 0x80) == 0; // Bit 7: 0=host-to-device, 1=device-to-host
512+
513+
// Ensure buffer is in DMA-capable memory
514+
uint8_t *dma_buffer = NULL;
515+
if (len > 0 && buffer != NULL) {
516+
dma_buffer = _ensure_dma_buffer(self, buffer, len, is_write);
517+
if (dma_buffer == NULL) {
518+
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer"));
519+
return 0;
520+
}
521+
}
522+
#else
523+
uint8_t *dma_buffer = buffer; // All memory is DMA-capable
524+
#endif
525+
429526
tusb_control_request_t request = {
430527
.bmRequestType = bmRequestType,
431528
.bRequest = bRequest,
@@ -437,7 +534,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
437534
.daddr = self->device_address,
438535
.ep_addr = 0,
439536
.setup = &request,
440-
.buffer = buffer,
537+
.buffer = dma_buffer,
441538
.complete_cb = _transfer_done_cb,
442539
};
443540

@@ -446,7 +543,16 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
446543
mp_raise_usb_core_USBError(NULL);
447544
return 0;
448545
}
449-
return (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout);
546+
mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout);
547+
548+
#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);
552+
}
553+
#endif
554+
555+
return result;
450556
}
451557

452558
bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) {

shared-module/usb/core/Device.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ 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
1822
} usb_core_device_obj_t;

supervisor/port_heap.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,10 @@ void port_free(void *ptr);
2727

2828
void *port_realloc(void *ptr, size_t size, bool dma_capable);
2929

30+
#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
31+
// Check if a buffer pointer is in DMA-capable memory. DMA-capable memory is also accessible during
32+
// flash operations.
33+
bool port_buffer_is_dma_capable(const void *ptr);
34+
#endif
35+
3036
size_t port_heap_get_largest_free_size(void);

0 commit comments

Comments
 (0)