mirror of
https://github.com/grblHAL/core.git
synced 2026-08-17 00:22:09 +08:00
438 lines
10 KiB
C
438 lines
10 KiB
C
/*
|
|
fs_ram.c - heap based in memory filing system
|
|
|
|
NOTE: files will be deleted on first close after opened for reading.
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2026 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
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
grblHAL is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "hal.h"
|
|
#include "vfs.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_FNAME_LEN 32
|
|
|
|
typedef union
|
|
{
|
|
uint8_t mode;
|
|
struct {
|
|
uint8_t write :1,
|
|
stream :1,
|
|
unused :1;
|
|
};
|
|
} ram_file_flags_t;
|
|
|
|
typedef struct ram_file {
|
|
char name[MAX_FNAME_LEN + 1];
|
|
const uint8_t *data;
|
|
const uint8_t *pos;
|
|
ram_file_flags_t mode;
|
|
uint8_t refs;
|
|
size_t len;
|
|
size_t remaining;
|
|
stream_block_tx_buffer_t txbuf;
|
|
struct ram_file *next;
|
|
} ram_file_t;
|
|
|
|
#define ram_fp(p) (*((ram_file_t **)(&p)))
|
|
|
|
static ram_file_t *files = NULL;
|
|
static driver_reset_ptr driver_reset = NULL;
|
|
|
|
static bool ram_write (ram_file_t *file, const uint8_t *s, size_t length)
|
|
{
|
|
uint8_t *data;
|
|
|
|
if((data = realloc((void *)file->data, file->len + length))) {
|
|
memcpy((void *)(data + file->len), s, length);
|
|
file->data = data;
|
|
file->len += length;
|
|
file->remaining = file->len;
|
|
file->pos = data + file->len;
|
|
} else {
|
|
if(file->data != NULL)
|
|
free((void *)file->data);
|
|
file->data = file->pos = NULL;
|
|
file->len = file->remaining = 0;
|
|
}
|
|
|
|
file->txbuf.s = file->txbuf.data;
|
|
file->txbuf.length = 0;
|
|
|
|
return !!data;
|
|
}
|
|
|
|
FLASHMEM static ram_file_t *find_file (const char *name)
|
|
{
|
|
ram_file_t *file = files;
|
|
|
|
while(file && strcmp(name, file->name))
|
|
file = file->next;
|
|
|
|
return file;
|
|
}
|
|
|
|
FLASHMEM static vfs_file_t *fs_open (const char *filename, const char *mode)
|
|
{
|
|
vfs_file_t *file;
|
|
ram_file_t *rfile = find_file(filename);
|
|
|
|
if(strlen(filename) > MAX_FNAME_LEN || (strchr(mode, 'r') && rfile == NULL) || (rfile && rfile->refs))
|
|
return NULL;
|
|
|
|
if((file = malloc(sizeof(vfs_file_t) + sizeof(ram_file_t *) - 1))) {
|
|
|
|
if(rfile == NULL && (rfile = calloc(sizeof(ram_file_t), 1))) {
|
|
|
|
if(rfile->txbuf.s == NULL) {
|
|
rfile->txbuf.s = rfile->txbuf.data;
|
|
rfile->txbuf.length = 0;
|
|
rfile->txbuf.max_length = sizeof(rfile->txbuf.data);
|
|
strcpy(rfile->name, filename);
|
|
}
|
|
|
|
if(files == NULL)
|
|
files = rfile;
|
|
else {
|
|
ram_file_t *add = files;
|
|
while(add->next)
|
|
add = add->next;
|
|
add->next = rfile;
|
|
}
|
|
}
|
|
|
|
if(rfile) {
|
|
rfile->refs++;
|
|
rfile->mode.write = !!strchr(mode, 'w');
|
|
rfile->pos = rfile->data;
|
|
file->size = rfile->remaining = rfile->len;
|
|
file->status.is_temporary = On;
|
|
memcpy(&file->handle, &rfile, sizeof(ram_file_t *));
|
|
} else {
|
|
free(file);
|
|
file = NULL;
|
|
}
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
FLASHMEM static bool unlink (ram_file_t *file)
|
|
{
|
|
bool unlink;
|
|
|
|
if((unlink = !file->refs || --file->refs == 0)) {
|
|
|
|
if(file == files)
|
|
files = file->next;
|
|
else {
|
|
ram_file_t *prev = files;
|
|
while(prev->next && prev->next != file)
|
|
prev = prev->next;
|
|
if(prev)
|
|
prev->next = file->next;
|
|
}
|
|
}
|
|
|
|
if(unlink) {
|
|
if(file->data)
|
|
free((void *)file->data);
|
|
free(file);
|
|
}
|
|
|
|
return unlink;
|
|
}
|
|
|
|
FLASHMEM static void fs_close (vfs_file_t *file)
|
|
{
|
|
ram_file_t *rfile = ram_fp(file->handle);
|
|
|
|
if(rfile->mode.write) {
|
|
if(rfile->txbuf.length)
|
|
ram_write(rfile, rfile->txbuf.data, rfile->txbuf.length);
|
|
rfile->mode.write = false;
|
|
rfile->refs--;
|
|
} else
|
|
unlink(rfile);
|
|
|
|
free(file);
|
|
}
|
|
|
|
FLASHMEM static size_t fs_read (void *buffer, size_t size, size_t count, vfs_file_t *file)
|
|
{
|
|
size_t rcount = 0;
|
|
ram_file_t *rfile = ram_fp(file->handle);
|
|
|
|
if(rfile->pos) {
|
|
rcount = size * count > rfile->remaining ? rfile->remaining : size * count;
|
|
memcpy(buffer, rfile->pos, rcount);
|
|
rfile->pos += rcount;
|
|
} else
|
|
rfile->remaining = rcount = 0;
|
|
|
|
rfile->remaining -= rcount;
|
|
|
|
return rcount;
|
|
}
|
|
|
|
static size_t fs_write (const void *buffer, size_t size, size_t count, vfs_file_t *file)
|
|
{
|
|
uint8_t *s = (uint8_t *)buffer;
|
|
size_t length = size * count;
|
|
ram_file_t *rfile;
|
|
|
|
if(length == 0 || (rfile = ram_fp(file->handle)) == NULL)
|
|
return 0;
|
|
|
|
if(rfile->txbuf.length && (rfile->txbuf.length + length) > rfile->txbuf.max_length) {
|
|
if(!ram_write(rfile, rfile->txbuf.data, rfile->txbuf.length))
|
|
return 0;
|
|
}
|
|
|
|
if(rfile->txbuf.length == 0 && length > rfile->txbuf.max_length)
|
|
return ram_write(rfile, s, length) ? length : 0;
|
|
|
|
memcpy(rfile->txbuf.s, s, length);
|
|
rfile->txbuf.length += length;
|
|
rfile->txbuf.s += length;
|
|
|
|
return length;
|
|
}
|
|
|
|
FLASHMEM static size_t fs_tell (vfs_file_t *file)
|
|
{
|
|
return ram_fp(file->handle)->len - ram_fp(file->handle)->remaining;
|
|
}
|
|
|
|
FLASHMEM static int fs_seek (vfs_file_t *file, size_t offset)
|
|
{
|
|
ram_file_t *rfile = NULL;
|
|
|
|
vfs_errno = ENOENT;
|
|
|
|
if((rfile = ram_fp(file->handle)) && (vfs_errno = offset <= rfile->len ? 0 : EINVAL) == 0) {
|
|
rfile->pos = rfile->data + offset;
|
|
rfile->remaining = rfile->len - offset;
|
|
}
|
|
|
|
return vfs_errno == 0 ? 0 : -1;
|
|
}
|
|
|
|
FLASHMEM static int fs_truncate (vfs_file_t *file, size_t length)
|
|
{
|
|
ram_file_t *rfile = NULL;
|
|
|
|
if(length == 0 && (rfile = ram_fp(file->handle))) {
|
|
if(rfile->data) {
|
|
free((void *)rfile->data);
|
|
rfile->data = rfile->pos = NULL;
|
|
rfile->len = rfile->remaining = 0;
|
|
}
|
|
}
|
|
|
|
return rfile && rfile->len == length ? 0 : -1;
|
|
}
|
|
|
|
FLASHMEM static bool fs_eof (vfs_file_t *file)
|
|
{
|
|
return ram_fp(file->handle)->remaining == 0;
|
|
}
|
|
|
|
FLASHMEM static int fs_rename (const char *from, const char *to)
|
|
{
|
|
ram_file_t *file = NULL;
|
|
|
|
if(strlen(to) <= MAX_FNAME_LEN && (file = find_file(from)))
|
|
strcpy(file->name, to);
|
|
|
|
return file ? 0 : -1;
|
|
}
|
|
|
|
FLASHMEM static int fs_unlink (const char *filename)
|
|
{
|
|
ram_file_t *file;
|
|
|
|
if((file = find_file(filename)) && file->refs == 0)
|
|
unlink(file);
|
|
else
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
FLASHMEM static int fs_dirop (const char *path)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
FLASHMEM static vfs_dir_t *fs_opendir (const char *path)
|
|
{
|
|
vfs_dir_t *dir;
|
|
|
|
if((dir = files ? calloc(1, sizeof(vfs_dir_t) + sizeof(ram_file_t *) - 1) : NULL))
|
|
memcpy(&dir->handle, &files, sizeof(ram_file_t *));
|
|
|
|
return dir;
|
|
}
|
|
|
|
FLASHMEM static char *fs_readdir (vfs_dir_t *dir, vfs_dirent_t *dirent)
|
|
{
|
|
ram_file_t *f;
|
|
|
|
*dirent->name = '\0';
|
|
|
|
if((f = ram_fp(dir->handle))) {
|
|
vfs_errno = 0;
|
|
dirent->size = f->len;
|
|
dirent->st_mode.mode = 0;
|
|
strcpy(dirent->name, f->name);
|
|
memcpy(&dir->handle, &f->next, sizeof(ram_file_t *));
|
|
}
|
|
|
|
return *dirent->name ? dirent->name : NULL;
|
|
}
|
|
|
|
FLASHMEM static void fs_closedir (vfs_dir_t *dir)
|
|
{
|
|
if(dir)
|
|
free(dir);
|
|
}
|
|
|
|
FLASHMEM static int fs_stat (const char *filename, vfs_stat_t *st)
|
|
{
|
|
ram_file_t *file;
|
|
|
|
if((file = find_file(filename)))
|
|
st->st_size = file->len;
|
|
|
|
return file ? 0 : -1;
|
|
}
|
|
|
|
FLASHMEM static void fs_reset (void)
|
|
{
|
|
driver_reset();
|
|
|
|
ram_file_t *file = files, *next;
|
|
|
|
if(file) do {
|
|
next = file->next;
|
|
if(file->data)
|
|
free((void *)file->data);
|
|
free(file);
|
|
} while((file = next));
|
|
}
|
|
|
|
FLASHMEM static bool fs_getfree (vfs_free_t *free)
|
|
{
|
|
ram_file_t *file = files;
|
|
|
|
free->used = 0;
|
|
|
|
if(file) do {
|
|
free->used += file->len;
|
|
} while ((file = file->next));
|
|
|
|
free->size = free->used; // available memory?
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
|
|
Code for redirecting output stream to file, replaces networking/fs_stream.c
|
|
|
|
static vfs_file_t *file; -> move to ram_file_t?
|
|
static stream_write_ptr wrptr;
|
|
|
|
fdopen()?
|
|
|
|
static void stream_write (const char *s)
|
|
{
|
|
size_t length = strlen(s);
|
|
|
|
if(length == 0)
|
|
return;
|
|
|
|
fs_write(s, length, 1, file);
|
|
}
|
|
|
|
FLASHMEM bool redirect_output (const char *filename)
|
|
{
|
|
if(hal.stream.write == stream_write)
|
|
return NULL;
|
|
|
|
if((file = fs_open(filename, "w"))) {
|
|
file->mode.stream = On;
|
|
wrptr = hal.stream.write;
|
|
hal.stream.write = stream_write;
|
|
}
|
|
|
|
return !!file;
|
|
}
|
|
|
|
FLASHMEM bool redirect_close (void)
|
|
{
|
|
bool ok;
|
|
|
|
if(hal.stream.write == stream_write)
|
|
hal.stream.write = wrptr;
|
|
|
|
if((ok = !!file)) {
|
|
fs_close(file);
|
|
file = NULL;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
*/
|
|
|
|
FLASHMEM void fs_ram_mount (void)
|
|
{
|
|
PROGMEM static const vfs_t fs = {
|
|
.fs_name = "RAM",
|
|
.fopen = fs_open,
|
|
.fclose = fs_close,
|
|
.fread = fs_read,
|
|
.fwrite = fs_write,
|
|
.ftell = fs_tell,
|
|
.fseek = fs_seek,
|
|
.ftruncate = fs_truncate,
|
|
.feof = fs_eof,
|
|
.frename = fs_rename,
|
|
.funlink = fs_unlink,
|
|
.fmkdir = fs_dirop,
|
|
.fchdir = fs_dirop,
|
|
.frmdir = fs_dirop,
|
|
.fopendir = fs_opendir,
|
|
.readdir = fs_readdir,
|
|
.fclosedir = fs_closedir,
|
|
.fstat = fs_stat,
|
|
.fgetfree = fs_getfree
|
|
};
|
|
|
|
if(driver_reset == NULL) {
|
|
driver_reset = hal.driver_reset;
|
|
hal.driver_reset = fs_reset;
|
|
vfs_mount(NULL, "/ram", &fs, (vfs_st_mode_t){ .directory = true, .hidden = true });
|
|
}
|
|
}
|