Skip to content

Commit d0c8400

Browse files
committed
Automount SD cards
And make them available over USB MSC. They can be remount to make them writable (slowly) from the host PC. Fixes #9954. Fixes #8678. Fixes #3477
1 parent ca0eec3 commit d0c8400

File tree

13 files changed

+257
-62
lines changed

13 files changed

+257
-62
lines changed

extmod/vfs.c

+21-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
#include "extmod/vfs_posix.h"
4848
#endif
4949

50+
51+
#if CIRCUITPY_SDCARDIO
52+
#include "shared-module/sdcardio/__init__.h"
53+
#endif
54+
5055
// For mp_vfs_proxy_call, the maximum number of additional args that can be passed.
5156
// A fixed maximum size is used to avoid the need for a costly variable array.
5257
#define PROXY_MAX_ARGS (2)
@@ -67,6 +72,10 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
6772
// path is "" or "/" so return virtual root
6873
return MP_VFS_ROOT;
6974
}
75+
// CIRCUITPY-CHANGE: Try and automount the SD card.
76+
#if CIRCUITPY_SDCARDIO
77+
automount_sd_card();
78+
#endif
7079
for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
7180
size_t len = vfs->len - 1;
7281
if (len == 0) {
@@ -367,8 +376,18 @@ mp_obj_t mp_vfs_getcwd(void) {
367376
}
368377
MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd);
369378

370-
// CIRCUITPY-CHANGE: accessible from shared-module/os/__init__.c
371-
mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
379+
typedef struct _mp_vfs_ilistdir_it_t {
380+
mp_obj_base_t base;
381+
mp_fun_1_t iternext;
382+
union {
383+
mp_vfs_mount_t *vfs;
384+
mp_obj_t iter;
385+
} cur;
386+
bool is_str;
387+
bool is_iter;
388+
} mp_vfs_ilistdir_it_t;
389+
390+
static mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
372391
mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
373392
if (self->is_iter) {
374393
// continue delegating to root dir

extmod/vfs.h

-13
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,6 @@ typedef struct _mp_vfs_mount_t {
9494
struct _mp_vfs_mount_t *next;
9595
} mp_vfs_mount_t;
9696

97-
// CIRCUITPY-CHANGE: allow outside use of ilistdir_it_iternext
98-
typedef struct _mp_vfs_ilistdir_it_t {
99-
mp_obj_base_t base;
100-
mp_fun_1_t iternext;
101-
union {
102-
mp_vfs_mount_t *vfs;
103-
mp_obj_t iter;
104-
} cur;
105-
bool is_str;
106-
bool is_iter;
107-
} mp_vfs_ilistdir_it_t;
108-
109-
mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in);
11097
void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev);
11198
int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf);
11299
int mp_vfs_blockdev_read_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, uint8_t *buf);

locale/circuitpython.pot

+8-3
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ msgid "Cannot record to a file"
789789
msgstr ""
790790

791791
#: shared-module/storage/__init__.c
792-
msgid "Cannot remount '/' when visible via USB."
792+
msgid "Cannot remount path when visible via USB."
793793
msgstr ""
794794

795795
#: shared-bindings/digitalio/DigitalInOut.c
@@ -1975,6 +1975,7 @@ msgstr ""
19751975
#: shared-bindings/displayio/TileGrid.c
19761976
#: shared-bindings/memorymonitor/AllocationSize.c
19771977
#: shared-bindings/pulseio/PulseIn.c
1978+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
19781979
msgid "Slices not supported"
19791980
msgstr ""
19801981

@@ -2042,7 +2043,9 @@ msgstr ""
20422043
msgid "Tile height must exactly divide bitmap height"
20432044
msgstr ""
20442045

2045-
#: shared-bindings/displayio/TileGrid.c shared-module/displayio/TileGrid.c
2046+
#: shared-bindings/displayio/TileGrid.c
2047+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
2048+
#: shared-module/displayio/TileGrid.c
20462049
msgid "Tile index out of bounds"
20472050
msgstr ""
20482051

@@ -4277,7 +4280,9 @@ msgstr ""
42774280
msgid "unreadable attribute"
42784281
msgstr ""
42794282

4280-
#: shared-bindings/displayio/TileGrid.c shared-bindings/vectorio/VectorShape.c
4283+
#: shared-bindings/displayio/TileGrid.c
4284+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
4285+
#: shared-bindings/vectorio/VectorShape.c
42814286
msgid "unsupported %q type"
42824287
msgstr ""
42834288

ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
#define DEFAULT_DVI_BUS_BLUE_DN (&pin_GPIO18)
3131
#define DEFAULT_DVI_BUS_BLUE_DP (&pin_GPIO19)
3232

33+
#define DEFAULT_SD_SCK (&pin_GPIO34)
34+
#define DEFAULT_SD_MOSI (&pin_GPIO35)
35+
#define DEFAULT_SD_MISO (&pin_GPIO36)
36+
#define DEFAULT_SD_CS (&pin_GPIO39)
37+
#define DEFAULT_SD_CARD_DETECT (&pin_GPIO33)
38+
#define DEFAULT_SD_CARD_INSERTED true
39+
3340
#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47)
3441

3542
// #define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO44)

shared-module/os/__init__.c

+10-12
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,19 @@ mp_obj_t common_hal_os_getcwd(void) {
9090
mp_obj_t common_hal_os_listdir(const char *path) {
9191
mp_obj_t path_out;
9292
mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
93-
94-
mp_vfs_ilistdir_it_t iter;
95-
mp_obj_t iter_obj = MP_OBJ_FROM_PTR(&iter);
96-
9793
if (vfs == MP_VFS_ROOT) {
98-
// list the root directory
99-
iter.base.type = &mp_type_polymorph_iter;
100-
iter.iternext = mp_vfs_ilistdir_it_iternext;
101-
iter.cur.vfs = MP_STATE_VM(vfs_mount_table);
102-
iter.is_str = true;
103-
iter.is_iter = false;
104-
} else {
105-
iter_obj = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
94+
vfs = MP_STATE_VM(vfs_mount_table);
95+
while (vfs != NULL) {
96+
if (vfs->len == 1) {
97+
break;
98+
}
99+
vfs = vfs->next;
100+
}
101+
path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
106102
}
107103

104+
mp_obj_t iter_obj = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
105+
108106
mp_obj_t dir_list = mp_obj_new_list(0, NULL);
109107
mp_obj_t next;
110108
while ((next = mp_iternext(iter_obj)) != MP_OBJ_STOP_ITERATION) {

shared-module/sdcardio/SDCard.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) {
294294
return NULL;
295295
}
296296

297-
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) {
297+
mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) {
298298
self->bus = bus;
299299
common_hal_digitalio_digitalinout_construct(&self->cs, cs);
300300
common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL);
@@ -309,10 +309,19 @@ void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi
309309

310310
if (result != NULL) {
311311
common_hal_digitalio_digitalinout_deinit(&self->cs);
312-
mp_raise_OSError_msg(result);
312+
return result;
313313
}
314314

315315
self->baudrate = baudrate;
316+
return NULL;
317+
}
318+
319+
320+
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) {
321+
mp_rom_error_text_t result = sdcardio_sdcard_construct(self, bus, cs, baudrate);
322+
if (result != NULL) {
323+
mp_raise_OSError_msg(result);
324+
}
316325
}
317326

318327
void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) {

shared-module/sdcardio/SDCard.h

+2
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ typedef struct {
2424
uint32_t next_block;
2525
bool in_cmd25;
2626
} sdcardio_sdcard_obj_t;
27+
28+
mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate);

shared-module/sdcardio/__init__.c

+115
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,118 @@
33
// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC
44
//
55
// SPDX-License-Identifier: MIT
6+
7+
#include "shared-module/sdcardio/__init__.h"
8+
9+
#include "extmod/vfs_fat.h"
10+
11+
#include "shared-bindings/busio/SPI.h"
12+
#include "shared-bindings/digitalio/DigitalInOut.h"
13+
#include "shared-bindings/sdcardio/SDCard.h"
14+
15+
#include "supervisor/filesystem.h"
16+
17+
#ifdef DEFAULT_SD_CARD_DETECT
18+
static digitalio_digitalinout_obj_t sd_card_detect_pin;
19+
static sdcardio_sdcard_obj_t sdcard;
20+
21+
static mp_vfs_mount_t _sdcard_vfs;
22+
fs_user_mount_t _sdcard_usermount;
23+
24+
static bool _init_error = false;
25+
static bool _mounted = false;
26+
27+
#ifdef DEFAULT_SD_MOSI
28+
static busio_spi_obj_t busio_spi_obj;
29+
#endif
30+
#endif
31+
32+
void sdcardio_init(void) {
33+
#ifdef DEFAULT_SD_CARD_DETECT
34+
sd_card_detect_pin.base.type = &digitalio_digitalinout_type;
35+
common_hal_digitalio_digitalinout_construct(&sd_card_detect_pin, DEFAULT_SD_CARD_DETECT);
36+
common_hal_digitalio_digitalinout_switch_to_input(&sd_card_detect_pin, PULL_UP);
37+
common_hal_digitalio_digitalinout_never_reset(&sd_card_detect_pin);
38+
#endif
39+
}
40+
41+
void automount_sd_card(void) {
42+
#ifdef DEFAULT_SD_CARD_DETECT
43+
if (common_hal_digitalio_digitalinout_get_value(&sd_card_detect_pin) != DEFAULT_SD_CARD_INSERTED) {
44+
// No card.
45+
_init_error = false;
46+
if (_mounted) {
47+
// Unmount the card.
48+
mp_vfs_mount_t *cur = MP_STATE_VM(vfs_mount_table);
49+
if (cur == &_sdcard_vfs) {
50+
MP_STATE_VM(vfs_mount_table) = cur->next;
51+
} else {
52+
while (cur->next != &_sdcard_vfs && cur != NULL) {
53+
cur = cur->next;
54+
}
55+
if (cur != NULL) {
56+
cur->next = _sdcard_vfs.next;
57+
}
58+
}
59+
_sdcard_vfs.next = NULL;
60+
61+
#ifdef DEFAULT_SD_MOSI
62+
common_hal_busio_spi_deinit(&busio_spi_obj);
63+
#endif
64+
_mounted = false;
65+
}
66+
return;
67+
} else if (_init_error || _mounted) {
68+
// We've already tried and failed to init the card. Don't try again.
69+
return;
70+
}
71+
72+
busio_spi_obj_t *spi_obj;
73+
#ifndef DEFAULT_SD_MOSI
74+
spi_obj = MP_OBJ_TO_PTR(common_hal_board_create_spi(0));
75+
#else
76+
spi_obj = &busio_spi_obj;
77+
spi_obj->base.type = &busio_spi_type;
78+
common_hal_busio_spi_construct(spi_obj, DEFAULT_SD_SCK, DEFAULT_SD_MOSI, DEFAULT_SD_MISO, false);
79+
common_hal_busio_spi_never_reset(spi_obj);
80+
#endif
81+
sdcard.base.type = &sdcardio_SDCard_type;
82+
mp_rom_error_text_t error = sdcardio_sdcard_construct(&sdcard, spi_obj, DEFAULT_SD_CS, 25000000);
83+
if (error != NULL) {
84+
// Failed to communicate with the card.
85+
_init_error = true;
86+
}
87+
common_hal_digitalio_digitalinout_never_reset(&sdcard.cs);
88+
89+
fs_user_mount_t *vfs = &_sdcard_usermount;
90+
vfs->base.type = &mp_fat_vfs_type;
91+
vfs->fatfs.drv = vfs;
92+
93+
// Initialise underlying block device
94+
vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE
95+
mp_vfs_blockdev_init(&vfs->blockdev, &sdcard);
96+
97+
// mount the block device so the VFS methods can be used
98+
FRESULT res = f_mount(&vfs->fatfs);
99+
if (res != FR_OK) {
100+
_mounted = false;
101+
_init_error = true;
102+
common_hal_sdcardio_sdcard_deinit(&sdcard);
103+
#ifdef DEFAULT_SD_MOSI
104+
common_hal_busio_spi_deinit(spi_obj);
105+
#endif
106+
return;
107+
}
108+
109+
filesystem_set_concurrent_write_protection(vfs, true);
110+
filesystem_set_writable_by_usb(vfs, false);
111+
112+
mp_vfs_mount_t *sdcard_vfs = &_sdcard_vfs;
113+
sdcard_vfs->str = "/sd";
114+
sdcard_vfs->len = 3;
115+
sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount);
116+
sdcard_vfs->next = MP_STATE_VM(vfs_mount_table);
117+
MP_STATE_VM(vfs_mount_table) = sdcard_vfs;
118+
_mounted = true;
119+
#endif
120+
}

shared-module/sdcardio/__init__.h

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
// SPDX-License-Identifier: MIT
66

77
#pragma once
8+
9+
void sdcardio_init(void);
10+
void automount_sd_card(void);

shared-module/storage/__init__.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,23 @@ mp_obj_t common_hal_storage_getmount(const char *mount_path) {
174174
}
175175

176176
void common_hal_storage_remount(const char *mount_path, bool readonly, bool disable_concurrent_write_protection) {
177-
if (strcmp(mount_path, "/") != 0) {
177+
const char *path_under_mount;
178+
fs_user_mount_t *fs_usermount = filesystem_for_path(mount_path, &path_under_mount);
179+
if (path_under_mount[0] != 0 && strcmp(mount_path, "/") != 0) {
178180
mp_raise_OSError(MP_EINVAL);
179181
}
180182

181183
#if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_MSC
182-
if (!usb_msc_ejected() && storage_usb_is_enabled) {
183-
mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot remount '/' when visible via USB."));
184+
if (!blockdev_lock(fs_usermount)) {
185+
mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot remount path when visible via USB."));
184186
}
185187
#endif
186188

187-
filesystem_set_internal_writable_by_usb(readonly);
188-
filesystem_set_internal_concurrent_write_protection(!disable_concurrent_write_protection);
189+
filesystem_set_writable_by_usb(fs_usermount, readonly);
190+
filesystem_set_concurrent_write_protection(fs_usermount, !disable_concurrent_write_protection);
191+
blockdev_unlock(fs_usermount);
192+
193+
usb_msc_remount(fs_usermount);
189194
}
190195

191196
void common_hal_storage_erase_filesystem(bool extended) {

supervisor/shared/filesystem.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
#include "supervisor/flash.h"
1616
#include "supervisor/linker.h"
1717

18+
#if CIRCUITPY_SDCARDIO
19+
#include "shared-module/sdcardio/__init__.h"
20+
#endif
21+
1822
static mp_vfs_mount_t _circuitpy_vfs;
1923
static fs_user_mount_t _circuitpy_usermount;
2024

@@ -214,6 +218,10 @@ bool filesystem_init(bool create_allowed, bool force_create) {
214218
supervisor_flash_update_extended();
215219
#endif
216220

221+
#if CIRCUITPY_SDCARDIO
222+
sdcardio_init();
223+
#endif
224+
217225
return true;
218226
}
219227

@@ -288,7 +296,7 @@ fs_user_mount_t *filesystem_for_path(const char *path_in, const char **path_unde
288296
// because otherwise the path will be adjusted by os.getcwd() when it's looked up.
289297
if (strlen(vfs->str) != 1) {
290298
// Remove the mount point directory name, such as "/sd".
291-
path_under_mount += strlen(vfs->str);
299+
*path_under_mount += strlen(vfs->str);
292300
}
293301
}
294302
return fs_mount;

0 commit comments

Comments
 (0)