Improved handling of current working directory (CWD).

This commit is contained in:
Terje Io
2026-03-11 18:03:29 +01:00
parent ee077d8730
commit 38cc54d74e
8 changed files with 215 additions and 123 deletions

View File

@@ -1,6 +1,6 @@
## grblHAL ## ## grblHAL ##
Latest build date is 20260308, see the [changelog](changelog.md) for details. Latest build date is 20260311, see the [changelog](changelog.md) for details.
> [!NOTE] > [!NOTE]
> A settings reset will be performed on an update of builds prior to 20241208. 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.
@@ -89,4 +89,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. Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
--- ---
20260218 20260311

View File

@@ -1,5 +1,26 @@
## grblHAL changelog ## grblHAL changelog
<a name="20260311">Build 20260311
Core:
* Improved handling of current working directory \(CWD\).
Drivers:
* STM32F4xx: added support for additional aux input ports. Changed IRQ priority for motor fault inputs.
Plugins:
* SD card: added `$PWD` command for outputting current working directory and `$CWD=<directory>` as an alternative to `$F=<directory>` to set current working directory.
`<directory>` can be `..` for up one level, `/` for the root directory, a single directory name or a path to a directory - either relative to CWD or absolute.
* Spindle: delayed on actions on soft reset till after reset is cleared.
* Networking, ftp: improved handling of current working directory. Note that this is not the same as maintained by the core.
---
<a name="20260308">Build 20260308 <a name="20260308">Build 20260308
Core: Core:

2
grbl.h
View File

@@ -42,7 +42,7 @@
#else #else
#define GRBL_VERSION "1.1f" #define GRBL_VERSION "1.1f"
#endif #endif
#define GRBL_BUILD 20260308 #define GRBL_BUILD 20260311
#define GRBL_URL "https://github.com/grblHAL" #define GRBL_URL "https://github.com/grblHAL"

170
grbllib.c
View File

@@ -90,13 +90,19 @@ DCRAM system_t sys; //!< System global variable structure.
DCRAM grbl_t grbl; DCRAM grbl_t grbl;
DCRAM grbl_hal_t hal; DCRAM grbl_hal_t hal;
DCRAM static core_task_t task_pool[CORE_TASK_POOL_SIZE];
static driver_startup_t driver = { .ok = 0xFF }; static driver_startup_t driver = { .ok = 0xFF };
static core_task_t *next_task = NULL, *immediate_task = NULL, *on_booted = NULL, *systick_task = NULL, *last_freed = NULL;
static on_linestate_changed_ptr on_linestate_changed; static on_linestate_changed_ptr on_linestate_changed;
static settings_changed_ptr hal_settings_changed; static settings_changed_ptr hal_settings_changed;
static stepper_enable_ptr stepper_enable; static stepper_enable_ptr stepper_enable;
DCRAM static struct {
volatile core_task_t *immediate; //!< Pointer to first entry of linked list of tasks to run immediately.
volatile core_task_t *delayed; //!< Pointer to first entry of linked list of delayed tasks to run, in execution order.
volatile core_task_t *systick; //!< Pointer to first entry of linked list of systick (1 ms) tasks to run.
volatile core_task_t *on_booted; //!< Pointer to first entry of linked list of tasks to run once on cold boot.
volatile core_task_t *on_reset; //!< Pointer to first entry of linked list of tasks to on soft reset.
volatile core_task_t *last_freed; //!< Pointer to last freed task.
core_task_t pool[CORE_TASK_POOL_SIZE];
} tasks;
#ifdef KINEMATICS_API #ifdef KINEMATICS_API
kinematics_t kinematics; kinematics_t kinematics;
#endif #endif
@@ -110,8 +116,8 @@ __attribute__((always_inline)) static inline void task_free (core_task_t *task)
{ {
task->fn = NULL; task->fn = NULL;
task->next = NULL; task->next = NULL;
if(last_freed == NULL) if(tasks.last_freed == NULL)
last_freed = task; tasks.last_freed = task;
} }
__attribute__((always_inline)) static inline core_task_t *task_run (core_task_t *task) __attribute__((always_inline)) static inline core_task_t *task_run (core_task_t *task)
@@ -220,14 +226,23 @@ FLASHMEM static void stepperEnable (axes_signals_t enable, bool hold)
sys.steppers_enabled = /*!hold &&*/ enable.bits == AXES_BITMASK; sys.steppers_enabled = /*!hold &&*/ enable.bits == AXES_BITMASK;
} }
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
#endif
FLASHMEM static void print_pos_msg (void *data) FLASHMEM static void print_pos_msg (void *data)
{ {
hal.stream.write("grblHAL: power on self-test (POS) failed!" ASCII_EOL); hal.stream.write("grblHAL: power on self-test (POS) failed!" ASCII_EOL);
if(on_booted) do { if(tasks.on_booted) do {
} while((on_booted = task_run(on_booted))); } while((tasks.on_booted = task_run(tasks.on_booted)));
} }
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
FLASHMEM static void onPosFailure (serial_linestate_t state) FLASHMEM static void onPosFailure (serial_linestate_t state)
{ {
if(state.dtr) // delay a bit to let the USB stack come up if(state.dtr) // delay a bit to let the USB stack come up
@@ -275,7 +290,7 @@ FLASHMEM int grbl_enter (void)
bool looping = true; bool looping = true;
memset(&sys, 0, sizeof(system_t)); memset(&sys, 0, sizeof(system_t));
memset(&task_pool, 0, sizeof(task_pool)); memset(&tasks, 0, sizeof(tasks));
// Clear all and set some core function pointers // Clear all and set some core function pointers
memset(&grbl, 0, sizeof(grbl_t)); memset(&grbl, 0, sizeof(grbl_t));
@@ -354,9 +369,9 @@ FLASHMEM int grbl_enter (void)
polar_init(); polar_init();
#endif #endif
#if NVSDATA_BUFFER_ENABLE #if NVSDATA_BUFFER_ENABLE
nvs_buffer_init(); nvs_buffer_init();
#endif #endif
settings_init(); // Load settings from non-volatile storage settings_init(); // Load settings from non-volatile storage
memset(sys.position, 0, sizeof(sys.position)); // Clear machine position. memset(sys.position, 0, sizeof(sys.position)); // Clear machine position.
@@ -520,6 +535,9 @@ FLASHMEM int grbl_enter (void)
if(hal.driver_cap.mpg_mode) if(hal.driver_cap.mpg_mode)
protocol_enqueue_realtime_command(sys.mpg_mode ? CMD_STATUS_REPORT_ALL : CMD_STATUS_REPORT); protocol_enqueue_realtime_command(sys.mpg_mode ? CMD_STATUS_REPORT_ALL : CMD_STATUS_REPORT);
if(tasks.on_reset)
system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any on reset tasks
// Start main loop. Processes program inputs and executes them. // Start main loop. Processes program inputs and executes them.
if(!(looping = protocol_main_loop())) if(!(looping = protocol_main_loop()))
looping = hal.driver_release == NULL || hal.driver_release(); looping = hal.driver_release == NULL || hal.driver_release();
@@ -532,17 +550,22 @@ FLASHMEM int grbl_enter (void)
return 0; return 0;
} }
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
#endif
__attribute__((always_inline)) static inline core_task_t *task_alloc (void) __attribute__((always_inline)) static inline core_task_t *task_alloc (void)
{ {
core_task_t *task = NULL; core_task_t *task = NULL;
uint_fast8_t idx = CORE_TASK_POOL_SIZE; uint_fast8_t idx = CORE_TASK_POOL_SIZE;
if(last_freed) { if(tasks.last_freed) {
task = last_freed; task = tasks.last_freed;
last_freed = NULL; tasks.last_freed = NULL;
} else do { } else do {
if(task_pool[--idx].fn == NULL) if(tasks.pool[--idx].fn == NULL)
task = &task_pool[idx]; task = &tasks.pool[idx];
} while(task == NULL && idx); } while(task == NULL && idx);
return task; return task;
@@ -560,11 +583,11 @@ static void task_execute (sys_state_t state)
lock = true; lock = true;
if(immediate_task && sys.driver_started) { if(tasks.immediate && sys.driver_started) {
hal.irq_disable(); hal.irq_disable();
if((task = immediate_task)) if((task = tasks.immediate))
immediate_task = NULL; tasks.immediate = NULL;
hal.irq_enable(); hal.irq_enable();
if(task) do { if(task) do {
@@ -572,23 +595,23 @@ static void task_execute (sys_state_t state)
} }
uint32_t now = hal.get_elapsed_ticks(); uint32_t now = hal.get_elapsed_ticks();
if(!(now == last_ms || next_task == systick_task)) { if(!(now == last_ms || tasks.delayed == tasks.systick)) {
last_ms = now; last_ms = now;
if((task = systick_task)) do { if((task = tasks.systick)) do {
task->fn(task->data); task->fn(task->data);
} while((task = task->next)); } while((task = task->next));
while((task = next_task) && (int32_t)(task->time - now) <= 0) { while((task = tasks.delayed) && (int32_t)(task->time - now) <= 0) {
hal.irq_disable(); hal.irq_disable();
if(task == next_task) if(task == tasks.delayed)
next_task = task->next; tasks.delayed = task->next;
else { else {
core_task_t *t; core_task_t *t;
if((t = next_task)) { if((t = tasks.delayed)) {
while(t->next && t->next != task) while(t->next && t->next != task)
t = t->next; t = t->next;
if(t->next && t->next == task) if(t->next && t->next == task)
@@ -622,13 +645,13 @@ ISR_CODE bool ISR_FUNC(task_add_delayed)(foreground_task_ptr fn, void *data, uin
task->data = data; task->data = data;
task->next = NULL; task->next = NULL;
if(next_task == NULL) if(tasks.delayed == NULL)
next_task = task; tasks.delayed = task;
else if((int32_t)(task->time - next_task->time) <= 0) { else if((int32_t)(task->time - tasks.delayed->time) <= 0) {
task->next = next_task; task->next = tasks.delayed;
next_task = task; tasks.delayed = task;
} else { } else {
core_task_t *t = next_task; core_task_t *t = tasks.delayed;
while(t) { while(t) {
if(t->next == NULL || (int32_t)(task->time - t->next->time) < 0) { if(t->next == NULL || (int32_t)(task->time - t->next->time) < 0) {
task->next = t->next; task->next = t->next;
@@ -651,12 +674,12 @@ ISR_CODE void task_delete (foreground_task_ptr fn, void *data)
hal.irq_disable(); hal.irq_disable();
if((task = next_task)) do { if((task = tasks.delayed)) do {
if(fn == task->fn && (data == NULL || data == task->data)) { if(fn == task->fn && (data == NULL || data == task->data)) {
if(prev) if(prev)
prev->next = task->next; prev->next = task->next;
else else
next_task = task->next; tasks.delayed = task->next;
task_free(task); task_free(task);
break; break;
} }
@@ -678,10 +701,10 @@ ISR_CODE bool ISR_FUNC(task_add_systick)(foreground_task_ptr fn, void *data)
task->data = data; task->data = data;
task->next = NULL; task->next = NULL;
if(systick_task == NULL) if(tasks.systick == NULL)
systick_task = task; tasks.systick = task;
else { else {
core_task_t *t = systick_task; core_task_t *t = tasks.systick;
while(t->next) while(t->next)
t = t->next; t = t->next;
t->next = task; t->next = task;
@@ -699,12 +722,12 @@ FLASHMEM void task_delete_systick (foreground_task_ptr fn, void *data)
hal.irq_disable(); hal.irq_disable();
if((task = systick_task)) do { if((task = tasks.systick)) do {
if(fn == task->fn && data == task->data) { if(fn == task->fn && data == task->data) {
if(prev) if(prev)
prev->next = task->next; prev->next = task->next;
else else
systick_task = task->next; tasks.systick = task->next;
task_free(task); task_free(task);
break; break;
} }
@@ -731,10 +754,10 @@ ISR_CODE bool ISR_FUNC(task_add_immediate)(foreground_task_ptr fn, void *data)
task->data = data; task->data = data;
task->next = NULL; task->next = NULL;
if(immediate_task == NULL) if(tasks.immediate == NULL)
immediate_task = task; tasks.immediate = task;
else { else {
core_task_t *t = immediate_task; core_task_t *t = tasks.immediate;
while(t->next) while(t->next)
t = t->next; t = t->next;
t->next = task; t->next = task;
@@ -746,6 +769,42 @@ ISR_CODE bool ISR_FUNC(task_add_immediate)(foreground_task_ptr fn, void *data)
return task != NULL; return task != NULL;
} }
/*! \brief Enqueue a function to be called once by the foreground process after the reset sequence is completed.
\param fn pointer to a \a foreground_task_ptr type of function.
\param data pointer to data to be passed to the callee.
\returns true if successful, false otherwise.
*/
ISR_CODE bool ISR_FUNC(task_run_on_reset)(foreground_task_ptr fn, void *data)
{
core_task_t *task = NULL;
if(!sys.cold_start) {
hal.irq_disable();
if(fn && (task = task_alloc())) {
task->fn = fn;
task->data = data;
task->next = NULL;
if(tasks.on_reset == NULL)
tasks.on_reset = task;
else {
core_task_t *t = tasks.on_reset;
while(t->next)
t = t->next;
t->next = task;
}
}
hal.irq_enable();
}
return task != NULL;
}
/*! \brief Enqueue a function to be called once by the foreground process after the boot sequence is completed. /*! \brief Enqueue a function to be called once by the foreground process after the boot sequence is completed.
\param fn pointer to a \a foreground_task_ptr type of function. \param fn pointer to a \a foreground_task_ptr type of function.
\param data pointer to data to be passed to the callee. \param data pointer to data to be passed to the callee.
@@ -765,10 +824,10 @@ ISR_CODE bool ISR_FUNC(task_run_on_startup)(foreground_task_ptr fn, void *data)
task->data = data; task->data = data;
task->next = NULL; task->next = NULL;
if(on_booted == NULL) if(tasks.on_booted == NULL)
on_booted = task; tasks.on_booted = task;
else { else {
core_task_t *t = on_booted; core_task_t *t = tasks.on_booted;
while(t->next) while(t->next)
t = t->next; t = t->next;
t->next = task; t->next = task;
@@ -792,28 +851,31 @@ FLASHMEM void task_execute_on_startup (void)
core_task_t *task, *prev = NULL; core_task_t *task, *prev = NULL;
if((task = on_booted)) do { if((task = tasks.on_booted)) do {
if(!(task->fn == report_warning)) { if(!(task->fn == report_warning)) {
if(prev) if(prev)
prev->next = task->next; prev->next = task->next;
else { else {
prev = NULL; prev = NULL;
on_booted = task->next; tasks.on_booted = task->next;
} }
task_free(task); task_free(task);
} else } else
prev = task; prev = task;
} while((task = prev ? prev->next : on_booted)); } while((task = prev ? prev->next : tasks.on_booted));
while(next_task) while(tasks.delayed)
task_delete(next_task->fn, NULL); task_delete(tasks.delayed->fn, NULL);
while(systick_task) while(tasks.systick)
task_delete_systick(systick_task->fn, NULL); task_delete_systick(tasks.systick->fn, NULL);
} }
if(on_booted && (sys.driver_started || !hal.stream.state.linestate_event)) do { if(tasks.on_booted && (sys.driver_started || !hal.stream.state.linestate_event)) do {
} while((on_booted = task_run(on_booted))); } while((tasks.on_booted = task_run(tasks.on_booted)));
if(tasks.on_reset) do {
} while((tasks.on_reset = task_run(tasks.on_reset)));
if(!sys.driver_started) { if(!sys.driver_started) {
@@ -825,6 +887,10 @@ FLASHMEM void task_execute_on_startup (void)
} }
} }
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
FLASHMEM void task_raise_alarm (void *data) FLASHMEM void task_raise_alarm (void *data)
{ {
system_raise_alarm((alarm_code_t)data); system_raise_alarm((alarm_code_t)data);

View File

@@ -264,7 +264,7 @@ static bool modbus_send_rtu (modbus_message_t *msg, const modbus_callbacks_t *ca
while(spin_lock); while(spin_lock);
if(block) { if((block &= !sys.blocking_event)) {
if(is_blocking) if(is_blocking)
return false; return false;

View File

@@ -98,6 +98,7 @@ typedef struct {
float rpm_high_limit; float rpm_high_limit;
float angular_position; //!< Number of revolutions since last reset float angular_position; //!< Number of revolutions since last reset
float rpm_programmed; float rpm_programmed;
bool ccw;
uint32_t index_count; uint32_t index_count;
uint32_t pulse_count; uint32_t pulse_count;
uint32_t error_count; uint32_t error_count;

1
task.h
View File

@@ -26,6 +26,7 @@ typedef void (*foreground_task_ptr)(void *data);
bool task_add_immediate (foreground_task_ptr fn, void *data); bool task_add_immediate (foreground_task_ptr fn, void *data);
bool task_add_delayed (foreground_task_ptr fn, void *data, uint32_t delay_ms); bool task_add_delayed (foreground_task_ptr fn, void *data, uint32_t delay_ms);
bool task_run_on_reset (foreground_task_ptr fn, void *data);
bool task_run_on_startup (foreground_task_ptr fn, void *data); bool task_run_on_startup (foreground_task_ptr fn, void *data);
void task_delete (foreground_task_ptr fn, void *data); void task_delete (foreground_task_ptr fn, void *data);
bool task_add_systick (foreground_task_ptr fn, void *data); bool task_add_systick (foreground_task_ptr fn, void *data);

137
vfs.c
View File

@@ -226,6 +226,43 @@ char *vfs_fixpath (char *path)
return path; return path;
} }
static const char *parse_path (const char *path)
{
static char *abspath;
static size_t maxlen = 0;
if(strlen(cwd) + strlen(path) + 2 > maxlen) {
maxlen = max(VFS_CWD_LENGTH, strlen(cwd) + strlen(path) + 2);
abspath = realloc(abspath, maxlen);
}
if(abspath) {
char newpath[VFS_CWD_LENGTH];
strcpy(newpath, path);
strcpy(abspath, *path == '/' ? "/" : cwd);
char *p, *el = strtok(newpath, "/");
while(el) {
if(!strcmp("..", el)) {
if((p = strrchr(abspath, '/')))
*(p + (p == abspath ? 1 : 0)) = '\0';
} else if(*el && strcmp(el, ".")) {
if(strlen(abspath) == 1)
strcat(abspath, el);
else
strcat(strcat(abspath, "/"), el);
}
el = strtok(NULL, "/");
}
} else
maxlen = 0;
return abspath ? (const char *)abspath : path;
}
static vfs_mount_t *get_mount (const char *path) static vfs_mount_t *get_mount (const char *path)
{ {
vfs_errno = 0; vfs_errno = 0;
@@ -384,44 +421,26 @@ int vfs_rmdir (const char *path)
int vfs_chdir (const char *path) int vfs_chdir (const char *path)
{ {
int ret; int ret = -1;
char *p; vfs_mount_t *mount;
vfs_errno = 0; vfs_errno = 0;
path = parse_path(path);
if(!strcmp("..", path) && strcmp("/", (path = cwd)) && (p = strrchr(cwd, '/'))) if((mount = get_mount(path))) {
*(p + (p == cwd ? 1 : 0)) = '\0'; if(mount->vfs->fchdir)
ret = mount->vfs->fchdir(get_filename(mount, path));
if(*path != '/' && strcmp(cwd, "/")) { else {
if(strcmp(path, "..")) { vfs_stat_t st;
if(strlen(cwd) > 1) if(!((ret = mount->vfs->fstat(get_filename(mount, path), &st)) == 0 && st.st_mode.directory))
strcat(cwd, "/"); ret = -1;
strcat(cwd, path);
} else {
char *s = strrchr(cwd, '/');
if(s)
*s = '\0';
}
} else {
if(*path == '/')
strcpy(cwd, path);
else
strcat(strcpy(cwd, "/"), path);
vfs_fixpath(cwd);
if((cwdmount = get_mount(cwd)) && strchr(cwd + 1, '/') == NULL && cwdmount != &root) {
strcpy(cwd, cwdmount->path);
vfs_fixpath(cwd);
return 0;
} }
} }
if((ret = cwdmount ? cwdmount->vfs->fchdir(path) : -1) != 0) // + strlen(mount->path));)) if(ret == 0) {
vfs_fixpath(cwd); strcpy(cwd, path);
cwdmount = mount;
}
return ret; return ret;
} }
@@ -498,16 +517,21 @@ void vfs_closedir (vfs_dir_t *dir)
char *vfs_getcwd (char *buf, size_t len) char *vfs_getcwd (char *buf, size_t len)
{ {
char *cwds = cwdmount->vfs->fgetcwd ? cwdmount->vfs->fgetcwd(NULL, len) : cwd; char *cwds = cwdmount->vfs->fgetcwd ? cwdmount->vfs->fgetcwd(NULL, len) : cwd;
size_t cwdlen = strlen(cwdmount->path) + strlen(cwds) + 2;
vfs_errno = 0; vfs_errno = 0;
if(buf == NULL) if(buf == NULL) {
buf = (char *)malloc(strlen(cwds) + 1); len = cwdlen + 1;
buf = (char *)malloc(cwdlen + 1);
}
if(buf) if(buf && cwdlen < len)
strcpy(buf, cwds); strcat(strcpy(buf, cwdmount->path), cwds + 1);
return buf ? buf : cwds; vfs_fixpath(buf);
return cwdlen < len ? buf : NULL;
} }
int vfs_chmod (const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask) int vfs_chmod (const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask)
@@ -519,39 +543,11 @@ int vfs_chmod (const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask)
int vfs_stat (const char *filename, vfs_stat_t *st) int vfs_stat (const char *filename, vfs_stat_t *st)
{ {
char tmp[VFS_CWD_LENGTH], *p; filename = parse_path(filename);
if(!strcmp("..", filename)) {
strcpy(tmp, cwd);
if((p = strrchr(tmp, '/')))
*(p + (p == tmp ? 1 : 0)) = '\0';
filename = tmp;
}
vfs_mount_t *mount = get_mount(filename); vfs_mount_t *mount = get_mount(filename);
int ret = mount ? mount->vfs->fstat(get_filename(mount, filename), st) : -1; return mount ? mount->vfs->fstat(get_filename(mount, filename), st) : -1;
if(ret == -1 && (!strcmp("/", filename) || (strchr(filename, '/') == NULL && !strcmp("/", cwd)))) {
strcat(cwd, filename);
mount = get_mount(cwd);
cwd[1] = '\0';
if(mount) {
st->st_size = 0;
st->st_mode.mode = 0;
st->st_mode.directory = true;
#if defined(ESP_PLATFORM)
st->st_mtim = mount->st_mtim;
#else
st->st_mtime = mount->st_mtime;
#endif
ret = 0;
}
}
return ret;
} }
int vfs_utime (const char *filename, struct tm *modified) int vfs_utime (const char *filename, struct tm *modified)
@@ -634,11 +630,18 @@ bool vfs_unmount (const char *path)
if(!strcmp(path, "/")) { if(!strcmp(path, "/")) {
root.vfs = &fs_null; root.vfs = &fs_null;
root.mode = (vfs_st_mode_t){ .directory = true, .read_only = true, .hidden = true }; root.mode = (vfs_st_mode_t){ .directory = true, .read_only = true, .hidden = true };
if(cwdmount == &root)
strcpy(cwd, "/");
} else { } else {
vfs_mount_t *mount = get_mount(path); vfs_mount_t *mount = get_mount(path);
if(mount) { if(mount) {
if(mount == cwdmount) {
cwdmount = &root;
strcpy(cwd, "/");
}
vfs_mount_t *pmount = &root; vfs_mount_t *pmount = &root;
while(pmount->next && pmount->next != mount) while(pmount->next && pmount->next != mount)