Espresso 0.0.1b
This commit is contained in:
248
drivers/fs/fat16.c
Normal file
248
drivers/fs/fat16.c
Normal file
@ -0,0 +1,248 @@
|
||||
#include <drivers/ide.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fs/fat16.h>
|
||||
|
||||
static fat16_t fat;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint8_t jmp[3];
|
||||
char oem[8];
|
||||
uint16_t bytes_per_sector;
|
||||
uint8_t sectors_per_cluster;
|
||||
uint16_t reserved_sectors;
|
||||
uint8_t fat_count;
|
||||
uint16_t root_entries;
|
||||
uint16_t total_sectors_short;
|
||||
uint8_t media_desc;
|
||||
uint16_t fat_size_sectors;
|
||||
uint16_t sectors_per_track;
|
||||
uint16_t heads;
|
||||
uint32_t hidden_sectors;
|
||||
uint32_t total_sectors_long;
|
||||
// Extended Boot Record...
|
||||
} fat16_bpb_t;
|
||||
|
||||
typedef struct {
|
||||
char name[8];
|
||||
char ext[3];
|
||||
uint8_t attr;
|
||||
uint8_t reserved;
|
||||
uint8_t create_time_tenth;
|
||||
uint16_t create_time;
|
||||
uint16_t create_date;
|
||||
uint16_t access_date;
|
||||
uint16_t first_cluster_hi;
|
||||
uint16_t write_time;
|
||||
uint16_t write_date;
|
||||
uint16_t first_cluster_lo;
|
||||
uint32_t size;
|
||||
} fat16_direntry_raw_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
static uint8_t sector_buffer[512];
|
||||
|
||||
uint32_t cluster_to_lba(uint16_t cluster)
|
||||
{
|
||||
return fat.data_start + ((cluster - 2) * fat.sectors_per_cluster);
|
||||
}
|
||||
|
||||
|
||||
int32_t fat16_init(uint8_t drive)
|
||||
{
|
||||
if (ide_read48(drive, 0, 1, sector_buffer) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
fat16_bpb_t* bpb = (fat16_bpb_t*)sector_buffer;
|
||||
|
||||
fat.drive = drive;
|
||||
fat.bytes_per_sector = bpb->bytes_per_sector;
|
||||
fat.sectors_per_cluster = bpb->sectors_per_cluster;
|
||||
fat.root_entry_count = bpb->root_entries;
|
||||
|
||||
fat.fat_start = bpb->reserved_sectors;
|
||||
fat.root_dir_start = fat.fat_start + bpb->fat_count * bpb->fat_size_sectors;
|
||||
fat.data_start = fat.root_dir_start + ((bpb->root_entries * 32 + fat.bytes_per_sector - 1) / fat.bytes_per_sector);
|
||||
fat.current_dir_cluster = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t fat16_get_fat_entry(uint16_t cluster)
|
||||
{
|
||||
uint32_t fat_offset = cluster * 2;
|
||||
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
|
||||
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
|
||||
|
||||
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0)
|
||||
{
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
return *(uint16_t*)§or_buffer[ent_offset];
|
||||
}
|
||||
|
||||
uint16_t fat16_alloc_cluster(void)
|
||||
{
|
||||
for (uint16_t cluster = 2; cluster < 0xFFF0; ++cluster) {
|
||||
uint16_t entry = fat16_get_fat_entry(cluster);
|
||||
if (entry == 0x0000) {
|
||||
// Mark as end of chain
|
||||
uint32_t fat_offset = cluster * 2;
|
||||
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
|
||||
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
|
||||
|
||||
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0;
|
||||
*(uint16_t*)§or_buffer[ent_offset] = 0xFFFF;
|
||||
if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0;
|
||||
|
||||
return cluster;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t fat16_list_dir(fat16_dir_entry_t* entries, size_t max_entries)
|
||||
{
|
||||
size_t count = 0;
|
||||
if (fat.current_dir_cluster == 0)
|
||||
{
|
||||
uint16_t root_sectors = ((fat.root_entry_count * 32) + (fat.bytes_per_sector - 1)) / fat.bytes_per_sector;
|
||||
for (uint32_t i = 0; i < root_sectors && count < max_entries; ++i)
|
||||
{
|
||||
if (ide_read48(fat.drive, fat.root_dir_start + i, 1, sector_buffer) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
fat16_direntry_raw_t* de = (fat16_direntry_raw_t*)sector_buffer;
|
||||
for (int32_t j = 0; j < 512 / 32 && count < max_entries; ++j, ++de)
|
||||
{
|
||||
if (de->name[0] == 0x00 || de->name[0] == 0xE5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(entries[count].name, de->name, 8);
|
||||
entries[count].name[8] = '\0';
|
||||
entries[count].attr = de->attr;
|
||||
entries[count].cluster = de->first_cluster_lo;
|
||||
entries[count].size = de->size;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* TODO: Add support for listing subdirectories if current_dir_cluster != 0 */
|
||||
return count;
|
||||
}
|
||||
|
||||
int32_t fat16_read_file(const char* filename, void* buffer, size_t max_size) {
|
||||
fat16_dir_entry_t entries[64];
|
||||
int32_t n = fat16_list_dir(entries, 64);
|
||||
for (int32_t i = 0; i < n; ++i) {
|
||||
if (!(entries[i].attr & FAT16_ATTR_DIRECTORY) &&
|
||||
strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) {
|
||||
|
||||
uint16_t cluster = entries[i].cluster;
|
||||
uint8_t* out = (uint8_t*)buffer;
|
||||
size_t remaining = entries[i].size < max_size ? entries[i].size : max_size;
|
||||
|
||||
while (cluster >= 0x0002 && cluster < 0xFFF8 && remaining > 0) {
|
||||
uint32_t lba = cluster_to_lba(cluster);
|
||||
size_t to_read = fat.bytes_per_sector * fat.sectors_per_cluster;
|
||||
|
||||
if (ide_read48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0)
|
||||
return -1;
|
||||
|
||||
size_t copy_size = (remaining < to_read) ? remaining : to_read;
|
||||
memcpy(out, sector_buffer, copy_size);
|
||||
out += copy_size;
|
||||
remaining -= copy_size;
|
||||
|
||||
cluster = fat16_get_fat_entry(cluster);
|
||||
}
|
||||
|
||||
return (int32_t)(out - (uint8_t*)buffer);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int32_t fat16_write_file(const char* filename, const void* data, size_t size) {
|
||||
fat16_dir_entry_t entries[64];
|
||||
int32_t n = fat16_list_dir(entries, 64);
|
||||
for (int32_t i = 0; i < n; ++i) {
|
||||
if (strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) {
|
||||
uint16_t cluster = entries[i].cluster;
|
||||
const uint8_t* src = (const uint8_t*)data;
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
uint32_t lba = cluster_to_lba(cluster);
|
||||
size_t cluster_bytes = fat.sectors_per_cluster * fat.bytes_per_sector;
|
||||
size_t to_write = remaining < cluster_bytes ? remaining : cluster_bytes;
|
||||
|
||||
memset(sector_buffer, 0, cluster_bytes); // Clear old data
|
||||
memcpy(sector_buffer, src, to_write);
|
||||
if (ide_write48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0)
|
||||
return -1;
|
||||
|
||||
src += to_write;
|
||||
remaining -= to_write;
|
||||
|
||||
// Last chunk?
|
||||
if (remaining == 0)
|
||||
break;
|
||||
|
||||
// Check next cluster
|
||||
uint16_t next = fat16_get_fat_entry(cluster);
|
||||
if (next >= 0xFFF8 || next == 0x0000) {
|
||||
// Need to allocate a new cluster
|
||||
uint16_t new_cluster = fat16_alloc_cluster();
|
||||
if (new_cluster == 0) return -1;
|
||||
|
||||
// Update FAT to point to new cluster
|
||||
uint32_t fat_offset = cluster * 2;
|
||||
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
|
||||
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
|
||||
|
||||
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1;
|
||||
*(uint16_t*)§or_buffer[ent_offset] = new_cluster;
|
||||
if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1;
|
||||
|
||||
next = new_cluster;
|
||||
}
|
||||
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
return (int32_t)size;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t fat16_change_dir(const char* dirname)
|
||||
{
|
||||
fat16_dir_entry_t entries[64];
|
||||
int32_t n = fat16_list_dir(entries, 64);
|
||||
for (int32_t i = 0; i < n; ++i)
|
||||
{
|
||||
if ((entries[i].attr & FAT16_ATTR_DIRECTORY) && strncmp(entries[i].name, dirname, FAT16_MAX_FILENAME) == 0)
|
||||
{
|
||||
fat.current_dir_cluster = entries[i].cluster;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user