diff --git a/README.md b/README.md
index b943d0c..2b61a31 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
## grblHAL ##
-Latest build date is 20241208, see the [changelog](changelog.md) for details.
+Latest build date is 20241219, see the [changelog](changelog.md) for details.
> [!NOTE]
-> A settings reset will be performed on an update of builds prior to 20241217. Backup and restore of settings is recommended.
+> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
> [!NOTE]
> Build 20240222 has moved the probe input to the ioPorts pool of inputs and will be allocated from it when configured.
@@ -93,4 +93,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are
Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
---
-20241217
+20241219
diff --git a/changelog.md b/changelog.md
index 3c43501..d626aaa 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,26 @@
## grblHAL changelog
+Build 20241219
+
+Core:
+
+* Added directory \(mounts\) listing capabilities to the default root file system when no real root file system \(SD card\) is mounted.
+
+Boards:
+
+* ESP32, iMXRT1062, RP2040: added option to mount littlefs as root filesystem when SD card is not enabled.
+Ref. RP2040 issue [#103](https://github.com/grblHAL/RP2040/issues/103).
+
+* STM32F3xx: added serial port to `$pins` report.
+
+Plugins:
+
+* WebUI, Networking and SD card: updated to handle littlefs mounted as root filesystem.
+
+---
+
+## grblHAL changelog
+
Build 20241217
Core:
diff --git a/driver_opts.h b/driver_opts.h
index a724b23..6ae061c 100644
--- a/driver_opts.h
+++ b/driver_opts.h
@@ -531,7 +531,7 @@
#endif
#ifndef FTP_ENABLE
#define FTP_ENABLE 0
-#elif !SDCARD_ENABLE
+#elif !(SDCARD_ENABLE || LITTLEFS_ENABLE)
#undef FTP_ENABLE
#define FTP_ENABLE 0
#endif
@@ -648,6 +648,19 @@
#define SDCARD_ENABLE 0
#endif
+#if LITTLEFS_ENABLE == 2 && SDCARD_ENABLE
+#undef LITTLEFS_ENABLE
+#define LITTLEFS_ENABLE 1
+#endif
+
+#if LITTLEFS_ENABLE
+#if LITTLEFS_ENABLE == 2
+#define LITTLEFS_MOUNT_DIR "/"
+#else
+#define LITTLEFS_MOUNT_DIR "/littlefs"
+#endif
+#endif
+
#ifndef SPI_ENABLE
#if SDCARD_ENABLE || TRINAMIC_SPI_ENABLE
#define SPI_ENABLE 1
diff --git a/grbl.h b/grbl.h
index dfb2cf2..91f55e8 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20241217
+#define GRBL_BUILD 20241219
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/ngc_flowctrl.c b/ngc_flowctrl.c
index 12abdf0..a7e8985 100644
--- a/ngc_flowctrl.c
+++ b/ngc_flowctrl.c
@@ -669,7 +669,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
if((subname = ngc_string_param_get((ngc_string_id_t)o_label))) {
char filename[60];
vfs_file_t *file;
-#if LITTLEFS_ENABLE
+#if LITTLEFS_ENABLE == 1
sprintf(filename, "/littlefs/%s.macro", subname);
if((file = stream_redirect_read(filename, onNamedSubError, onNamedSubEOF)) == NULL) {
diff --git a/stream_file.c b/stream_file.c
index e1eb9bd..391fa39 100644
--- a/stream_file.c
+++ b/stream_file.c
@@ -62,7 +62,7 @@ static int16_t stream_read_file (void)
return ASCII_LF; // Return a linefeed if the last character was not a linefeed.
}
} else
- c = SERIAL_NO_DATA; // TODO: close all streams?
+ return SERIAL_NO_DATA; // TODO: close all streams?
return (int16_t)c;
}
@@ -120,6 +120,11 @@ static void onReportHandlersInit (void)
grbl.report.status_message = trap_status_messages;
}
+bool stream_is_file (void)
+{
+ return hal.stream.type == StreamType_File;
+}
+
vfs_file_t *stream_redirect_read (char *filename, status_message_ptr status_handler, on_file_end_ptr eof_handler)
{
static bool error_handler_ok = false;
diff --git a/stream_file.h b/stream_file.h
index 9776d5b..11d04ec 100644
--- a/stream_file.h
+++ b/stream_file.h
@@ -24,5 +24,6 @@
#include "vfs.h"
#include "core_handlers.h"
+bool stream_is_file (void);
void stream_redirect_close (vfs_file_t *file);
vfs_file_t *stream_redirect_read (char *filename, status_message_ptr status_handler, on_file_end_ptr eof_handler);
diff --git a/vfs.c b/vfs.c
index a55754c..dd8fe87 100644
--- a/vfs.c
+++ b/vfs.c
@@ -31,6 +31,28 @@
#undef feof
#endif
+static vfs_mount_t *get_rootfs (void);
+
+static inline vfs_mount_t *path_is_mount_dir (const char *path)
+{
+ size_t plen = strlen(path);
+
+ vfs_mount_t *mount = get_rootfs();
+
+ if(*path == '/' && plen == 1)
+ return mount;
+
+ size_t mlen;
+
+ if((mount = mount->next)) do {
+ mlen = strlen(mount->path) - 1;
+ if(mlen == plen && !strncmp(mount->path, path, mlen))
+ break;
+ } while((mount = mount->next));
+
+ return mount;
+}
+
// NULL file system
static vfs_file_t *fs_open (const char *filename, const char *mode)
@@ -79,7 +101,38 @@ static int fs_dirop (const char *path)
static vfs_dir_t *fs_opendir (const char *path)
{
- return NULL;
+ static vfs_dir_t *dir = NULL;
+
+ if(dir == NULL)
+ dir = calloc(sizeof(vfs_dir_t) + 3, 1);
+
+ if(dir) {
+ vfs_mount_t **mount = (vfs_mount_t **)&dir->handle;
+ *mount = get_rootfs()->next;
+ }
+
+ return !strcmp(path, "/") ? dir : NULL;
+}
+
+static char *fs_readdir (vfs_dir_t *dir, vfs_dirent_t *dirent)
+{
+ vfs_mount_t **mount = (vfs_mount_t **)&dir->handle;
+
+ vfs_errno = 0;
+
+ if(*mount) {
+ strcpy(dirent->name, (*mount)->path);
+ *(strchr(dirent->name, '\0') - 1) = '\0';
+ dirent->st_mode = (*mount)->mode;
+ dirent->st_mode.directory = true;
+ while((*mount = (*mount)->next)) {
+ if(!(*mount)->mode.hidden)
+ break;
+ }
+ } else
+ *dirent->name = '\0';
+
+ return *dirent->name ? dirent->name : NULL;
}
static void fs_closedir (vfs_dir_t *dir)
@@ -88,6 +141,22 @@ static void fs_closedir (vfs_dir_t *dir)
static int fs_stat (const char *filename, vfs_stat_t *st)
{
+ char path[64];
+ vfs_mount_t *mount;
+
+ if((mount = path_is_mount_dir(strcat(strcpy(path, "/"), filename)))) {
+
+ st->st_size = 1024;
+ st->st_mode = mount->mode;
+ st->st_mode.directory = true;
+#if ESP_PLATFORM
+ st->st_mtim = (time_t)0;
+#else
+ st->st_mtime = (time_t)0;
+#endif
+ return 0;
+ }
+
return -1;
}
@@ -114,6 +183,7 @@ static const vfs_t fs_null = {
.fchdir = fs_chdir,
.frmdir = fs_dirop,
.fopendir = fs_opendir,
+ .readdir = fs_readdir,
.fclosedir = fs_closedir,
.fstat = fs_stat,
.fgetcwd = fs_getcwd
@@ -133,6 +203,11 @@ static char cwd[100] = "/";
int vfs_errno = 0;
vfs_events_t vfs = {0};
+static vfs_mount_t *get_rootfs (void)
+{
+ return &root;
+}
+
// Strip trailing directory separator, FatFS dont't like it (WinSCP adds it)
char *vfs_fixpath (char *path)
{
@@ -143,25 +218,6 @@ char *vfs_fixpath (char *path)
return path;
}
-static inline vfs_mount_t *path_is_mount_dir (const char *path)
-{
- size_t plen = strlen(path);
-
- if(*path == '/' && plen == 1)
- return &root;
-
- size_t mlen;
- vfs_mount_t *mount = root.next;
-
- if(mount) do {
- mlen = strlen(mount->path) - 1;
- if(mlen == plen && !strncmp(mount->path, path, mlen))
- break;
- } while((mount = mount->next));
-
- return mount;
-}
-
static vfs_mount_t *get_mount (const char *path)
{
vfs_errno = 0;
@@ -324,7 +380,7 @@ int vfs_chdir (const char *path)
vfs_errno = 0;
- if(*path != '/') {
+ if(*path != '/' && strcmp(cwd, "/")) {
if(strcmp(path, "..")) {
if(strlen(cwd) > 1)
strcat(cwd, "/");
@@ -336,24 +392,24 @@ int vfs_chdir (const char *path)
}
} else {
- strcpy(cwd, path);
+ if(*path == '/')
+ strcpy(cwd, path);
+ else
+ strcat(strcpy(cwd, "/"), path);
- if((cwdmount = get_mount(path)) && strchr(path + 1, '/') == NULL && cwdmount != &root) {
+ vfs_fixpath(cwd);
+
+ if((cwdmount = get_mount(cwd)) && strchr(cwd + 1, '/') == NULL && cwdmount != &root) {
strcpy(cwd, cwdmount->path);
- char *s;
- if((s = strrchr(cwd, '/')))
- *s = '\0';
+ vfs_fixpath(cwd);
return 0;
}
}
- if((ret = cwdmount ? cwdmount->vfs->fchdir(path) : -1) != 0) { // + strlen(mount->path));))
- char *s = strrchr(cwd, '/');
- if(s)
- *s = '\0';
- }
+ if((ret = cwdmount ? cwdmount->vfs->fchdir(path) : -1) != 0) // + strlen(mount->path));))
+ vfs_fixpath(cwd);
return ret;
}