FAT16 library written for an ATmega project with SD card
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
c-fat16/fat16.h

221 lines
5.4 KiB

#pragma once
/** Abstract block device interface */
typedef struct {
// Sequential read
void (*load)(void* dest, const uint16_t len);
// Sequential write
void (*store)(const void* src, const uint16_t len);
// Sequential byte write
void (*write)(const uint8_t b);
// Sequential 2-byte write
void (*write16)(const uint16_t b);
// Sequential byte read
uint8_t (*read)(void);
// Sequential 2-byte read
uint16_t (*read16)(void);
// Absolute seek
void (*seek)(const uint32_t);
// Relative seek
void (*rseek)(const int16_t);
} BLOCKDEV;
// -------------------------------
/** file types (values don't matter) */
typedef enum {
FT_NONE = '-',
FT_DELETED = 'x',
FT_SUBDIR = 'D',
FT_PARENT = 'P',
FT_LABEL = 'L',
FT_LFN = '~',
FT_SELF = '.',
FT_FILE = 'F'
} FAT16_FT;
// File Attributes (bit flags)
#define FA_READONLY 0x01 // read only file
#define FA_HIDDEN 0x02 // hidden file
#define FA_SYSTEM 0x04 // system file
#define FA_LABEL 0x08 // volume label entry, found only in root directory.
#define FA_DIR 0x10 // subdirectory
#define FA_ARCHIVE 0x20 // archive flag
/** Boot Sector structure - INTERNAL! */
typedef struct __attribute__((packed)) {
// Fields loaded directly from disk:
// 13 bytes skipped
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t num_fats;
uint16_t root_entries;
// 3 bytes skipped
uint16_t fat_size_sectors;
// 8 bytes skipped
uint32_t total_sectors; // if "short size sectors" is used, it's copied here too
// 7 bytes skipped
char volume_label[11]; // space padded, no terminator
// Added fields:
uint32_t bytes_per_cluster;
} Fat16BootSector;
/** FAT filesystem handle - private fields! */
typedef struct __attribute__((packed)) {
// Backing block device
const BLOCKDEV* dev;
// Root directory sector start
uint32_t rd_addr;
// Start of first cluster (number "2")
uint32_t data_addr;
// Start of fat table
uint32_t fat_addr;
// Boot sector data struct
Fat16BootSector bs;
} FAT16;
/** File handle struct */
typedef struct __attribute__((packed)) {
// Fields loaded directly from disk:
uint8_t name[11]; // Starting 0x05 converted to 0xE5, other "magic chars" left intact
uint8_t attribs; // composed of FA_* constants
// 14 bytes skipped
uint16_t clu_start;
uint32_t size;
// Added fields:
FAT16_FT type;
// --- Private fields ---
// Cursor
uint32_t cur_abs; // absolute position in device
uint32_t cur_rel; // relative position in file
uint16_t cur_clu; // cluster where the cursor is
uint16_t cur_ofs; // offset within the active cluster
// File position in the directory
uint16_t clu; // first cluster of directory
uint16_t num; // fiel entry number
// pointer to FAT
const FAT16* fat;
} FAT16_FILE;
/** Initialize a filesystem */
void fat16_init(const BLOCKDEV* dev, FAT16* fat);
/**
* Open the first file of the root directory.
* The file may be invalid (eg. a volume label, deleted etc),
* or blank (type FT_NONE) if the filesystem is empty.
*
* Either way, the prev and next functions will work as expected.
*/
void fat16_open_root(const FAT16* fat, FAT16_FILE* file);
/**
* Resolve volume label.
*/
char* fat16_volume_label(const FAT16* fat, char* str);
// ----------- FILE I/O -------------
/**
* Move file cursor to a position relative to file start
* Returns false on I/O error (bad file, out of range...)
*/
bool fat16_fseek(FAT16_FILE* file, uint32_t addr);
/**
* Read bytes from file into memory
* Returns false on I/O error (bad file, out of range...)
*/
bool fat16_fread(FAT16_FILE* file, void* target, uint32_t len);
/**
* Write into file at a "seek" position.
* "seek" cursor must be within (0..filesize)
*/
bool fat16_fwrite(FAT16_FILE* file, void* src, uint32_t len);
/**
* Create a new file in given folder
*
* directory ... parent folder's first entry
* file ... where to store info about newly opened file
* name ... name of the new file, including extension
*/
bool fat16_newfile(FAT16_FILE* directory, FAT16_FILE* file, const char* name);
/**
* Write new file size (also to the disk).
* Allocates / frees needed clusters, does not erase them.
*/
void fat16_set_file_size(FAT16_FILE* file, uint32_t size);
/** Delete a file entry and free clusters. Does NOT descend into subdirectories. */
void fat16_delete_file(FAT16_FILE* file);
// --------- NAVIGATION ------------
/** Go to previous file in the directory (false = no prev file) */
bool fat16_prev(FAT16_FILE* file);
/** Go to next file in directory (false = no next file) */
bool fat16_next(FAT16_FILE* file);
/** Open a directory (file is a directory entry) */
bool fat16_opendir(FAT16_FILE* file);
/** Rewind to first file in directory */
void fat16_first(FAT16_FILE* file);
/**
* Find a file with given "display name" in this directory.
* If file is found, "dir" will contain it's handle.
* Either way, "dir" gets modified and you may need to rewind it afterwards.
*/
bool fat16_find_file(FAT16_FILE* dir, const char* name);
// -------- FILE INSPECTION -----------
/** Check if file is a valid entry, or long-name/label/deleted */
bool fat16_is_file_valid(const FAT16_FILE* file);
/**
* Resolve a file name, trim spaces and add null terminator.
* Returns the passed char*, or NULL on error.
*/
char* fat16_display_name(const FAT16_FILE* file, char* str);
/**
* Convert filename to zero-padded fixed length one
* Returns the passed char*.
*/
char* fat16_undisplay_name(const char* name, char* fixed);