create f_stdout, f_stdin, f_stderr and f_reopen() fatfs extensions
Create different 'drives' for SD, serial console, local console, possibly flash. This allows implementation of the extension at media access layer level.
and remove posix stdin/out from picolibc?
Or keep picolibc's stdio and using the retargeting api to map to FatFs:
#include "ff.h" // FatFs #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h>
// Keep track of open files #define MAX_FILES 4 static FIL file_table[MAX_FILES]; static int file_used[MAX_FILES] = {0};
// Convert file descriptor to FIL* static FIL* fd_to_fil(int fd) { if (fd < 0 || fd >= MAX_FILES || !file_used[fd]) return NULL; return &file_table[fd]; }
// Open a file int _open(const char *path, int flags, int mode) { FIL *f = NULL; int fd;
// find a free slot
for (fd = 0; fd < MAX_FILES; fd++) {
if (!file_used[fd]) {
f = &file_table[fd];
file_used[fd] = 1;
break;
}
}
if (!f) {
errno = EMFILE;
return -1;
}
BYTE fmode = 0;
if (flags & 0x0) fmode = FA_READ;
if (flags & 0x1) fmode = FA_WRITE | FA_CREATE_ALWAYS;
FRESULT res = f_open(f, path, fmode);
if (res != FR_OK) {
file_used[fd] = 0;
errno = EIO;
return -1;
}
return fd;
}
// Close a file int _close(int fd) { FIL *f = fd_to_fil(fd); if (!f) { errno = EBADF; return -1; } f_close(f); file_used[fd] = 0; return 0; }
// Read from a file ssize_t _read(int fd, void *buf, size_t len) { FIL *f = fd_to_fil(fd); if (!f) { errno = EBADF; return -1; } UINT br; FRESULT res = f_read(f, buf, len, &br); if (res != FR_OK) { errno = EIO; return -1; } return br; }
// Write to a file ssize_t _write(int fd, const void *buf, size_t len) { FIL *f = fd_to_fil(fd); if (!f) { errno = EBADF; return -1; } UINT bw; FRESULT res = f_write(f, buf, len, &bw); if (res != FR_OK) { errno = EIO; return -1; } return bw; }
// Seek in a file off_t _lseek(int fd, off_t offset, int whence) { FIL *f = fd_to_fil(fd); if (!f) { errno = EBADF; return -1; }
DWORD newpos;
switch (whence) {
case SEEK_SET: newpos = offset; break;
case SEEK_CUR: newpos = f_tell(f) + offset; break;
case SEEK_END: newpos = f_size(f) + offset; break;
default: errno = EINVAL; return -1;
}
if (f_lseek(f, newpos) != FR_OK) {
errno = EIO;
return -1;
}
return f_tell(f);
}