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 ##
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]
> 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.
---
20260218
20260311

View File

@@ -1,5 +1,26 @@
## 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
Core:

2
grbl.h
View File

@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20260308
#define GRBL_BUILD 20260311
#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_hal_t hal;
DCRAM static core_task_t task_pool[CORE_TASK_POOL_SIZE];
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 settings_changed_ptr hal_settings_changed;
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
kinematics_t kinematics;
#endif
@@ -110,8 +116,8 @@ __attribute__((always_inline)) static inline void task_free (core_task_t *task)
{
task->fn = NULL;
task->next = NULL;
if(last_freed == NULL)
last_freed = task;
if(tasks.last_freed == NULL)
tasks.last_freed = 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;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
#endif
FLASHMEM static void print_pos_msg (void *data)
{
hal.stream.write("grblHAL: power on self-test (POS) failed!" ASCII_EOL);
if(on_booted) do {
} while((on_booted = task_run(on_booted)));
if(tasks.on_booted) do {
} while((tasks.on_booted = task_run(tasks.on_booted)));
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
FLASHMEM static void onPosFailure (serial_linestate_t state)
{
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;
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
memset(&grbl, 0, sizeof(grbl_t));
@@ -354,9 +369,9 @@ FLASHMEM int grbl_enter (void)
polar_init();
#endif
#if NVSDATA_BUFFER_ENABLE
#if NVSDATA_BUFFER_ENABLE
nvs_buffer_init();
#endif
#endif
settings_init(); // Load settings from non-volatile storage
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)
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.
if(!(looping = protocol_main_loop()))
looping = hal.driver_release == NULL || hal.driver_release();
@@ -532,17 +550,22 @@ FLASHMEM int grbl_enter (void)
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)
{
core_task_t *task = NULL;
uint_fast8_t idx = CORE_TASK_POOL_SIZE;
if(last_freed) {
task = last_freed;
last_freed = NULL;
if(tasks.last_freed) {
task = tasks.last_freed;
tasks.last_freed = NULL;
} else do {
if(task_pool[--idx].fn == NULL)
task = &task_pool[idx];
if(tasks.pool[--idx].fn == NULL)
task = &tasks.pool[idx];
} while(task == NULL && idx);
return task;
@@ -560,11 +583,11 @@ static void task_execute (sys_state_t state)
lock = true;
if(immediate_task && sys.driver_started) {
if(tasks.immediate && sys.driver_started) {
hal.irq_disable();
if((task = immediate_task))
immediate_task = NULL;
if((task = tasks.immediate))
tasks.immediate = NULL;
hal.irq_enable();
if(task) do {
@@ -572,23 +595,23 @@ static void task_execute (sys_state_t state)
}
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;
if((task = systick_task)) do {
if((task = tasks.systick)) do {
task->fn(task->data);
} 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();
if(task == next_task)
next_task = task->next;
if(task == tasks.delayed)
tasks.delayed = task->next;
else {
core_task_t *t;
if((t = next_task)) {
if((t = tasks.delayed)) {
while(t->next && t->next != task)
t = t->next;
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->next = NULL;
if(next_task == NULL)
next_task = task;
else if((int32_t)(task->time - next_task->time) <= 0) {
task->next = next_task;
next_task = task;
if(tasks.delayed == NULL)
tasks.delayed = task;
else if((int32_t)(task->time - tasks.delayed->time) <= 0) {
task->next = tasks.delayed;
tasks.delayed = task;
} else {
core_task_t *t = next_task;
core_task_t *t = tasks.delayed;
while(t) {
if(t->next == NULL || (int32_t)(task->time - t->next->time) < 0) {
task->next = t->next;
@@ -651,12 +674,12 @@ ISR_CODE void task_delete (foreground_task_ptr fn, void *data)
hal.irq_disable();
if((task = next_task)) do {
if((task = tasks.delayed)) do {
if(fn == task->fn && (data == NULL || data == task->data)) {
if(prev)
prev->next = task->next;
else
next_task = task->next;
tasks.delayed = task->next;
task_free(task);
break;
}
@@ -678,10 +701,10 @@ ISR_CODE bool ISR_FUNC(task_add_systick)(foreground_task_ptr fn, void *data)
task->data = data;
task->next = NULL;
if(systick_task == NULL)
systick_task = task;
if(tasks.systick == NULL)
tasks.systick = task;
else {
core_task_t *t = systick_task;
core_task_t *t = tasks.systick;
while(t->next)
t = t->next;
t->next = task;
@@ -699,12 +722,12 @@ FLASHMEM void task_delete_systick (foreground_task_ptr fn, void *data)
hal.irq_disable();
if((task = systick_task)) do {
if((task = tasks.systick)) do {
if(fn == task->fn && data == task->data) {
if(prev)
prev->next = task->next;
else
systick_task = task->next;
tasks.systick = task->next;
task_free(task);
break;
}
@@ -731,10 +754,10 @@ ISR_CODE bool ISR_FUNC(task_add_immediate)(foreground_task_ptr fn, void *data)
task->data = data;
task->next = NULL;
if(immediate_task == NULL)
immediate_task = task;
if(tasks.immediate == NULL)
tasks.immediate = task;
else {
core_task_t *t = immediate_task;
core_task_t *t = tasks.immediate;
while(t->next)
t = t->next;
t->next = task;
@@ -746,6 +769,42 @@ ISR_CODE bool ISR_FUNC(task_add_immediate)(foreground_task_ptr fn, void *data)
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.
\param fn pointer to a \a foreground_task_ptr type of function.
\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->next = NULL;
if(on_booted == NULL)
on_booted = task;
if(tasks.on_booted == NULL)
tasks.on_booted = task;
else {
core_task_t *t = on_booted;
core_task_t *t = tasks.on_booted;
while(t->next)
t = t->next;
t->next = task;
@@ -792,28 +851,31 @@ FLASHMEM void task_execute_on_startup (void)
core_task_t *task, *prev = NULL;
if((task = on_booted)) do {
if((task = tasks.on_booted)) do {
if(!(task->fn == report_warning)) {
if(prev)
prev->next = task->next;
else {
prev = NULL;
on_booted = task->next;
tasks.on_booted = task->next;
}
task_free(task);
} else
prev = task;
} while((task = prev ? prev->next : on_booted));
} while((task = prev ? prev->next : tasks.on_booted));
while(next_task)
task_delete(next_task->fn, NULL);
while(tasks.delayed)
task_delete(tasks.delayed->fn, NULL);
while(systick_task)
task_delete_systick(systick_task->fn, NULL);
while(tasks.systick)
task_delete_systick(tasks.systick->fn, NULL);
}
if(on_booted && (sys.driver_started || !hal.stream.state.linestate_event)) do {
} while((on_booted = task_run(on_booted)));
if(tasks.on_booted && (sys.driver_started || !hal.stream.state.linestate_event)) do {
} 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) {
@@ -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)
{
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);
if(block) {
if((block &= !sys.blocking_event)) {
if(is_blocking)
return false;

View File

@@ -98,6 +98,7 @@ typedef struct {
float rpm_high_limit;
float angular_position; //!< Number of revolutions since last reset
float rpm_programmed;
bool ccw;
uint32_t index_count;
uint32_t pulse_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_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);
void task_delete (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;
}
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)
{
vfs_errno = 0;
@@ -384,44 +421,26 @@ int vfs_rmdir (const char *path)
int vfs_chdir (const char *path)
{
int ret;
char *p;
int ret = -1;
vfs_mount_t *mount;
vfs_errno = 0;
path = parse_path(path);
if(!strcmp("..", path) && strcmp("/", (path = cwd)) && (p = strrchr(cwd, '/')))
*(p + (p == cwd ? 1 : 0)) = '\0';
if(*path != '/' && strcmp(cwd, "/")) {
if(strcmp(path, "..")) {
if(strlen(cwd) > 1)
strcat(cwd, "/");
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((mount = get_mount(path))) {
if(mount->vfs->fchdir)
ret = mount->vfs->fchdir(get_filename(mount, path));
else {
vfs_stat_t st;
if(!((ret = mount->vfs->fstat(get_filename(mount, path), &st)) == 0 && st.st_mode.directory))
ret = -1;
}
}
if((ret = cwdmount ? cwdmount->vfs->fchdir(path) : -1) != 0) // + strlen(mount->path));))
vfs_fixpath(cwd);
if(ret == 0) {
strcpy(cwd, path);
cwdmount = mount;
}
return ret;
}
@@ -498,16 +517,21 @@ void vfs_closedir (vfs_dir_t *dir)
char *vfs_getcwd (char *buf, size_t len)
{
char *cwds = cwdmount->vfs->fgetcwd ? cwdmount->vfs->fgetcwd(NULL, len) : cwd;
size_t cwdlen = strlen(cwdmount->path) + strlen(cwds) + 2;
vfs_errno = 0;
if(buf == NULL)
buf = (char *)malloc(strlen(cwds) + 1);
if(buf == NULL) {
len = cwdlen + 1;
buf = (char *)malloc(cwdlen + 1);
}
if(buf)
strcpy(buf, cwds);
if(buf && cwdlen < len)
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)
@@ -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)
{
char tmp[VFS_CWD_LENGTH], *p;
if(!strcmp("..", filename)) {
strcpy(tmp, cwd);
if((p = strrchr(tmp, '/')))
*(p + (p == tmp ? 1 : 0)) = '\0';
filename = tmp;
}
filename = parse_path(filename);
vfs_mount_t *mount = get_mount(filename);
int ret = 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;
return mount ? mount->vfs->fstat(get_filename(mount, filename), st) : -1;
}
int vfs_utime (const char *filename, struct tm *modified)
@@ -634,11 +630,18 @@ bool vfs_unmount (const char *path)
if(!strcmp(path, "/")) {
root.vfs = &fs_null;
root.mode = (vfs_st_mode_t){ .directory = true, .read_only = true, .hidden = true };
if(cwdmount == &root)
strcpy(cwd, "/");
} else {
vfs_mount_t *mount = get_mount(path);
if(mount) {
if(mount == cwdmount) {
cwdmount = &root;
strcpy(cwd, "/");
}
vfs_mount_t *pmount = &root;
while(pmount->next && pmount->next != mount)