From 38cc54d74e740ca7b60dc5d778e83fc5cfefdc71 Mon Sep 17 00:00:00 2001 From: Terje Io Date: Wed, 11 Mar 2026 18:03:29 +0100 Subject: [PATCH] Improved handling of current working directory (CWD). --- README.md | 4 +- changelog.md | 21 ++++++ grbl.h | 2 +- grbllib.c | 170 ++++++++++++++++++++++++++++++++-------------- modbus_rtu.c | 2 +- spindle_control.h | 1 + task.h | 1 + vfs.c | 137 +++++++++++++++++++------------------ 8 files changed, 215 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index f4084a8..d45abcf 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/changelog.md b/changelog.md index ee81c83..bc6f8ae 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,26 @@ ## grblHAL changelog +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=` as an alternative to `$F=` to set current working 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. + +--- + Build 20260308 Core: diff --git a/grbl.h b/grbl.h index 3116b14..b70f47e 100644 --- a/grbl.h +++ b/grbl.h @@ -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" diff --git a/grbllib.c b/grbllib.c index 4e0a997..bc881b1 100644 --- a/grbllib.c +++ b/grbllib.c @@ -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); diff --git a/modbus_rtu.c b/modbus_rtu.c index 105056b..bbf3897 100644 --- a/modbus_rtu.c +++ b/modbus_rtu.c @@ -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; diff --git a/spindle_control.h b/spindle_control.h index fde5cc6..0662c30 100644 --- a/spindle_control.h +++ b/spindle_control.h @@ -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; diff --git a/task.h b/task.h index 4018264..6a19b11 100644 --- a/task.h +++ b/task.h @@ -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); diff --git a/vfs.c b/vfs.c index 9f8c2ea..344fab9 100644 --- a/vfs.c +++ b/vfs.c @@ -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)