fygue: (WIP) add syncfs support + add FAT/Flash backend + prepare open() primitive

This commit is contained in:
Yann MAGNIN 2025-04-02 15:34:15 +02:00
parent b40a6b3fa8
commit 6d99494d30
No known key found for this signature in database
GPG key ID: D82629D933EADC59
15 changed files with 1599 additions and 5 deletions

View file

@ -259,6 +259,17 @@ set(SOURCES
src/touch/adconv.c
src/touch/touch.c
src/touch/driver.c
# Fygue filesystem
src/fs/fygue/fygue.c
src/fs/fygue/fygue_open.c
src/fs/fygue/fygue_syncfs.c
src/fs/fygue/fat/cluster.c
src/fs/fygue/fat/initialize.c
src/fs/fygue/fat/readdir.c
src/fs/fygue/fat/resolve.c
src/fs/fygue/fat/sector.c
src/fs/fygue/flash/cluster.c
src/fs/fygue/flash/cmap.c
)
set(ASSETS_FX src/font5x7.png src/gdb/icons-i1msb.png)

View file

@ -0,0 +1,46 @@
/*
** fygue/fat/cluster - FAT cluster handling
*/
#include "fat.h"
//---
// Public
//---
/* fygue_fat_cluster_next() - find the next cluster ID of a file */
int fygue_fat_cluster_next(struct fygue_fat *fat, int *cluster_current)
{
uint16_t *fat0;
int cluster;
if (fat == NULL || cluster_current == NULL)
return -1;
cluster = *cluster_current;
if (cluster <= 1)
return -2;
if (cluster >= fat->ClusterCount)
return -2;
fat0 = (void*)fat->FAT0Addr;
if (fat0[cluster] == 0xffff)
return -3;
*cluster_current = fat0[cluster];
return 0;
}
/* fygue_fat_cluster_get_sector() - get sector ID from cluster ID */
int fygue_fat_cluster_get_sector(
struct fygue_fat *fat,
int cluster_id,
int *sector_id
) {
if (fat == NULL || sector_id == NULL)
return -1;
if (cluster_id >= fat->ClusterCount)
return -1;
if (cluster_id <= 1) {
*sector_id = fat->RootSectorID;
} else {
*sector_id = fat->DataSectorID + ((cluster_id - 2) * 8);
}
return 0;
}

114
src/fs/fygue/fat/fat.h Normal file
View file

@ -0,0 +1,114 @@
#ifndef FS_FYGUE_FAT_H
#define FS_FYGUE_FAT_H 1
#include <gint/defs/types.h>
#include "../flash/flash.h"
/* _FAT_WORD() : helper for 16bit value */
#define FAT_WORD(x) \
(((x & 0xff00) >> 8) | ((x & 0x00ff) << 8))
/* _FAT_DWORD() : helper for 32bit value */
#define FAT_DWORD(x) ( \
(((x) & 0xff000000) >> 24) \
| (((x) & 0x00ff0000) >> 8) \
| (((x) & 0x0000ff00) << 8) \
| (((x) & 0x000000ff) << 24) \
)
/* fygue_fat - fat information */
struct fygue_fat
{
enum {
FYGUE_FAT_TYPE_FAT12 = 0,
FYGUE_FAT_TYPE_FAT16 = 1,
} Type;
int SectorHiddenCount;
int SectorCount;
int ClusterCount;
int TotalCapacity;
int RootEntCount;
int FAT0SectorID;
int FAT1SectorID;
int RootSectorID;
int DataSectorID;
uintptr_t FAT0Addr;
uintptr_t FAT1Addr;
uintptr_t RootAddr;
uintptr_t BPB;
struct {
struct fygue_flash_cmap cmap;
} _flash;
};
/* fygue_fat_dirent - FAT dirent information */
struct fygue_fat_dirent
{
char name[256];
int type;
size_t size;
uintptr_t meta_addr;
unsigned int cluster_id;
};
/* fygue_fat_dir - fygue directory information */
struct fygue_fat_dir
{
int cluster_entry;
int cluster_current;
int root_dirent_count;
int sector_id;
int sector_count;
int dirent_cursor;
uintptr_t dirent_current_addr;
bool end_of_dirent;
bool dirty;
};
/* fygue_fat_init_cold() - fully initialize the FAT information */
extern int fygue_fat_init_cold(struct fygue_fat *fat);
/* fygue_fat_init_hot() - re-initialize the FAT information */
extern int fygue_fat_init_hot(struct fygue_fat *fat);
/* fygue_fat_resolve() - resolve the path and set the dirent information */
extern int fygue_fat_resolve(
struct fygue_fat *fat,
char const * const path,
struct fygue_fat_dirent *dirent
);
/* fygue_fat_readdir(): readdir primitive */
extern int fygue_fat_readdir(
struct fygue_fat *fat,
struct fygue_fat_dir *dir,
struct fygue_fat_dirent *dirent
);
//---
// Cluster interface
//---
/* fygue_fat_cluster_get_next() - find the next cluster ID of a file */
extern int fygue_fat_cluster_get_next(
struct fygue_fat *fat,
int *cluster_current
);
/* fygue_fat_cluster_get_sector() - get sector ID from cluster ID */
extern int fygue_fat_cluster_get_sector(
struct fygue_fat *fat,
int cluster_id,
int *sector_id
);
/* fygue_fat_get_sector_addr() - get logical sector addr */
extern int fygue_fat_sector_get_addr(
struct fygue_fat *fat,
uintptr_t *addr,
int sector
);
#endif /* FS_FYGUE_FAT_H */

View file

@ -0,0 +1,195 @@
/*
** fygue/fat/initialize - FAT initialization
*/
#include <string.h>
#include <gint/defs/attributes.h>
#include "fat.h"
//---
// Internals
//---
/* struct _fat_bpb : Bios Parameter Block */
struct _fat_bpb
{
// common boot structure
uint8_t BS_jmpBoot[3];
uint8_t BS_OEMName[8];
// BIOS parameter Block
uint16_t BPB_BytsPerSec;
uint8_t BPB_SecPerClus;
uint16_t BPB_RsvdSecCnt;
uint8_t BPB_NumFATs;
uint16_t BPB_RootEntCnt;
uint16_t BPB_TotSec16;
uint8_t BPB_Media;
uint16_t BPB_FATSz16;
uint16_t BPB_SecPerTrk;
uint16_t BPB_NumHeads;
uint32_t BPB_HiddSec;
uint32_t BPB_TotSec32;
// Extended BIOS Parameter Block
uint8_t BS_DrvNum;
uint8_t BS_Reserved1;
uint8_t BS_BootSig;
uint8_t BS_VolID[4];
uint8_t BS_VolLab[11];
uint8_t BS_FilSysType[8];
uint8_t _unused[448];
// Signature
uint8_t Signature_word[2];
} GPACKED(1);
/* _fygue_fat_sector0() - load the special sector0 information
*
* notes
* Fugue use the special sector0 to fetch critical flash information
* (total logical sector count and number of hidden sector). Then, based
* on fecthed information, Fugue select an hardcoded FAT configuration
*
* return
* -1 unable to load the sector0
* -2 unsupported Fugue configuration */
static int _fygue_fat_sector0(struct fygue_fat *fat)
{
uint8_t *sector;
int rc;
rc = fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
(void*)&sector,
0
);
if (rc != 0)
return -1;
fat->SectorHiddenCount = (
(sector[0x1c9] << 24) |
(sector[0x1c8] << 16) |
(sector[0x1c7] << 8) |
(sector[0x1c6] << 0)
);
fat->SectorCount = (
(sector[0x1cd] << 24) |
(sector[0x1cc] << 16) |
(sector[0x1cb] << 8) |
(sector[0x1ca] << 0)
);
rc = fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
&fat->BPB,
fat->SectorHiddenCount
);
if (rc != 0)
return -2;
return 0;
}
/* _fygue_fat_configure() - configure the FAT information
*
* notes
* the objectif is to find the first FAT address and the reserved root
* cluster (at the end of FATs since we only have to handle FAT12/16)
*
* note that for FAT12/16 two special cluster entry are added, this is
* why we have a manual offset to calculate the number of sector per
* FAT */
static int _fygue_fat_configure(struct fygue_fat *fat)
{
struct _fat_bpb *bpb;
int RootDirSectors;
int DataSec;
int CountofClusters;
bpb = (void *)fat->BPB;
RootDirSectors = FAT_WORD(bpb->BPB_RootEntCnt) * 32;
RootDirSectors += FAT_WORD(bpb->BPB_BytsPerSec) - 1;
RootDirSectors /= FAT_WORD(bpb->BPB_BytsPerSec);
DataSec = FAT_WORD(bpb->BPB_RsvdSecCnt);
DataSec += FAT_WORD(bpb->BPB_FATSz16) * bpb->BPB_NumFATs;
DataSec += RootDirSectors;
DataSec = FAT_WORD(bpb->BPB_TotSec16) - DataSec;
CountofClusters = DataSec / bpb->BPB_SecPerClus;
if (CountofClusters < 4085) {
fat->Type = FYGUE_FAT_TYPE_FAT12;
} else if (CountofClusters < 65525) {
fat->Type = FYGUE_FAT_TYPE_FAT16;
} else {
// TODO: maybe gint panic ?
return -2;
}
fat->TotalCapacity = DataSec * FAT_WORD(bpb->BPB_BytsPerSec);
fat->RootEntCount = FAT_WORD(bpb->BPB_RootEntCnt);
// todo : FAT12-specific operation
fat->ClusterCount = FAT_WORD(bpb->BPB_FATSz16);
fat->ClusterCount *= FAT_WORD(bpb->BPB_BytsPerSec);
fat->ClusterCount /= 2;
fat->FAT0SectorID = fat->SectorHiddenCount;
fat->FAT0SectorID += FAT_WORD(bpb->BPB_RsvdSecCnt);
fat->FAT1SectorID = fat->FAT0SectorID;
fat->FAT1SectorID += FAT_WORD(bpb->BPB_FATSz16);
fat->RootSectorID = fat->FAT1SectorID;
fat->RootSectorID += FAT_WORD(bpb->BPB_FATSz16);
fat->DataSectorID = fat->RootSectorID;
fat->DataSectorID += RootDirSectors;
return 0;
}
/* _fygue_fat_prepare() - prepare logic information */
static int _fygue_fat_prepare(struct fygue_fat *fat) {
int rc;
rc = fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
&fat->FAT0Addr,
fat->FAT0SectorID
);
if (rc != 0)
return -1;
rc = fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
(void*)&fat->FAT1Addr,
fat->FAT1SectorID
);
if (rc != 0)
return -1;
rc = fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
&fat->RootAddr,
fat->RootSectorID
);
if (rc != 0)
return -1;
return 0;
}
//---
// Public
//---
/* fygue_fat_init_cold() - fully initialize the FAT information */
int fygue_fat_init_cold(struct fygue_fat *fat)
{
if (fat == NULL)
return -1;
memset(fat, 0x00, sizeof(struct fygue_fat));
if (fygue_flash_cmap_init(&fat->_flash.cmap) != 0)
return -2;
if (
_fygue_fat_sector0(fat) != 0 ||
_fygue_fat_configure(fat) != 0 ||
_fygue_fat_prepare(fat) != 0
) {
return -3;
}
return 0;
}

419
src/fs/fygue/fat/readdir.c Normal file
View file

@ -0,0 +1,419 @@
/*
** fygue/fat/readdir - readdir primitive
*/
#include <alloca.h>
#include <gint/defs/attributes.h>
#include "fat.h"
//---
// Internals
//---
// types
/* _fat_dirent - FAT directory entry structure */
struct _fat_dirent
{
uint8_t DIR_Name[11];
uint8_t DIR_Attr;
uint8_t DIR_NTRes;
uint8_t DIR_CrtTimeTenth;
uint16_t DIR_CrtTime;
uint16_t DIR_CrtDate;
uint16_t DIR_LstAccDate;
uint16_t DIR_FstClusHI;
uint16_t DIR_WrtTime;
uint16_t DIR_WrtDate;
uint16_t DIR_FstClusLO;
uint32_t DIR_FileSize;
} GPACKED(1);
/* _fat_dir_name : Fugue directory name strcuture */
struct _fat_dirname
{
struct {
uint8_t :1; // must be 0
uint8_t last :1;
uint8_t :1; // must be 0
uint8_t id :5;
} DIR_Sequence;
uint16_t DIR_Char0;
uint16_t DIR_Char1;
uint16_t DIR_Char2;
uint16_t DIR_Char3;
uint16_t DIR_Char4;
uint8_t DIR_Attr; // must be 0x0f
uint8_t DIR_Type; // must be 0x00
uint8_t checksum;
uint16_t DIR_Char5;
uint16_t DIR_Char6;
uint16_t DIR_Char7;
uint16_t DIR_Char8;
uint16_t DIR_Char9;
uint16_t DIR_Char10;
uint16_t DIR_FstClus; // must be 0x0000
uint16_t DIR_Char11;
uint16_t DIR_Char12;
} GPACKED(1);
/* _fat_info - internal struct info */
struct _fat_info
{
struct fygue_fat *fat;
struct fygue_fat_dir *dir;
struct fygue_fat_dirent *current;
uint8_t *filename;
int idx;
};
/* _fygue_fat_readdir_cursor_update() - update internal directory cursor
*
* notes
* - assume that dir is always non-null
* - special root handling (root= dir->cluster_id == 0)
*
* return
* -1 end of dirent
* 0 success */
static int _fygue_fat_readdir_cursor_update(struct _fat_info *finfo)
{
struct fygue_fat_dir *dir;
int rc;
dir = finfo->dir;
if (dir->end_of_dirent == true)
return -1;
dir->end_of_dirent = true;
if (dir->cluster_entry == 0)
{
dir->dirent_cursor += 1;
if (dir->dirent_cursor >= dir->root_dirent_count)
return -1;
if ((dir->dirent_cursor % 16) != 0)
{
dir->dirent_current_addr += sizeof(struct _fat_dirent);
dir->end_of_dirent = false;
return 0;
}
dir->sector_id += 1;
} else {
dir->dirent_cursor += 1;
if ((dir->dirent_cursor % 16) != 0)
{
dir->dirent_current_addr += sizeof(struct _fat_dirent);
dir->end_of_dirent = false;
return 0;
}
dir->dirent_cursor = 0;
dir->sector_count += 1;
if (dir->sector_count >= 8)
{
rc = fygue_fat_cluster_get_next(
finfo->fat,
&dir->cluster_current
);
if (rc != 0)
return -1;
rc = fygue_fat_cluster_get_sector(
finfo->fat,
dir->cluster_current,
&dir->sector_id
);
if (rc != 0)
return -1;
dir->sector_count = 0;
}
}
rc = fygue_fat_sector_get_addr(
finfo->fat,
&dir->dirent_current_addr,
dir->sector_id
);
if (rc != 0)
return -1;
dir->end_of_dirent = false;
return 0;
}
//---
// FAT/VFAT dirent handling
//---
/* _fygue_fat_readdir_dirent_gen() - generate dirent */
static void _fygue_fat_readdir_dirent_gen(
struct _fat_info *finfo,
struct _fat_dirent *dirent
) {
struct fygue_fat_dirent *current;
int idx;
idx = 0;
current = finfo->current;
while (finfo->idx > 0)
current->name[idx++] = finfo->filename[--finfo->idx];
current->name[idx] = 0x00;
current->type = dirent->DIR_Attr;
current->size = FAT_DWORD(dirent->DIR_FileSize);
current->meta_addr = (uintptr_t)dirent;
current->cluster_id = (
(FAT_WORD(dirent->DIR_FstClusHI) << 16) |
(FAT_WORD(dirent->DIR_FstClusLO) << 0)
);
}
/* _fygue_fat_readdir_name_shard_update() - update internal name info */
static int _fygue_fat_readdir_name_shard_update(
struct _fat_info *finfo,
uint16_t buffer[13]
) {
uint8_t b0;
uint8_t b1;
for (int i = 0 ; i < 13 ; i++)
{
if (buffer[i] == 0x0000 || buffer[i] == 0xffff)
continue;
if (buffer[i] >= 0x20 && buffer[i] <= 0x7a) {
finfo->filename[finfo->idx++] = buffer[i] & 0x00ff;
continue;
}
b0 = (buffer[i] & 0x00ff) >> 0;
b1 = (buffer[i] & 0xff00) >> 8;
if (
(b0 >= 0x81 && b0 <= 0x9f) ||
(b0 >= 0xa1 && b0 <= 0xfc)
) {
if (b1 >= 0x40 && b1 <= 0xfc && b1 != 0x7f)
{
finfo->filename[finfo->idx++] = b1;
finfo->filename[finfo->idx++] = b0;
continue;
}
}
return -2;
}
return 0;
}
/* _fygue_fat_readdir_name_prepare() - classic FAT handling */
static int _fygue_fat_readdir_name_fat_prepare(
struct _fat_info *finfo,
struct _fat_dirent *dirent
) {
uint16_t buffer[13];
bool need_dot;
int j;
j = 13;
need_dot = false;
for (int i = 0 ; i < 11 ; i++)
{
if (i == 8)
need_dot = true;
if (dirent->DIR_Name[i] == 0x20)
continue;
if (need_dot) {
buffer[--j] = '.';
need_dot = false;
}
if (dirent->DIR_Name[i] >= 'A' && dirent->DIR_Name[i] <= 'Z') {
buffer[--j] = dirent->DIR_Name[i] + 0x20;
} else {
buffer[--j] = dirent->DIR_Name[i];
}
}
while (--j >= 0)
buffer[j] = 0x0000;
finfo->idx = 0;
return _fygue_fat_readdir_name_shard_update(finfo, buffer);
}
/* _fygue_fat_readdir_name_update() - pre-generate filename info */
static int _fygue_fat_readdir_name_vfat_prepare(
struct _fat_info *finfo,
struct _fat_dirname *dirname
) {
uint16_t buffer[13];
buffer[0] = FAT_WORD(dirname->DIR_Char12);
buffer[1] = FAT_WORD(dirname->DIR_Char11);
buffer[2] = FAT_WORD(dirname->DIR_Char10);
buffer[3] = FAT_WORD(dirname->DIR_Char9);
buffer[4] = FAT_WORD(dirname->DIR_Char8);
buffer[5] = FAT_WORD(dirname->DIR_Char7);
buffer[6] = FAT_WORD(dirname->DIR_Char6);
buffer[7] = FAT_WORD(dirname->DIR_Char5);
buffer[8] = FAT_WORD(dirname->DIR_Char4);
buffer[9] = FAT_WORD(dirname->DIR_Char3);
buffer[10] = FAT_WORD(dirname->DIR_Char2);
buffer[11] = FAT_WORD(dirname->DIR_Char1);
buffer[12] = FAT_WORD(dirname->DIR_Char0);
return _fygue_fat_readdir_name_shard_update(finfo, buffer);
}
//---
// dirent handling
//---
/* _fygue_readdir_vfat() - handle VFAT dirent generation
*
* todo
* - properly handle broken chain (0x00, 0xe5, ...)
* - properly handle `cursor_update()` error
* returns
* -3 internal error
* -2 invalid VFAT
* -1 invlaid VFAT (start)
* 0 success */
static int _fygue_readdir_vfat(struct _fat_info *finfo)
{
struct _fat_dirname *dirname;
int count;
dirname = (void*)finfo->dir->dirent_current_addr;
if (dirname->DIR_Sequence.last == 0) {
finfo->dir->dirty = true;
return -1;
}
finfo->idx = 0;
count = dirname->DIR_Sequence.id;
do {
dirname = (void*)finfo->dir->dirent_current_addr;
if (dirname->DIR_Attr != 0x0f)
return -2;
if (dirname->DIR_Sequence.id != count)
return -2;
if (_fygue_fat_readdir_name_vfat_prepare(finfo, dirname) != 0)
return -2;
if (_fygue_fat_readdir_cursor_update(finfo) != 0)
return -3;
count -= 1;
} while (count >= 1);
_fygue_fat_readdir_dirent_gen(
finfo,
(void*)finfo->dir->dirent_current_addr
);
finfo->dir->dirty = true;
return 0;
}
/* _fygue_readdir_fat() - handle FAT dirent generation */
static int _fygue_readdir_fat(struct _fat_info *finfo)
{
struct _fat_dirent *dirent;
dirent = (void*)finfo->dir->dirent_current_addr;
_fygue_fat_readdir_name_fat_prepare(finfo, dirent);
_fygue_fat_readdir_dirent_gen(finfo, dirent);
finfo->dir->dirty = true;
return 0;
}
/* _fygue_readdir_fat_check() - check FAT type
*
* notes
* check special first character of the FAT entry and try to determine
* if it is:
* 0x00 unused entry and all remaning entry will be unused
* 0xe5 unused entry
* 0x2e special "." or ".." entry
* then check the dirent attribute to distinguish an VFAT entry from an
* classic FAT entry
*
* return
* -4 unsupported enty (volume ID)
* -3 this is a end-of-dirent entry (0x00)
* -2 this is an unused entry (0xe5)
* -1 this is a special "." or ".." entry
* 0 this is a classic FAT entry
* 1 this is a VFAT entry */
static int _fygue_readdir_fat_check(struct _fat_info *finfo)
{
struct _fat_dirent *dirent;
dirent = (void*)finfo->dir->dirent_current_addr;
switch (dirent->DIR_Name[0])
{
case 0x00:
finfo->dir->end_of_dirent = true;
return -3;
case 0xe5:
finfo->dir->dirty = true;
return -2;
case 0x2e:
if (dirent->DIR_Name[1] == '.') {
finfo->filename[0] = '.';
finfo->filename[1] = '.';
finfo->filename[2] = '\0';
finfo->idx = 2;
} else {
finfo->filename[0] = '.';
finfo->filename[1] = '\0';
finfo->idx = 1;
}
_fygue_fat_readdir_dirent_gen(finfo, dirent);
finfo->dir->dirty = true;
return -1;
}
if (dirent->DIR_Attr == 0x0f)
return 1;
if ((dirent->DIR_Attr & 0x08) != 0) {
finfo->dir->dirty = true;
return -4;
}
return 0;
}
//---
// Public
//---
/* fygue_fat_readdir() - readdir-like primitive
*
* return
* -3 no dirent remaning
* -2 internal error
* -1 argument error
* 0 success */
int fygue_fat_readdir(
struct fygue_fat *fat,
struct fygue_fat_dir *dir,
struct fygue_fat_dirent *current
) {
struct _fat_info finfo;
int rc;
if (fat == NULL || current == NULL || dir == NULL)
return -1;
finfo.fat = fat;
finfo.current = current;
finfo.dir = dir;
finfo.filename = alloca(256 * sizeof(uint8_t));
if (finfo.filename == NULL)
return -2;
while (dir->end_of_dirent == false)
{
if (finfo.dir->dirty)
{
if (_fygue_fat_readdir_cursor_update(&finfo) != 0)
break;
finfo.dir->dirty = false;
}
rc = _fygue_readdir_fat_check(&finfo);
if (rc == -3)
break;
if (rc == -1)
return 0;
if (rc < 0)
continue;
if (rc == 0 && _fygue_readdir_fat(&finfo) == 0)
return 0;
if (rc == 1 && _fygue_readdir_vfat(&finfo) == 0)
return 0;
}
dir->end_of_dirent = true;
return -3;
}

128
src/fs/fygue/fat/resolve.c Normal file
View file

@ -0,0 +1,128 @@
#include <string.h>
#include "fat.h"
//---
// Internals
//---
/* _fygue_path_get_name(): get the current file name
*
* notes:
* - assume that prefix is not NULL and clean
* - assume that size is not NULL
* - assume that prefix can start with '/' */
static int _fygue_path_get_name(char const **prefix, size_t *len)
{
while ((*prefix)[0] == '/') {
*prefix = &((*prefix)[1]);
}
if ((*prefix)[0] == '\0')
return -1;
*len = 0;
while ((*prefix)[0] != '\0' && (*prefix)[0] != '/') {
*len += 1;
*prefix = &((*prefix)[1]);
}
return 0;
}
/* _fugue_dirent_resolve(): convert found dirent to dir */
static int _fygue_dirent_resolve(
struct fygue_fat *fat,
struct fygue_fat_dir *dir,
struct fygue_fat_dirent *dirent
) {
memset(dir, 0x00, sizeof(struct fygue_fat_dir));
if (dirent == NULL)
{
dir->cluster_entry = 0;
dir->root_dirent_count = fat->RootEntCount;
} else {
dir->cluster_entry = dirent->cluster_id;
dir->root_dirent_count = 0;
}
dir->dirty = false;
dir->sector_count = 0;
dir->dirent_cursor = 0;
dir->end_of_dirent = false;
dir->cluster_current = dir->cluster_entry;
int rc = fygue_fat_cluster_get_sector(
fat,
dir->cluster_current,
&dir->sector_id
);
if (rc != 0)
return -1;
return fygue_fat_sector_get_addr(
fat,
&dir->dirent_current_addr,
dir->sector_id
);
}
/* _fygue_opendir_find() - try to find the directory entry */
static int _fygue_dirent_find(
struct fygue_fat *fat,
struct fygue_fat_dir *dir,
struct fygue_fat_dirent *dirent,
char const *start,
size_t len
) {
while (true)
{
if (fygue_fat_readdir(fat, dir, dirent) != 0)
return -1;
if (strncmp(dirent->name, start, len) != 0)
continue;
return 0;
}
}
//---
// Public
//---
/* fygue_fat_resolve(): resolve any path provided
*
* notes
* - assume that the path is clean */
int fygue_fat_resolve(
struct fygue_fat *fat,
char const * const path,
struct fygue_fat_dirent *dirent
) {
struct fygue_fat_dir dir;
char const *prefix;
size_t len;
bool is_root;
if (fat == NULL || path == NULL || dirent == NULL)
return -1;
if (path[0] != '/')
return -1;
if (_fygue_dirent_resolve(fat, &dir, NULL) != 0)
return -3;
prefix = path;
is_root = true;
while (true)
{
if (_fygue_path_get_name(&prefix, &len) != 0)
break;
is_root = false;
if (_fygue_dirent_find(fat, &dir, dirent, prefix, len) != 0)
return -4;
if (_fygue_dirent_resolve(fat, &dir, dirent) != 0)
return -4;
}
if (is_root)
{
memset(dirent, 0x00, sizeof(struct fygue_fat_dirent));
dirent->name[0] = '/';
dirent->name[1] = '\0';
dirent->type = 0x10;
dirent->size = sizeof(struct fygue_fat_dirent);
dirent->meta_addr = dir.dirent_current_addr;
dirent->cluster_id = dir.cluster_entry;
}
return 0;
}

23
src/fs/fygue/fat/sector.c Normal file
View file

@ -0,0 +1,23 @@
/*
** fygue/_fat/sector - FAT sector handling
*/
#include "fat.h"
//---
// Public
//---
/* fygue_fat_sector_get_addr() - get logical sector addr */
int fygue_fat_sector_get_addr(
struct fygue_fat *fat,
uintptr_t *addr,
int sector
) {
if (fat == NULL)
return -1;
return fygue_flash_cmap_lsector_get_addr(
&fat->_flash.cmap,
addr,
sector
);
}

View file

@ -0,0 +1,191 @@
/*
** fygue/_flash/cluster - flash cluster handling
*/
#include <string.h>
#include <gint/defs/attributes.h>
#include "flash.h"
//---
// Internals
//---
/* _bfile_cluster_metadata - special cluster metadata information */
struct _bfile_cluster_metadata
{
uint8_t bitmask[2];
uint32_t fcluster_version;
uint16_t lcluster_id;
uint16_t fcluster_crc;
uint8_t constant[24];
uint8_t signature[3];
uint8_t ecc_bitmask_position[3];
} GPACKED(1);
/* _fygue_flash_cluster_convert - ensure that the provided meta is valid
*
* notes
* - assume that fcluster is not NULL
* - try using P1 information first then, P2 if this greedy-read fail
* - to ensure that the provided bfile metadata is valid we will perform
* some test (also performed by Casio). First, ensure that the
* fcluster "kind" is valid (only 0x88, 0x22 or 0x11 are known and
* used) and then ensure that the "constant" part is also valid
*
* todo : ensure checksum
* todo : ensure ecc handling (?)
* todo : try P1 then P2
*
* return
* -1 if the provided `fcluster` is not NULL
* -1 if the bfile metadata cannot be validated
* 0 otherwise */
static int _fygue_flash_cluster_convert(
struct fygue_flash_cluster *fcluster,
uintptr_t bfile_meta
) {
static const uint8_t _constant1[] = {
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
};
static const uint8_t _constant2[] = {
0x11, 0x11, 0x11, 0x22,
0x22, 0x22, 0x44, 0x44,
0x44, 0x88, 0x88, 0x88,
0x77, 0x77, 0x77, 0xbb,
0xbb, 0xbb, 0xdd, 0xdd,
0xdd, 0xee, 0xee, 0xee,
};
struct _bfile_cluster_metadata *meta[2] = {
(void*)(bfile_meta | 0x80000000),
(void*)(bfile_meta | 0xa0000000),
};
if (fcluster == NULL)
return -1;
for (int i = 0 ; i < 2 ; i++)
{
if (
meta[i]->signature[0] != 0x5a ||
meta[i]->signature[1] != 0x5a ||
meta[i]->signature[2] != 0x5a
) {
continue;
}
if (
meta[i]->bitmask[0] != 0x88 &&
meta[i]->bitmask[0] != 0x22 &&
meta[i]->bitmask[0] != 0x11
) {
continue;
}
if (meta[i]->bitmask[1] != 0x0f)
continue;
if (meta[i]->bitmask[0] != 0x11 && meta[i]->lcluster_id != 0xffff)
continue;
if (
memcmp(&(meta[i]->constant), _constant1, 0x18) != 0 &&
memcmp(&(meta[i]->constant), _constant2, 0x18) != 0
) {
continue;
}
fcluster->lcluster_id = meta[i]->lcluster_id;
fcluster->kind = meta[i]->bitmask[0];
fcluster->version = meta[i]->fcluster_version;
return 0;
}
return -2;
}
//---
// Public
//---
/* fygue_flash_cluster_geometry() - get geometry information
*
* notes
* generate flash cluster geometry information. Note that we handle
* sector as a full 32 entry table, which differ from logical cluster ID
* that ignore the last entry which contain meta information
*
* returns
* -1 if the provided address is not valid
* 0 success */
int fygue_flash_cluster_geometry(
uintptr_t addr,
uint16_t *fcluster_id,
uint16_t *fsector_id,
uint16_t *fcluster_off
) {
if ((addr & 0x0fffffff) < 0x00c80000)
return -1;
addr = (addr & 0x0fffffff) - 0x00c80000;
if (fcluster_id != NULL) {
*fcluster_id = ((addr & 0xfffe0000) >> 17) * 32;
*fcluster_id += ((addr & 0x0001ffff) >> 12);
}
if (fsector_id != NULL)
*fsector_id = ((addr & 0xfffe0000) >> 17) * 32;
if (fcluster_off != NULL)
*fcluster_off = ((addr & 0x0001ffff) >> 12);
return 0;
}
/* fygue_flash_cluster_get() - get flash cluster information
*
* return
* -1 if the provided `fline` is not valid
* -2 if the provided `fline` have invalid Bfile metadata
* 0 if the meta information has been generated */
int fygue_flash_cluster_get(
uint16_t fcluster_id,
struct fygue_flash_cluster *fcluster
) {
uintptr_t bfile_meta;
uintptr_t bfile_data;
int fsector_id;
int fcluster_off;
if (fcluster == NULL)
return -1;
if (fcluster_id >= __FYGUE_FCLUSTER_COUNT)
return -1;
fsector_id = fcluster_id / 32;
fcluster_off = fcluster_id % 32;
if (fcluster_off == 31)
{
fcluster->kind = 0x667;
fcluster->lcluster_id = 0xffff;
fcluster->version = 0xffffffff;
} else {
bfile_meta = 0x00c80000;
bfile_meta += (fsector_id * 0x20000) + 0x1f000;
bfile_meta += (fcluster_off * 0x40);
if (_fygue_flash_cluster_convert(fcluster, bfile_meta) != 0)
return -2;
}
bfile_data = 0x00c80000;
bfile_data += (fsector_id * 0x20000);
bfile_data += (fcluster_off * 0x1000);
fcluster->data_p1 = bfile_data | 0xa0000000;
fcluster->data_p2 = bfile_data | 0xa0000000;
fcluster->fcluster_id_bfile = (fsector_id * 31) + fcluster_off;
fcluster->fcluster_id = fcluster_id;
if (fcluster->kind == 0x22)
{
uint32_t *data = (void*)fcluster->data_p1;
if (
((data[0] ^ data[1]) != 0xffffffff) ||
((data[2] ^ data[3]) != 0xffffffff) ||
((data[4] ^ data[5]) != 0xffffffff)
) {
return -2;
}
fcluster->version = data[0];
}
return 0;
}

152
src/fs/fygue/flash/cmap.c Normal file
View file

@ -0,0 +1,152 @@
/*
** fygue/flash/cmap - flash cluster map
*/
#include <stdlib.h>
#include <string.h>
#include <gint/defs/attributes.h>
#include "flash.h"
//---
// Internals
//---
/* fygue_flash_cmap_update() - update the cluster redirection map
*
* notes
* Since Casio use its own methods to handle flash sector and cluster we
* need to rebuild the translation page between "logical cluster"
* (lcluster) and "flash cluster" (fcluster).
*
* Casio do not erase systematicaly each sector when they become
* outdated, instead, they use a `age` mechanism which indicate "when"
* the cluster has been created a keep the more recent one
*
* return
* -1 if the provided information is not valid
* 0 if the provided lcluster exists and has not been updated
* 1 if the provided lcluster has been created
* 2 if the provided lcluster exists and has been updated */
static int fygue_flash_cmap_update(
struct fygue_flash_cmap *cmap,
struct fygue_flash_cluster *fcluster
) {
struct fygue_flash_cmap_entry *entry;
if (cmap == NULL || fcluster == NULL)
return -1;
if (fcluster->kind != 0x11)
return -1;
if (cmap->lcluster_id_max > fcluster->lcluster_id)
{
if (
cmap->lcluster[fcluster->lcluster_id].version != 0xffffffff &&
cmap->lcluster[fcluster->lcluster_id].version
< fcluster->version
) {
return 1;
}
} else {
cmap->lcluster = realloc(
cmap->lcluster,
(fcluster->lcluster_id + 1) * sizeof(
struct fygue_flash_cmap_entry
)
);
for (
int i = cmap->lcluster_id_max ;
i < fcluster->lcluster_id ;
i++
) {
cmap->lcluster[i].fcluster_id = 0xffff;
cmap->lcluster[i].fcluster_id_bfile = 0xffff;
cmap->lcluster[i].fcluster_data_p1 = 0xffffffff;
cmap->lcluster[i].fcluster_data_p2 = 0xffffffff;
cmap->lcluster[i].version = 0xffffffff;
}
cmap->lcluster_id_max = fcluster->lcluster_id + 1;
}
entry = &(cmap->lcluster[fcluster->lcluster_id]);
entry->fcluster_id = fcluster->fcluster_id;
entry->fcluster_id_bfile = fcluster->fcluster_id_bfile;
entry->fcluster_data_p1 = fcluster->data_p1;
entry->fcluster_data_p2 = fcluster->data_p2;
entry->version = fcluster->version;
return 0;
}
//---
// Public
//---
/* fygue_flash_cmap_init() - initialize fcluster translation
*
* notes:
* Casio have its own flash handling layer that hook Fugue primitives to
* control how/when the hardware will perform IO operation
* (read,write,erase). This because the Flash used in recent devices use
* sector of 128ko which imply, when you want to update one byte of
* memory, you need to:
* - copy the whole page in RAM
* - perform the modification
* - write the new page information
*
* So, todo
*
* returns:
* -1 if the sector 0 is not found
* 0 success */
int fygue_flash_cmap_init(struct fygue_flash_cmap *cmap)
{
struct fygue_flash_cluster fcluster;
int rc;
if (cmap == NULL)
return -1;
for (int fsector = 0 ; fsector < __FYGUE_FSECTOR_COUNT ; fsector++)
{
if (fygue_flash_cluster_get(fsector * 32, &fcluster) != 0)
continue;
if (fcluster.kind != 0x22)
continue;
for (int j = 0 ; j < 31 ; j++)
{
rc = fygue_flash_cluster_get((fsector * 32) + j, &fcluster);
if (rc != 0)
continue;
if (fcluster.kind != 0x11)
continue;
fygue_flash_cmap_update(cmap, &fcluster);
}
}
if (cmap->lcluster == NULL)
return -1;
if (cmap->lcluster[0].fcluster_id == 0xffff)
return -1;
return 0;
}
/* fygue_flash_cmap_lsector_get_addr() - get logical sector address */
int fygue_flash_cmap_lsector_get_addr(
struct fygue_flash_cmap *cmap,
uintptr_t *sector,
uint16_t lsector_id
) {
uintptr_t fsector_addr;
uint16_t lcluster_id;
if (sector == NULL || cmap == NULL || cmap->lcluster == NULL)
return -1;
lcluster_id = lsector_id / 8;
if (
cmap->lcluster_id_max < lcluster_id ||
cmap->lcluster[lcluster_id].fcluster_id == 0xffff
) {
return -2;
}
fsector_addr = cmap->lcluster[lcluster_id].fcluster_data_p1;
fsector_addr += (lsector_id % 8) * 512;
*sector = fsector_addr;
return 0;
}

View file

@ -0,0 +1,67 @@
#ifndef FS_FYGUE_FLASH_H
#define FS_FYGUE_FLASH_H 1
#include <gint/defs/types.h>
/* __FYGUE_* - hardcoded fygue information */
#define __FYGUE_FSECTOR_COUNT (0x9c)
#define __FYGUE_FCLUSTER_COUNT (__FYGUE_FSECTOR_COUNT * 32)
/* fygue_fcluster - generic flash cluster information */
struct fygue_flash_cluster
{
uint32_t kind;
uint16_t fcluster_id;
uint16_t fcluster_id_bfile;
uint16_t lcluster_id;
uint32_t version;
uintptr_t data_p1;
uintptr_t data_p2;
};
/* fygue_fcluster_map_entry - cmap list entry */
struct fygue_flash_cmap_entry
{
uint16_t fcluster_id;
uint16_t fcluster_id_bfile;
uintptr_t fcluster_data_p1;
uintptr_t fcluster_data_p2;
uint32_t version;
};
/* fygue_cmap_info - Cluster redirection information */
struct fygue_flash_cmap
{
struct fygue_flash_cmap_entry *lcluster;
int lcluster_id_max;
};
// cluster map interface
/* fygue_flash_cmap_init() - initialize the fcluster redirection map */
extern int fygue_flash_cmap_init(struct fygue_flash_cmap *cmap);
/* fygue_flash_cmap_lsector_get_addr() - get logical sector address */
extern int fygue_flash_cmap_lsector_get_addr(
struct fygue_flash_cmap *cmap,
uintptr_t *sector,
uint16_t lsector_id
);
// cluster interface
/* fygue_flash_cluster_geometry() - get geometry information */
extern int fygue_flash_cluster_geometry(
uintptr_t addr,
uint16_t *fcluster_id,
uint16_t *fsector_id,
uint16_t *fcluster_off
);
/* fygue_flash_cluster_get() - get cluster information */
extern int fygue_flash_cluster_get(
uint16_t fcluster_id,
struct fygue_flash_cluster *fcluster
);
#endif /* FS_FYGUE_FLASH_H */

84
src/fs/fygue/fygue.c Normal file
View file

@ -0,0 +1,84 @@
#include <stdlib.h>
#include "fygue.h"
//---
// Internals
//---
/* __fygue_fsinfo: internal filesystem information */
static struct fygue_fsinfo *__fygue_fsinfo = NULL;
//---
// Primitives
//---
/* fygue_mount() - mount (if needed) and return the filesystem information
*
* notes
* - assume that this global is const after initialisation */
int fygue_mount(struct fygue_fsinfo **fsinfo, bool refresh)
{
if (fsinfo == NULL)
return -1;
if (__fygue_fsinfo == NULL)
{
*fsinfo = NULL;
__fygue_fsinfo = calloc(1, sizeof(struct fygue_fsinfo));
if (__fygue_fsinfo == NULL)
return -2;
if (fygue_fat_init_cold(&(__fygue_fsinfo->fat)) != 0)
return -3;
__fygue_fsinfo->dirty = false;
}
if (refresh && __fygue_fsinfo->dirty) {
if (fygue_fat_init_hot(&(__fygue_fsinfo->fat)) != 0)
return -3;
__fygue_fsinfo->dirty = false;
}
*fsinfo = __fygue_fsinfo;
return 0;
}
/* fygue_resolve() - try to resolve path
*
* notes
* - assume that path is clean
* - automatically mount the filesystem if needed
* - the saved pathname will not be saved in stat */
int fygue_resolve(
char const * const path,
int *type,
struct fygue_stat *stat
) {
struct fygue_fsinfo *fsinfo;
struct fygue_fat_dirent dirent;
if (path == NULL || path[0] != '\\')
return -1;
if (fygue_mount(&fsinfo, true) != 0)
return -2;
if (fygue_fat_resolve(&(fsinfo->fat), path, &dirent) != 0)
return -3;
(void)type;
(void)stat;
// todo: convert FAT dirent into Fygue stat
return 0;
}
//---
// Descriptor
//---
const fs_descriptor_type_t fygue_descriptor_type = {
.read = NULL,
.write = NULL,
.lseek = NULL,
.close = NULL,
};
const fs_descriptor_type_t fygue_dir_descriptor_type = {
.read = NULL,
.write = NULL,
.lseek = NULL,
.close = NULL,
};

View file

@ -1,26 +1,91 @@
#ifndef FS_FYGUE_H
#define FS_FYGUE_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <gint/fs.h>
//---
// Public global
//---
/* File descriptor type */
extern const fs_descriptor_type_t fygue_descriptor_type;
/* Directory descriptor type */
extern const fs_descriptor_type_t fygue_dir_descriptor_type;
/* Specific implementations of some standard functions */
//---
// Public (low-level) API
//---
/* fygue_open() - open file or directory */
extern int fygue_open(char const *path, int flags, mode_t mode);
/* fygue_stat() - get file or directory information */
extern int fygue_stat(
char const * restrict path,
struct stat * restrict statbuf
);
/* fygue_syncfs() - request filesystem re-synchronisation */
extern int fygue_syncfs(void *desc);
//---
// Internals
//---
#include <gint/hardware.h>
#include <errno.h>
#define ENOTSUP_IF_NOT_FYGUE(rc) \
if( \
(gint[HWFS] != HWFS_FUGUE) || \
(flags & O_WRONLY) || \
(flags & O_RDWR) \
) { \
errno = ENOTSUP; \
return (rc); \
}
#include "fat/fat.h"
/* fygue_fsinfo: internal fygue FS information */
struct fygue_fsinfo
{
struct fygue_fat fat;
bool dirty;
};
/* fygue_stat: internals file information */
struct fygue_stat {
int type;
union {
struct {
int file;
};
struct {
int dir;
};
};
};
/* fygue_file_descriptor: internal file descriptor information */
struct fygue_file_descriptor
{
bool dirty;
};
/* fygue_mount(): mount and return the filesystem info */
extern int fygue_mount(struct fygue_fsinfo **fsinfo, bool refresh);
#ifdef __cplusplus
}
#endif
#endif /* FS_FYGUE_H */

83
src/fs/fygue/fygue_open.c Normal file
View file

@ -0,0 +1,83 @@
#if 0
#include <gint/gint.h>
#include <gint/bfile.h>
#include "../fugue/util.h"
#include "fygue.h"
#include "utils.h"
int fygue_open(char const *path, int flags, GUNUSED mode_t mode)
{
int rc;
ENOTSUP_IF_NOT_FYGUE(-1);
/* Exclusive open means no sense unless creation is also requested */
bool excl = (flags & O_EXCL) && (flags & O_CREAT);
/* Truncation requires the file to be removed/recreated */
bool trunc = (flags & O_TRUNC) && (flags & O_CREAT);
/* stat the entry */
exists = fygue_resolve(path);
/* If the entry exists and O_EXCL was requested, fail. */
if(exists && excl) {
errno = EEXIST;
return -1;
}
/* If the entry is not a directory but O_DIRECTORY is set, fail. If the
directory doesn't exist, we fail regardless of O_CREAT. */
if((flags & O_DIRECTORY) && type != BFile_Type_Directory) {
errno = (exists ? ENOTDIR : ENOENT);
return -1;
}
/* If the entry is a directory, open it as such */
if(type == BFile_Type_Directory) {
void *dp = fugue_dir_explore(path);
if(!dp) {
rc = -1;
goto end;
}
fs_descriptor_t data = {
.type = &fugue_dir_descriptor_type,
.data = dp,
};
return = fs_create_descriptor(&data);
}
/* prepare Fugue/Casio path */
uint16_t *fcpath = fs_path_normalize_fc(path);
if(fcpath == NULL) {
errno = ENOMEM;
return -1;
}
/* If O_TRUNC is requested and either the file exists or we can create
* it, remove it. (If fugue_fd < 0 an opening error might still have
* occurred so we delete it just in case.) */
if((flags & O_TRUNC) && (fugue_fd >= 0 || (flags & O_CREAT))) {
gint_world_enter(GINT_WORLD_OS);
BFile_Remove(fcpath);
gint_world_leave();
fugue_fd = BFile_EntryNotFound;
}
/* If the file does not exist and O_CREAT is set, create it */
if((flags & O_CREAT) && ((flags & O_TRUNC) || fugue_fd < 0))
{
int new_file_size = 0;
gint_world_enter(GINT_WORLD_OS);
int err = BFile_Create(fcpath, BFile_File, &new_file_size);
gint_world_leave();
if(err < 0) {
errno = bfile_error_to_errno(err);
return -1;
}
}
end:
free(fcpath);
return rc;
}
#endif

View file

@ -0,0 +1,16 @@
#include "fygue.h"
int fygue_syncfs(void *data)
{
struct fygue_fsinfo *fsinfo;
struct fygue_file_descriptor *descriptor;
if (data == NULL)
return -1;
if (fygue_mount(&fsinfo, false) != 0)
return -2;
descriptor = data;
descriptor->dirty = true;
fsinfo->dirty = true;
return 0;
}

View file

@ -22,10 +22,10 @@ int open(char const *path, int flags, ...)
* descriptor because when some flags are set (e.g O_CREATE) Fygue
* invoke BFile_*() syscall that perform IO operation.
*
* Note that Fygue's open() primitive with perform a "lazy" open operation
* by simply ensure that the file exists and mark the file descriptor as
* dirty to force internal data refresh on the next Fygue's IO
* operation */
* Note that Fygue's open() primitive with perform a "lazy" open
* operation by simply ensure that the file exists and mark the file
* descriptor as dirty to force internal data refresh on the next
* Fygue's IO operation */
fs_fygue_sync();
return rc;
}