1515#include "supervisor/flash.h"
1616#include "supervisor/linker.h"
1717
18- static mp_vfs_mount_t _mp_vfs ;
19- static fs_user_mount_t _internal_vfs ;
18+ static mp_vfs_mount_t _circuitpy_vfs ;
19+ static fs_user_mount_t _circuitpy_usermount ;
20+
21+ #if CIRCUITPY_SAVES_PARTITION_SIZE > 0
22+ static mp_vfs_mount_t _saves_vfs ;
23+ static fs_user_mount_t _saves_usermount ;
24+ #endif
2025
2126static volatile uint32_t filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS ;
2227volatile bool filesystem_flush_requested = false;
@@ -73,23 +78,37 @@ static void make_file_with_contents(FATFS *fatfs, const char *filename, const by
7378// want it to be executed without using stack within main() function
7479bool filesystem_init (bool create_allowed , bool force_create ) {
7580 // init the vfs object
76- fs_user_mount_t * vfs_fat = & _internal_vfs ;
77- vfs_fat -> blockdev .flags = 0 ;
78- supervisor_flash_init_vfs (vfs_fat );
81+ fs_user_mount_t * circuitpy = & _circuitpy_usermount ;
82+ circuitpy -> blockdev .flags = 0 ;
83+ supervisor_flash_init_vfs (circuitpy );
84+
85+ #if CIRCUITPY_SAVES_PARTITION_SIZE > 0
86+ // SAVES is placed before CIRCUITPY so that CIRCUITPY takes up the remaining space.
87+ circuitpy -> blockdev .offset = CIRCUITPY_SAVES_PARTITION_SIZE ;
88+ circuitpy -> blockdev .size = -1 ;
89+
90+ fs_user_mount_t * saves = & _saves_usermount ;
91+ saves -> blockdev .flags = 0 ;
92+ saves -> blockdev .offset = 0 ;
93+ saves -> blockdev .size = CIRCUITPY_SAVES_PARTITION_SIZE ;
94+ supervisor_flash_init_vfs (saves );
95+ filesystem_set_concurrent_write_protection (saves , true);
96+ filesystem_set_writable_by_usb (saves , false);
97+ #endif
7998
80- mp_vfs_mount_t * vfs = & _mp_vfs ;
81- vfs -> len = 0 ;
99+ mp_vfs_mount_t * circuitpy_vfs = & _circuitpy_vfs ;
100+ circuitpy_vfs -> len = 0 ;
82101
83102 // try to mount the flash
84- FRESULT res = f_mount (& vfs_fat -> fatfs );
103+ FRESULT res = f_mount (& circuitpy -> fatfs );
85104 if ((res == FR_NO_FILESYSTEM && create_allowed ) || force_create ) {
86105 // No filesystem so create a fresh one, or reformat has been requested.
87106 uint8_t working_buf [FF_MAX_SS ];
88107 BYTE formats = FM_FAT ;
89108 #if FF_FS_EXFAT
90109 formats |= FM_EXFAT | FM_FAT32 ;
91110 #endif
92- res = f_mkfs (& vfs_fat -> fatfs , formats , 0 , working_buf , sizeof (working_buf ));
111+ res = f_mkfs (& circuitpy -> fatfs , formats , 0 , working_buf , sizeof (working_buf ));
93112 if (res != FR_OK ) {
94113 return false;
95114 }
@@ -98,46 +117,66 @@ bool filesystem_init(bool create_allowed, bool force_create) {
98117
99118 // set label
100119 #ifdef CIRCUITPY_DRIVE_LABEL
101- res = f_setlabel (& vfs_fat -> fatfs , CIRCUITPY_DRIVE_LABEL );
120+ res = f_setlabel (& circuitpy -> fatfs , CIRCUITPY_DRIVE_LABEL );
102121 #else
103- res = f_setlabel (& vfs_fat -> fatfs , "CIRCUITPY" );
122+ res = f_setlabel (& circuitpy -> fatfs , "CIRCUITPY" );
104123 #endif
105124 if (res != FR_OK ) {
106125 return false;
107126 }
108127
109128 #if CIRCUITPY_USB_DEVICE
110129 // inhibit file indexing on MacOS
111- res = f_mkdir (& vfs_fat -> fatfs , "/.fseventsd" );
130+ res = f_mkdir (& circuitpy -> fatfs , "/.fseventsd" );
112131 if (res != FR_OK ) {
113132 return false;
114133 }
115- make_empty_file (& vfs_fat -> fatfs , "/.fseventsd/no_log" );
116- make_empty_file (& vfs_fat -> fatfs , "/.metadata_never_index" );
134+ make_empty_file (& circuitpy -> fatfs , "/.fseventsd/no_log" );
135+ make_empty_file (& circuitpy -> fatfs , "/.metadata_never_index" );
117136
118137 // Prevent storing trash on all OSes.
119- make_empty_file (& vfs_fat -> fatfs , "/.Trashes" ); // MacOS
120- make_empty_file (& vfs_fat -> fatfs , "/.Trash-1000" ); // Linux, XDG trash spec:
138+ make_empty_file (& circuitpy -> fatfs , "/.Trashes" ); // MacOS
139+ make_empty_file (& circuitpy -> fatfs , "/.Trash-1000" ); // Linux, XDG trash spec:
121140 // https://specifications.freedesktop.org/trash-spec/trashspec-latest.html
122141 #endif
123142
124143 #if CIRCUITPY_SDCARDIO || CIRCUITPY_SDIOIO
125- res = f_mkdir (& vfs_fat -> fatfs , "/sd" );
144+ res = f_mkdir (& circuitpy -> fatfs , "/sd" );
126145 #if CIRCUITPY_FULL_BUILD
127- MAKE_FILE_WITH_OPTIONAL_CONTENTS (& vfs_fat -> fatfs , "/sd/placeholder.txt" ,
146+ MAKE_FILE_WITH_OPTIONAL_CONTENTS (& circuitpy -> fatfs , "/sd/placeholder.txt" ,
128147 "SD cards mounted at /sd will hide this file from Python."
129148 " SD cards are not visible via USB CIRCUITPY.\n" );
130149 #endif
131150 #endif
132151
152+ #if CIRCUITPY_SAVES_PARTITION_SIZE > 0
153+ res = f_mkfs (& saves -> fatfs , formats , 0 , working_buf , sizeof (working_buf ));
154+ if (res == FR_OK ) {
155+ // Flush the new file system to make sure it's repaired immediately.
156+ supervisor_flash_flush ();
157+ res = f_setlabel (& saves -> fatfs , "CPSAVES" );
158+ }
159+
160+ if (res == FR_OK ) {
161+ res = f_mkdir (& circuitpy -> fatfs , "/saves" );
162+ }
163+ #if CIRCUITPY_FULL_BUILD
164+ if (res == FR_OK ) {
165+ MAKE_FILE_WITH_OPTIONAL_CONTENTS (& circuitpy -> fatfs , "/saves/placeholder.txt" ,
166+ "A separate filesystem mounted at /saves will hide this file from Python."
167+ " Saves are visible via USB CPSAVES.\n" );
168+ }
169+ #endif
170+ #endif
171+
133172 #if CIRCUITPY_OS_GETENV
134- make_empty_file (& vfs_fat -> fatfs , "/settings.toml" );
173+ make_empty_file (& circuitpy -> fatfs , "/settings.toml" );
135174 #endif
136175 // make a sample code.py file
137- MAKE_FILE_WITH_OPTIONAL_CONTENTS (& vfs_fat -> fatfs , "/code.py" , "print(\"Hello World!\")\n" );
176+ MAKE_FILE_WITH_OPTIONAL_CONTENTS (& circuitpy -> fatfs , "/code.py" , "print(\"Hello World!\")\n" );
138177
139178 // create empty lib directory
140- res = f_mkdir (& vfs_fat -> fatfs , "/lib" );
179+ res = f_mkdir (& circuitpy -> fatfs , "/lib" );
141180 if (res != FR_OK ) {
142181 return false;
143182 }
@@ -148,16 +187,28 @@ bool filesystem_init(bool create_allowed, bool force_create) {
148187 return false;
149188 }
150189
151- vfs -> str = "/" ;
152- vfs -> len = 1 ;
153- vfs -> obj = MP_OBJ_FROM_PTR (vfs_fat );
154- vfs -> next = NULL ;
155-
156- MP_STATE_VM (vfs_mount_table ) = vfs ;
190+ circuitpy_vfs -> str = "/" ;
191+ circuitpy_vfs -> len = 1 ;
192+ circuitpy_vfs -> obj = MP_OBJ_FROM_PTR (circuitpy );
193+ circuitpy_vfs -> next = NULL ;
194+
195+ MP_STATE_VM (vfs_mount_table ) = circuitpy_vfs ;
196+
197+ #if CIRCUITPY_SAVES_PARTITION_SIZE > 0
198+ res = f_mount (& saves -> fatfs );
199+ if (res == FR_OK ) {
200+ mp_vfs_mount_t * saves_vfs = & _saves_vfs ;
201+ saves_vfs -> str = "/saves" ;
202+ saves_vfs -> len = 6 ;
203+ saves_vfs -> obj = MP_OBJ_FROM_PTR (& _saves_usermount );
204+ saves_vfs -> next = MP_STATE_VM (vfs_mount_table );
205+ MP_STATE_VM (vfs_mount_table ) = saves_vfs ;
206+ }
207+ #endif
157208
158209 // The current directory is used as the boot up directory.
159210 // It is set to the internal flash filesystem by default.
160- MP_STATE_PORT (vfs_cur ) = vfs ;
211+ MP_STATE_PORT (vfs_cur ) = circuitpy_vfs ;
161212
162213 #if CIRCUITPY_STORAGE_EXTEND
163214 supervisor_flash_update_extended ();
@@ -175,7 +226,7 @@ void PLACE_IN_ITCM(filesystem_flush)(void) {
175226}
176227
177228void filesystem_set_internal_writable_by_usb (bool writable ) {
178- fs_user_mount_t * vfs = & _internal_vfs ;
229+ fs_user_mount_t * vfs = & _circuitpy_usermount ;
179230
180231 filesystem_set_writable_by_usb (vfs , writable );
181232}
@@ -199,7 +250,7 @@ bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) {
199250}
200251
201252void filesystem_set_internal_concurrent_write_protection (bool concurrent_write_protection ) {
202- filesystem_set_concurrent_write_protection (& _internal_vfs , concurrent_write_protection );
253+ filesystem_set_concurrent_write_protection (& _circuitpy_usermount , concurrent_write_protection );
203254}
204255
205256void filesystem_set_concurrent_write_protection (fs_user_mount_t * vfs , bool concurrent_write_protection ) {
@@ -211,14 +262,14 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu
211262}
212263
213264bool filesystem_present (void ) {
214- return _mp_vfs .len > 0 ;
265+ return _circuitpy_vfs .len > 0 ;
215266}
216267
217268fs_user_mount_t * filesystem_circuitpy (void ) {
218269 if (!filesystem_present ()) {
219270 return NULL ;
220271 }
221- return & _internal_vfs ;
272+ return & _circuitpy_usermount ;
222273}
223274
224275fs_user_mount_t * filesystem_for_path (const char * path_in , const char * * path_under_mount ) {
0 commit comments