"hardened" task deregistration code and changed signature of setting available and ioport_find_free() calls.

Some low-level ioport improvements, added definition for virtual pins/ports.
Deprecated direct access to number of the different ioport ports in hal.port struct, use ioports_unclaimed() instead.
This commit is contained in:
Terje Io
2025-01-14 17:13:56 +01:00
parent c681661516
commit efe4245a06
15 changed files with 184 additions and 94 deletions

View File

@@ -1,5 +1,31 @@
## grblHAL changelog
<a name="20250114">Build 20250114
Core:
* "hardened" task deregistration code and changed signature of setting available and `ioport_find_free()` calls.
* Some low-level ioport improvements, added definition for virtual pins/ports.
Deprecated direct access to number of the different ioport ports in `hal.port` struct, use `ioports_unclaimed()` instead.
Drivers:
* ESP32: some fixes for Trinamic SPI comms.
Plugins:
* Many: updated for setting call signature changes and to take advandage of latest core functionality.
Plugins claiming auxiliary ports changed to use `-1` for port number settings that are to be ignored/not claimed.
* Plasma: refactored, uses new task functionality for processing and has improved settings handling. Virtual auxiliary ports disabled for now.
Templates:
* Many: updated to take advandage of latest core functionality etc. Removed some superfluous ones.
---
<a name="20250111">Build 20250111
Core:

View File

@@ -213,6 +213,7 @@ typedef enum {
Input_QEI_B,
Input_QEI_Select,
Input_QEI_Index,
Virtual_Pin,
// Single pin bidirectional peripherals
Bidirectional_MotorUARTX,
Bidirectional = Bidirectional_MotorUARTX,
@@ -421,6 +422,7 @@ PROGMEM static const pin_name_t pin_names[] = {
{ .function = Input_QEI_B, .name = "QEI B" },
{ .function = Input_QEI_Select, .name = "QEI select" },
{ .function = Input_QEI_Index, .name = "QEI index" },
{ .function = Virtual_Pin, .name = "Virtual" },
{ .function = Bidirectional_MotorUARTX, .name = "UART X" },
{ .function = Bidirectional_MotorUARTY, .name = "UART Y" },
{ .function = Bidirectional_MotorUARTZ, .name = "UART Z" },
@@ -458,6 +460,7 @@ typedef enum {
PinGroup_CAN,
PinGroup_LED,
PinGroup_Home,
PinGroup_Virtual,
// Interrupt capable pins that may have debounce processing enabled
PinGroup_Control = (1<<8),
PinGroup_Limit = (1<<9),

View File

@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2017-2024 Terje Io
Copyright (c) 2017-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -87,6 +87,7 @@ PROGMEM static const status_detail_t status_detail[] = {
#if COMPATIBILITY_LEVEL <= 1
{ Status_GCodeCoordSystemLocked, "Coordinate system is locked." },
#endif
{ Status_UnexpectedDemarcation, "Unexpected file demarcation." },
#if NGC_EXPRESSIONS_ENABLE
{ Status_ExpressionUknownOp, "Unknown operation found in expression." },
{ Status_ExpressionDivideByZero, "Divide by zero in expression attempted." },

View File

@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2017-2024 Terje Io
Copyright (c) 2017-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -86,6 +86,7 @@ typedef enum {
Status_GcodeInvalidRetractPosition = 54,
Status_IllegalHomingConfiguration = 55,
Status_GCodeCoordSystemLocked = 56,
Status_UnexpectedDemarcation = 57,
// Some error codes as defined in bdring's ESP32 port
Status_SDMountError = 60,

44
gcode.c
View File

@@ -798,34 +798,42 @@ status_code_t gc_execute_block (char *block)
tool_id_t t;
} single_meaning_value = {0};
block = gc_normalize_block(block, &status, &message);
bool fs_changed;
if((fs_changed = gc_state.file_stream ? hal.stream.file == NULL : hal.stream.file != NULL))
gc_state.file_stream = hal.stream.file != NULL;
block = gc_normalize_block(block, &status, &message);
if(status != Status_OK)
FAIL(status);
// Determine if the line is a program start/end marker.
if(block[0] == CMD_PROGRAM_DEMARCATION && block[1] == '\0') {
block[0] = '\0';
if(gc_state.file_stream) {
if(!fs_changed && !gc_state.file_run)
status = Status_UnexpectedDemarcation;
else if(!(gc_state.file_run = fs_changed)) {
protocol_buffer_synchronize(); // Empty planner buffer
grbl.report.feedback_message(Message_ProgramEnd);
if(grbl.on_program_completed)
grbl.on_program_completed(ProgramFlow_EndPercent, state_get() == STATE_CHECK_MODE);
}
} else
gc_state.file_run = !gc_state.file_run;
if(status == Status_OK && grbl.on_file_demarcate)
grbl.on_file_demarcate(gc_state.file_run);
}
if(block[0] == '\0') {
if(message)
gc_output_message(message);
return status;
}
// Determine if the line is a program start/end marker.
// Old comment from protocol.c:
// NOTE: This maybe installed to tell grblHAL when a program is running vs manual input,
// where, during a program, the system auto-cycle start will continue to execute
// everything until the next '%' sign. This will help fix resuming issues with certain
// functions that empty the planner buffer to execute its task on-time.
if (block[0] == CMD_PROGRAM_DEMARCATION && block[1] == '\0') {
gc_state.file_run = !gc_state.file_run;
if(message)
gc_output_message(message);
if(grbl.on_file_demarcate)
grbl.on_file_demarcate(gc_state.file_run);
return Status_OK;
}
/* -------------------------------------------------------------------------------------
STEP 1: Initialize parser block struct and copy current g-code state modes. The parser
updates these modes and commands as the block line is parsed and will only be used and

View File

@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2017-2024 Terje Io
Copyright (c) 2017-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -207,7 +207,8 @@ typedef enum {
ProgramFlow_CompletedM2 = 2, //!< 2 - M2
ProgramFlow_CompletedM30 = 30, //!< 30 - M30
ProgramFlow_CompletedM60 = 60, //!< 60 - M60
ProgramFlow_Return = 99 //!< 99 - M99
ProgramFlow_Return = 99, //!< 99 - M99
ProgramFlow_EndPercent = 255 //!< 255 - %
} program_flow_t;
// Modal Group M9: Override control
@@ -625,6 +626,7 @@ typedef struct {
uint32_t g43_pending; //!< Tool offset to be selected on next M6, for macro ATC
#endif
bool file_run; //!< Tracks % command
bool file_stream; //!< Tracks streaming from file
bool is_laser_ppi_mode;
bool is_rpm_rate_adjusted;
bool tool_change;

2
grbl.h
View File

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

View File

@@ -420,7 +420,7 @@ int grbl_enter (void)
return 0;
}
static inline core_task_t *task_alloc (void)
__attribute__((always_inline)) static inline core_task_t *task_alloc (void)
{
core_task_t *task = NULL;
uint_fast8_t idx = CORE_TASK_POOL_SIZE;
@@ -436,7 +436,7 @@ static inline core_task_t *task_alloc (void)
return task;
}
static inline void task_free (core_task_t *task)
__attribute__((always_inline)) static inline void task_free (core_task_t *task)
{
task->fn = NULL;
if(last_freed == NULL)
@@ -525,6 +525,8 @@ void task_delete (foreground_task_ptr fn, void *data)
{
core_task_t *task, *prev = NULL;
hal.irq_disable();
if((task = next_task)) do {
if(fn == task->fn && data == task->data) {
if(prev)
@@ -536,6 +538,8 @@ void task_delete (foreground_task_ptr fn, void *data)
}
prev = task;
} while((task = task->next));
hal.irq_enable();
}
ISR_CODE bool ISR_FUNC(task_add_systick)(foreground_task_ptr fn, void *data)
@@ -569,6 +573,8 @@ void task_delete_systick (foreground_task_ptr fn, void *data)
{
core_task_t *task, *prev = NULL;
hal.irq_disable();
if((task = systick_task)) do {
if(fn == task->fn && data == task->data) {
if(prev)
@@ -580,6 +586,8 @@ void task_delete_systick (foreground_task_ptr fn, void *data)
}
prev = task;
} while((task = task->next));
hal.irq_enable();
}
/*! \brief Enqueue a function to be called once by the foreground process.

105
ioports.c
View File

@@ -96,39 +96,57 @@ uint8_t ioports_available (io_port_type_t type, io_port_direction_t dir)
return ports;
}
/*! \brief Get number of unclaimed digital or analog ports available.
\param type as an \a #io_port_type_t enum value.
\param dir as an \a #io_port_direction_t enum value.
\returns number of ports available.
*/
uint8_t ioports_unclaimed (io_port_type_t type, io_port_direction_t dir)
{
xbar_t *port;
uint8_t idx = 0, n_ports = 0;
if(hal.port.get_pin_info) do {
if((port = hal.port.get_pin_info(type, dir, idx++)) && !port->mode.claimed)
n_ports++;
} while(port);
return n_ports;
}
static struct ff_data {
uint8_t port;
const char *description;
} ff_data;
static bool match_port (xbar_t *properties, uint8_t port, void *data)
{
if(((struct ff_data *)data)->description && (!properties->description || strcmp(properties->description, ((struct ff_data *)data)->description)))
return false;
((struct ff_data *)data)->port = port;
return true;
}
/*! \brief find first free or claimed digital or analog port.
\param type as an \a #io_port_type_t enum value.
\param dir as an \a #io_port_direction_t enum value.
\param description pointer to a \a char constant for the pin description of a previousely claimed port or NULL if searching for the first free port.
\returns the port number if successful, 255 if not.
\returns the port number if successful, 0xFF (255) if not.
*/
uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, const char *description)
uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, pin_cap_t filter, const char *description)
{
uint8_t port;
bool found = false;
xbar_t *pin;
ff_data.port = 0xFF;
ff_data.description = (description && *description) ? description : NULL;
if(description) {
port = ioports_available(type, dir);
do {
if((pin = hal.port.get_pin_info(type, dir, --port))) {
if((found = pin->description && !strcmp(pin->description, description)))
port = pin->id;
}
} while(port && !found);
// TODO: pass modified filter with .claimable off when looking for description match?
if(ff_data.description && !ioports_enumerate(type, dir, (pin_cap_t){}, match_port, (void *)&ff_data)) {
ff_data.description = NULL;
ioports_enumerate(type, dir, filter, match_port, (void *)&ff_data);
}
if(!found) {
port = ioports_available(type, dir);
do {
if((pin = hal.port.get_pin_info(type, dir, --port))) {
if((found = !pin->mode.claimed))
port = pin->id;
}
} while(port && !found);
}
return found ? port : 255;
return ff_data.port;
}
/*! \brief Return information about a digital or analog port.
@@ -170,15 +188,24 @@ bool ioport_claim (io_port_type_t type, io_port_direction_t dir, uint8_t *port,
ok = (portinfo = ioport_get_info(type, dir, *port)) && !portinfo->mode.claimed && hal.port.claim(type, dir, port, description);
} else if((ok = ioports_available(type, dir) > 0)) {
} else {
if(type == Port_Digital)
*port = dir == Port_Input ? --hal.port.num_digital_in : --hal.port.num_digital_out;
else
*port = dir == Port_Input ? --hal.port.num_analog_in : --hal.port.num_analog_out;
uint_fast8_t count;
get_pin_info_ptr get_pin_info = hal.port.get_pin_info;
if(hal.port.set_pin_description)
hal.port.set_pin_description(type, dir, *port, description);
hal.port.get_pin_info = NULL; // Force count of unclaimed ports only.
if((ok = (count = ioports_available(type, dir)) > 0 && *port == count - 1 )) {
if(type == Port_Digital)
*port = dir == Port_Input ? --hal.port.num_digital_in : --hal.port.num_digital_out;
else
*port = dir == Port_Input ? --hal.port.num_analog_in : --hal.port.num_analog_out;
if(hal.port.set_pin_description)
hal.port.set_pin_description(type, dir, *port, description);
}
hal.port.get_pin_info = get_pin_info;
}
return ok;
@@ -296,39 +323,41 @@ bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uin
if(type == Port_Digital) {
cfg = &digital;
digital_in = digital_out = -1;
if(n_in) {
ports->in.n_start = hal.port.num_digital_in;
ports->in.n_start = ioports_available(Port_Digital, Port_Input);
hal.port.num_digital_in += (ports->in.n_ports = n_in);
ports->in.map = malloc(ports->in.n_ports * sizeof(ports->in.n_ports));
digital.in.ports = &ports->in;
digital_in = -1;
}
if(n_out) {
ports->out.n_start = hal.port.num_digital_out;
ports->out.n_start = ioports_available(Port_Digital, Port_Output);
hal.port.num_digital_out += (ports->out.n_ports = n_out);
ports->out.map = malloc(ports->out.n_ports * sizeof(ports->out.n_ports));
digital.out.ports = &ports->out;
digital_out = -1;
}
} else {
cfg = &analog;
analog_in = analog_out = -1;
if(n_in) {
ports->in.n_start = hal.port.num_analog_in;
ports->in.n_start = ioports_available(Port_Analog, Port_Input);
hal.port.num_analog_in += (ports->in.n_ports = n_in);
ports->in.map = malloc(ports->in.n_ports * sizeof(ports->in.n_ports));
analog.in.ports = &ports->in;
analog_in = -1;
}
if(n_out) {
ports->out.n_start = hal.port.num_analog_out;
ports->out.n_start = ioports_available(Port_Analog, Port_Output);
hal.port.num_analog_out += (ports->out.n_ports = n_out);
ports->out.map = malloc(ports->out.n_ports * sizeof(ports->out.n_ports));
analog.out.ports = &ports->out;
analog_out = -1;
}
}
@@ -496,7 +525,7 @@ void ioport_save_output_settings (xbar_t *xbar, gpio_out_config_t *config)
settings_write_global();
}
static bool is_setting_available (const setting_detail_t *setting)
static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t offset)
{
bool available = false;

View File

@@ -109,10 +109,10 @@ typedef bool (*ioports_enumerate_callback_ptr)(xbar_t *properties, uint8_t port,
//! Properties and handlers for auxiliary digital and analog I/O.
typedef struct {
uint8_t num_digital_in; //!< Number of digital inputs available.
uint8_t num_digital_out; //!< Number of digital outputs available.
uint8_t num_analog_in; //!< Number of analog inputs available.
uint8_t num_analog_out; //!< Number of analog outputs available.
uint8_t num_digital_in; //!< Deprecated, use ioports_unclaimed() to get count.
uint8_t num_digital_out; //!< Deprecated, use ioports_unclaimed() to get count.
uint8_t num_analog_in; //!< Deprecated, use ioports_unclaimed() to get count.
uint8_t num_analog_out; //!< Deprecated, use ioports_unclaimed() to get count.
digital_out_ptr digital_out; //!< Optional handler for setting a digital output.
analog_out_ptr analog_out; //!< Optional handler for setting an analog output.
wait_on_input_ptr wait_on_input; //!< Optional handler for reading a digital or analog input.
@@ -124,10 +124,11 @@ typedef struct {
} io_port_t;
uint8_t ioports_available (io_port_type_t type, io_port_direction_t dir);
uint8_t ioports_unclaimed (io_port_type_t type, io_port_direction_t dir);
xbar_t *ioport_get_info (io_port_type_t type, io_port_direction_t dir, uint8_t port);
bool ioport_claim (io_port_type_t type, io_port_direction_t dir, uint8_t *port, const char *description);
bool ioport_can_claim_explicit (void);
uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, const char *description);
uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, pin_cap_t filter, const char *description);
bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_cap_t filter, ioports_enumerate_callback_ptr callback, void *data);
void ioport_assign_function (aux_ctrl_t *aux_ctrl, pin_function_t *function);
void ioport_assign_out_function (aux_ctrl_out_t *aux_ctrl, pin_function_t *function);

View File

@@ -502,7 +502,7 @@ void report_grbl_settings (bool all, void *data)
for(idx = 0; idx < details->n_settings; idx++) {
setting = &details->settings[idx];
if(!is_hidden(setting) && (all || setting->type == Setting_IsLegacy || setting->type == Setting_IsLegacyFn) &&
(setting->is_available == NULL ||setting->is_available(setting))) {
(setting->is_available == NULL ||setting->is_available(setting, 0))) {
*psetting++ = (setting_detail_t *)setting;
n_settings++;
}
@@ -512,7 +512,7 @@ void report_grbl_settings (bool all, void *data)
if(all && (details = details->next)) do {
for(idx = 0; idx < details->n_settings; idx++) {
setting = &details->settings[idx];
if(!setting->flags.hidden && (setting->is_available == NULL || setting->is_available(setting))) {
if(!setting->flags.hidden && (setting->is_available == NULL || setting->is_available(setting, 0))) {
*psetting++ = (setting_detail_t *)setting;
n_settings++;
}
@@ -1101,15 +1101,20 @@ void report_build_info (char *line, bool extended)
hal.stream.write("]" ASCII_EOL);
#endif
if(hal.port.num_digital_in + hal.port.num_digital_out + hal.port.num_analog_in + hal.port.num_analog_out > 0) {
uint8_t digital_in = ioports_unclaimed(Port_Digital, Port_Input),
digital_out = ioports_unclaimed(Port_Digital, Port_Output),
analog_in = ioports_unclaimed(Port_Analog, Port_Input),
analog_out = ioports_unclaimed(Port_Analog, Port_Output);
if(digital_in || digital_out || analog_in || analog_out) {
hal.stream.write("[AUX IO:");
hal.stream.write(uitoa(hal.port.num_digital_in));
hal.stream.write(uitoa(digital_in));
hal.stream.write(",");
hal.stream.write(uitoa(hal.port.num_digital_out));
hal.stream.write(uitoa(digital_out));
hal.stream.write(",");
hal.stream.write(uitoa(hal.port.num_analog_in));
hal.stream.write(uitoa(analog_in));
hal.stream.write(",");
hal.stream.write(uitoa(hal.port.num_analog_out));
hal.stream.write(uitoa(analog_out));
hal.stream.write("]" ASCII_EOL);
}
@@ -1844,7 +1849,7 @@ static bool print_sorted (const setting_detail_t *setting, uint_fast16_t offset,
static bool print_unsorted (const setting_detail_t *setting, uint_fast16_t offset, void *args)
{
if(!(((report_args_t *)args)->group == setting->group && ((report_args_t *)args)->offset != offset) &&
(setting->is_available == NULL ||setting->is_available(setting)))
(setting->is_available == NULL ||setting->is_available(setting, 0)))
report_settings_detail(((report_args_t *)args)->format, setting, offset);
return true;
@@ -1881,7 +1886,7 @@ static status_code_t print_settings_details (settings_format_t format, setting_g
do {
for(idx = 0; idx < details->n_settings; idx++) {
setting = &details->settings[idx];
if(!is_hidden(setting) && (group == Group_All || setting->group == args.group) && (setting->is_available == NULL || setting->is_available(setting))) {
if(!is_hidden(setting) && (group == Group_All || setting->group == args.group) && (setting->is_available == NULL || setting->is_available(setting, 0))) {
*psetting++ = (setting_detail_t *)setting;
n_settings++;
}

View File

@@ -1716,16 +1716,16 @@ float setting_get_float_value (const setting_detail_t *setting, uint_fast16_t of
return value;
}
static bool is_group_available (const setting_detail_t *setting)
static bool is_group_available (const setting_detail_t *setting, uint_fast16_t offset)
{
return settings_is_group_available(setting->group);
}
static bool is_setting_available (const setting_detail_t *setting)
static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t offset)
{
bool available = false;
if(setting) switch(normalize_id(setting->id)) {
if(setting) switch(setting->id) {
case Setting_GangedDirInvertMask:
available = hal.stepper.get_ganged && hal.stepper.get_ganged(false).mask != 0;
@@ -1843,7 +1843,7 @@ static bool is_setting_available (const setting_detail_t *setting)
break;
case Setting_CoolantOnDelay:
available = !hal.signals_cap.safety_door_ajar && hal.coolant_cap.mask;
available = hal.coolant_cap.mask;
break;
default:
@@ -1853,7 +1853,7 @@ static bool is_setting_available (const setting_detail_t *setting)
return available;
}
static bool toolsetter_available (const setting_detail_t *setting)
static bool toolsetter_available (const setting_detail_t *setting, uint_fast16_t offset)
{
bool available = false;
@@ -1878,7 +1878,7 @@ static bool toolsetter_available (const setting_detail_t *setting)
return available;
}
static bool no_toolsetter_available (const setting_detail_t *setting)
static bool no_toolsetter_available (const setting_detail_t *setting, uint_fast16_t offset)
{
#if COMPATIBILITY_LEVEL <= 1
@@ -2563,9 +2563,9 @@ void settings_restore (settings_restore_t restore)
nvs_buffer_sync_physical();
}
inline static bool is_available (const setting_detail_t *setting)
inline static bool is_available (const setting_detail_t *setting, uint_fast16_t offset)
{
return setting->is_available == NULL || setting->is_available(setting);
return setting->is_available == NULL || setting->is_available(setting, offset);
}
bool settings_is_group_available (setting_group_t id)
@@ -2611,7 +2611,7 @@ bool settings_is_group_available (setting_group_t id)
do {
if(details->settings) {
for(idx = 0; idx < details->n_settings; idx++) {
if(details->settings[idx].group == id && (available = is_available(&details->settings[idx])))
if(details->settings[idx].group == id && (available = is_available(&details->settings[idx], 0)))
break;
}
}
@@ -2664,7 +2664,7 @@ const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t
do {
for(idx = 0; idx < details->n_settings; idx++) {
if(details->settings[idx].id == id && is_available(&details->settings[idx])) {
if(details->settings[idx].id == id && is_available(&details->settings[idx], offset)) {
if(details->settings[idx].group == Group_Axis0 && grbl.on_set_axis_setting_unit)
set_axis_unit(&details->settings[idx], grbl.on_set_axis_setting_unit(details->settings[idx].id, offset));

View File

@@ -985,7 +985,7 @@ typedef struct setting_detail {
setting_type_t type;
void *value;
void *get_value;
bool (*is_available)(const struct setting_detail *setting);
bool (*is_available)(const struct setting_detail *setting, uint_fast16_t offset);
setting_detail_flags_t flags;
} setting_detail_t;

View File

@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2024 Terje Io
Copyright (c) 2025 Terje Io
grblHAL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -120,6 +120,13 @@ static void onReportHandlersInit (void)
grbl.report.status_message = trap_status_messages;
}
void stream_set_type (stream_type_t type, vfs_file_t *file)
{
hal.stream.type = type;
if(!(hal.stream.file = file))
gc_state.file_stream = false;
}
bool stream_is_file (void)
{
return hal.stream.type == StreamType_File;
@@ -141,9 +148,8 @@ vfs_file_t *stream_redirect_read (char *filename, status_message_ptr status_hand
rd_stream->eof_handler = eof_handler;
rd_stream->status_handler = status_handler;
rd_stream->next = NULL;
hal.stream.file = file;
hal.stream.type = StreamType_File;
hal.stream.read = stream_read_file;
stream_set_type(StreamType_File, file);
if(streams == NULL)
rd_streams = rd_stream;
else do {
@@ -181,9 +187,8 @@ void stream_redirect_close (vfs_file_t *file)
if(stream) do {
if(stream->file_new == file) {
vfs_close(file);
hal.stream.file = stream->file;
hal.stream.read = stream->read;
hal.stream.type = stream->type;
stream_set_type(stream->type, stream->file);
if(stream == rd_streams)
rd_streams = stream->next;
else

View File

@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2024 Terje Io
Copyright (c) 2024-2025 Terje Io
grblHAL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,4 +26,5 @@
bool stream_is_file (void);
void stream_redirect_close (vfs_file_t *file);
void stream_set_type (stream_type_t type, vfs_file_t *file);
vfs_file_t *stream_redirect_read (char *filename, status_message_ptr status_handler, on_file_end_ptr eof_handler);