diff --git a/lib/blockdev.h b/lib/blockdev.h new file mode 100644 index 0000000..944bccf --- /dev/null +++ b/lib/blockdev.h @@ -0,0 +1,54 @@ +#pragma once + +// +// Block device interface, somewhat akin to stream.h +// Used for filesystem implementations. +// + +#include + +/** Abstract block device interface + * + * Populate an instance of this with pointers to your I/O functions. + */ +typedef struct +{ + /** Sequential read at cursor + * @param dest destination memory structure + * @param len number of bytes to load and store in {dest} + */ + void (*load)(void* dest, const uint16_t len); + + + /** Sequential write at cursor + * @param src source memory structure + * @param len number of bytes to write + */ + void (*store)(const void* src, const uint16_t len); + + + /** Write one byte at cursor + * @param b byte to write + */ + void (*write)(const uint8_t b); + + + /** Read one byte at cursor + * @return the read byte + */ + uint8_t (*read)(void); + + + /** Absolute seek - set cursor + * @param addr new cursor address + */ + void (*seek)(const uint32_t addr); + + + /** Relative seek - move cursor + * @param offset cursor address change + */ + void (*rseek)(const int16_t offset); + +} BLOCKDEV; + diff --git a/lib/fat16.c b/lib/fat16.c index c4f9f63..6ea4e44 100644 --- a/lib/fat16.c +++ b/lib/fat16.c @@ -103,7 +103,10 @@ uint32_t find_bs(const BLOCKDEV* dev) // Verify that the boot sector has a valid signature mark dev->seek(tmp + 510); dev->load(&tmp2, 2); - if (tmp2 != 0xAA55) continue; // continue to next entry + if (tmp2 != 0xAA55) + { + continue; // continue to next entry + } // return absolute MBR address return tmp; @@ -439,9 +442,12 @@ void write_file_header(FAT16_FILE* file, const char* fname_raw, const uint8_t at // =============== PUBLIC FUNCTION IMPLEMENTATIONS ================= /** Initialize a FAT16 handle */ -void fat16_init(const BLOCKDEV* dev, FAT16* fat) +bool fat16_init(const BLOCKDEV* dev, FAT16* fat) { const uint32_t bs_a = find_bs(dev); + + if (bs_a == 0) return false; + fat->dev = dev; read_bs(dev, &(fat->bs), bs_a); fat->fat_addr = bs_a + (fat->bs.reserved_sectors * 512); @@ -449,6 +455,8 @@ void fat16_init(const BLOCKDEV* dev, FAT16* fat) fat->data_addr = fat->rd_addr + (fat->bs.root_entries * 32); // entry is 32B long fat->bs.bytes_per_cluster = (fat->bs.sectors_per_cluster * 512); + + return true; } @@ -522,13 +530,19 @@ bool fat16_is_regular(const FAT16_FILE* file) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -bool fat16_read(FAT16_FILE* file, void* target, uint32_t len) +uint16_t fat16_read(FAT16_FILE* file, void* target, uint16_t len) { if (file->cur_abs == 0xFFFF) - return false; // file at the end already + return 0; // file at the end already if (file->cur_rel + len > file->size) - return false; // Attempt to read more than what is available + { + if (file->cur_rel > file->size) return 0; + len = file->size - file->cur_rel; + //return false; // Attempt to read more than what is available + } + + const uint16_t len_orig = len; const FAT16* fat = file->fat; const BLOCKDEV* dev = fat->dev; @@ -562,7 +576,7 @@ bool fat16_read(FAT16_FILE* file, void* target, uint32_t len) len -= chunk; } - return true; + return len_orig; } @@ -1000,6 +1014,23 @@ char* fat16_rawname(const char* disp_in, char* raw_out) } +FSAVEPOS fat16_savepos(const FAT16_FILE* file) +{ + FSAVEPOS fsp; + fsp.clu = file->clu; + fsp.num = file->num; + fsp.cur_rel = file->cur_rel; + return fsp; +} + + +void fat16_reopen(FAT16_FILE* file, const FSAVEPOS* pos) +{ + open_file(file->fat, file, pos->clu, pos->num); + fat16_seek(file, pos->cur_rel); +} + + /** Write new file size (also to the disk). Does not allocate clusters. */ void fat16_resize(FAT16_FILE* file, uint32_t size) { @@ -1073,11 +1104,7 @@ bool fat16_rmdir(FAT16_FILE* file) if (file->type != FT_SUBDIR) return false; // not a subdirectory entry - const FAT16* fat = file->fat; - - const uint16_t clu1 = file->clu; - const uint16_t num1 = file->num; - + const FSAVEPOS orig = fat16_savepos(file); // Open the subdir if (!fat16_opendir(file)) @@ -1097,7 +1124,7 @@ bool fat16_rmdir(FAT16_FILE* file) { // Valid child file was found, aborting. // reopen original file - open_file(fat, file, clu1, num1); + fat16_reopen(file, &orig); return false; } @@ -1106,7 +1133,7 @@ bool fat16_rmdir(FAT16_FILE* file) while (fat16_next(file)); // reopen original file - open_file(fat, file, clu1, num1); + fat16_reopen(file, &orig); // and delete as ordinary file delete_file_do(file); @@ -1126,8 +1153,7 @@ bool fat16_delete(FAT16_FILE* file) case FT_SUBDIR:; // semicolon needed to allow declaration after "case" // store original file location - const uint16_t clu1 = file->clu; - const uint16_t num1 = file->num; + const FSAVEPOS orig = fat16_savepos(file); // open the directory (skip "." and "..") open_file(file->fat, file, file->clu_start, 2); @@ -1139,14 +1165,14 @@ bool fat16_delete(FAT16_FILE* file) { // failure // reopen original file - open_file(file->fat, file, clu1, num1); + fat16_reopen(file, &orig); return false; } } while (fat16_next(file)); // go up and delete the dir - open_file(file->fat, file, clu1, num1); + fat16_reopen(file, &orig); return fat16_rmdir(file); default: @@ -1158,11 +1184,9 @@ bool fat16_delete(FAT16_FILE* file) bool fat16_parent(FAT16_FILE* file) { - const uint16_t clu1 = file->clu; - const uint16_t num1 = file->num; - // open second entry of the directory open_file(file->fat, file, file->clu, 1); + const FSAVEPOS orig = fat16_savepos(file); // if it's a valid PARENT link, follow it. if (file->type == FT_PARENT) @@ -1174,7 +1198,7 @@ bool fat16_parent(FAT16_FILE* file) { // in root already? // reopen original file - open_file(file->fat, file, clu1, num1); + fat16_reopen(file, &orig); return false; } } diff --git a/lib/fat16.h b/lib/fat16.h index ff5c19e..c0cdee7 100644 --- a/lib/fat16.h +++ b/lib/fat16.h @@ -10,26 +10,7 @@ #include #include -/** - * Abstract block device interface - * - * Populate this with pointers to your I/O functions. - */ -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 byte read - uint8_t (*read)(void); - // Absolute seek - void (*seek)(const uint32_t); - // Relative seek - void (*rseek)(const int16_t); -} BLOCKDEV; +#include "blockdev.h" // ------------------------------- @@ -52,6 +33,15 @@ typedef enum } FAT16_FT; +/** "File address" for saving and restoring file */ +typedef struct +{ + uint16_t clu; + uint16_t num; + uint32_t cur_rel; +} FSAVEPOS; + + // Include definitions of fully internal structs #include "fat16_internal.h" @@ -111,9 +101,22 @@ typedef struct __attribute__((packed)) } FAT16_FILE; +/** + * Save a file "position" into a struct, for later restoration. + * Cursor is also saved. + */ +FSAVEPOS fat16_savepos(const FAT16_FILE* file); -/** Initialize the file system - store into "fat" */ -void fat16_init(const BLOCKDEV* dev, FAT16* fat); +/** + * Restore a file from a saved position. + */ +void fat16_reopen(FAT16_FILE* file, const FSAVEPOS* pos); + + +/** + * Initialize the file system - store into "fat" + */ +bool fat16_init(const BLOCKDEV* dev, FAT16* fat); /** @@ -127,6 +130,9 @@ void fat16_root(const FAT16* fat, FAT16_FILE* file); /** * Resolve the disk label. * That can be in the Boot Sector, or in the first root directory entry. + * + * @param fat the FAT handle + * @param label_out string to store the label in. Should have at least 12 bytes. */ char* fat16_disk_label(const FAT16* fat, char* label_out); @@ -143,9 +149,9 @@ bool fat16_seek(FAT16_FILE* file, uint32_t addr); /** * Read bytes from file into memory - * Returns false on I/O error (bad file, out of range...) + * Returns number of bytes read, 0 on error. */ -bool fat16_read(FAT16_FILE* file, void* target, uint32_t len); +uint16_t fat16_read(FAT16_FILE* file, void* target, uint16_t len); /** diff --git a/lib/fat16_internal.h b/lib/fat16_internal.h index 9f196e6..82472ac 100644 --- a/lib/fat16_internal.h +++ b/lib/fat16_internal.h @@ -9,7 +9,6 @@ /** Boot Sector structure */ typedef struct __attribute__((packed)) { - // Fields loaded directly from disk: // 13 bytes skipped diff --git a/lib/sd.c b/lib/sd.c index 2ea0e19..af72506 100644 --- a/lib/sd.c +++ b/lib/sd.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -15,8 +14,13 @@ #define SD_WRITE_BLOCK 0x58 // write single block +bool sd_inited = false; + bool sd_init() { + if (sd_inited) return true; + sd_inited = true; + uint8_t i; spi_init(); diff --git a/lib/sd.h b/lib/sd.h index cba9f2b..73fb8db 100644 --- a/lib/sd.h +++ b/lib/sd.h @@ -29,7 +29,7 @@ bool sd_init(); * @param arg command argument * @return return value on success, 0xFF if nothing received back. */ -uint8_t sd_command(const uint8_t cmd, const uint32_t arg); +uint8_t sd_command(uint8_t cmd, uint32_t arg); /** @@ -42,7 +42,7 @@ uint8_t sd_command(const uint8_t cmd, const uint32_t arg); * @param len number of bytes to read * @return true on success */ -bool sd_read(const uint32_t sector, const uint16_t read_at, uint8_t * buffer, const uint16_t write_at, const uint16_t len); +bool sd_read(uint32_t sector, uint16_t read_at, uint8_t * buffer, uint16_t write_at, uint16_t len); /** @@ -52,4 +52,4 @@ bool sd_read(const uint32_t sector, const uint16_t read_at, uint8_t * buffer, co * @param buffer512 source buffer * @return true on success */ -bool sd_write(const uint32_t sector, const uint8_t * buffer512); +bool sd_write(uint32_t sector, const uint8_t * buffer512); diff --git a/lib/sd_blockdev.c b/lib/sd_blockdev.c new file mode 100644 index 0000000..574f7dd --- /dev/null +++ b/lib/sd_blockdev.c @@ -0,0 +1,172 @@ +#include +#include + +#include "sd_blockdev.h" +#include "sd.h" + +// helpers +void load_sector(const uint32_t addr); +void store_sector(); +void handle_cursor_ov(); + +// blockdev methods +void dev_load(void* dest, const uint16_t len); +void dev_store(const void* src, const uint16_t len); +uint8_t dev_read(); +void dev_write(const uint8_t b); +void dev_seek(const uint32_t addr); +void dev_rseek(const int16_t offset); + + +/** Sector buffer */ +uint8_t buff[512]; + +/** Address of the buffered sector */ +uint32_t buff_addr; + +/** Buffer needs to be flushed before next read */ +bool buff_dirty = false; + +/** Buffer holds a valid sector */ +bool buff_valid = false; + +/** seek cursor */ +uint32_t cursor_sec; +uint16_t cursor_offs; + + +/** Flush the buffer, if it's dirty */ +void sdb_flush() +{ + if (buff_dirty) + { + store_sector(); + buff_dirty = false; + } +} + + +void load_sector(const uint32_t addr) +{ + // do not load if already loaded + if (buff_valid && buff_addr == addr) { + return; + } + + sdb_flush(); + + // read entire sector + sd_read(addr, 0, buff, 0, 512); + buff_valid = true; + buff_addr = addr; +} + + +void store_sector() +{ + // Do not store if not laoded. + if (!buff_dirty) return; + if (!buff_valid) return; + + sd_write(buff_addr, buff); +} + + +/** + * Handle cursor overflow. + * MUST ABSOLUTELY NOT load/store buffer or change buffer addr! +*/ +inline void handle_cursor_ov() +{ + if (cursor_offs >= 512) + { + cursor_sec++; + cursor_offs = 0; + } +} + + + +void dev_write(const uint8_t b) +{ + load_sector(cursor_sec); + buff[cursor_offs++] = b; + buff_dirty = true; + + handle_cursor_ov(); +} + + +uint8_t dev_read() +{ + load_sector(cursor_sec); + const uint8_t b = buff[cursor_offs++]; + + handle_cursor_ov(); + + return b; +} + + +void dev_load(void* dest, const uint16_t len) +{ + for (uint16_t a = 0; a < len; a++) + { + *((uint8_t*)dest++) = dev_read(); + } +} + + +void dev_store(const void* src, const uint16_t len) +{ + for (uint16_t a = 0; a < len; a++) + { + dev_write(*((uint8_t*)src++)); + } +} + + +void dev_seek(const uint32_t addr) +{ + // compute sector and offset counters + cursor_sec = addr >> 9; + cursor_offs = addr & 0x1FF; +} + + +void dev_rseek(const int16_t offset) +{ + // add WITHIN the same sector + if (offset > 0 && cursor_offs + offset < 512) + { + cursor_offs += offset; + return; + } + + // subtract WITHIN the same sector + if (offset < 0 && ((uint16_t)(-offset) <= cursor_offs)) + { + cursor_offs += offset; + return; + } + + // abs addr change + dev_seek(((cursor_sec << 9) + cursor_offs) + offset); +} + + +/** Init SD card block device */ +bool sdb_init(BLOCKDEV* dev) +{ + if(!sd_init()) return false; + + dev->load = &dev_load; + dev->store = &dev_store; + dev->read = &dev_read; + dev->write = &dev_write; + dev->seek = &dev_seek; + dev->rseek = &dev_rseek; + + return true; +} + diff --git a/lib/sd_blockdev.h b/lib/sd_blockdev.h new file mode 100644 index 0000000..66fce6c --- /dev/null +++ b/lib/sd_blockdev.h @@ -0,0 +1,14 @@ +#pragma once + +#include "blockdev.h" + +/** + * Flush the sector buffer if it's dirty. + * + * Should be called after each sequence of writes, + * to avoid data loss. + */ +void sdb_flush(); + +/** Initialize the SD card block device */ +bool sdb_init(BLOCKDEV* dev); diff --git a/lib/sd_fat.c b/lib/sd_fat.c new file mode 100644 index 0000000..85117bb --- /dev/null +++ b/lib/sd_fat.c @@ -0,0 +1,34 @@ +#include +#include + +#include "sd_blockdev.h" +#include "sd_fat.h" +#include "fat16.h" + +FAT16 _fat; +BLOCKDEV _dev; + +bool sdfat_inited = false; + +bool sdfat_init() +{ + if (sdfat_inited) return true; + sdfat_inited = true; + + if (!sdb_init(&_dev)) return false; + if (!fat16_init(&_dev, &_fat)) return false; + + return true; +} + + +void sdfat_root(FAT16_FILE* file) +{ + fat16_root(&_fat, file); +} + + +void sdfat_disk_label(char* str) +{ + fat16_disk_label(&_fat, str); +} diff --git a/lib/sd_fat.h b/lib/sd_fat.h new file mode 100644 index 0000000..2cf835b --- /dev/null +++ b/lib/sd_fat.h @@ -0,0 +1,19 @@ +#pragma once + +// +// FAT-on-SD helpers +// + +#include "fat16.h" + +/** Initialize FAT16 filesystem on a SPI-connected SD card */ +bool sdfat_init(); + +/** Get first file of the root folder. */ +void sdfat_root(FAT16_FILE* file); + +/** Get a disk label. Str should have 12 chars. */ +void sdfat_disk_label(char* str); + +/** Flush the SD buffer (alis of sdb_flush()) */ +#define sdfat_flush() sdb_flush() diff --git a/lib/spi.c b/lib/spi.c index f6b3608..a651d44 100644 --- a/lib/spi.c +++ b/lib/spi.c @@ -5,9 +5,14 @@ #include "iopins.h" #include "spi.h" +bool spi_inited = false; + /** Init SPI (for SD card communication) */ void spi_init() { + if (spi_inited) return; + spi_inited = true; + // Pin configuration as_output(PIN_SS); as_output(PIN_MOSI); @@ -20,7 +25,7 @@ void spi_init() /** Write a byte to SPI. Returns received byte. */ -uint8_t spi_write(const uint8_t b) +uint8_t spi_write(uint8_t b) { SPDR = b; while (!(SPSR & _BV(SPIF))); diff --git a/lib/spi.h b/lib/spi.h index c2feb4f..b5103a2 100644 --- a/lib/spi.h +++ b/lib/spi.h @@ -26,5 +26,5 @@ void spi_init(); * @param ch the written byte * @return received byte */ -uint8_t spi_write(const uint8_t b); +uint8_t spi_write(uint8_t b); diff --git a/lib/stream.c b/lib/stream.c index 9b88638..ae7567b 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -6,7 +6,7 @@ #include "calc.h" -static char tmpstr[12]; // buffer for number rendering +static char tmpstr[16]; // buffer for number rendering void put_bytes(const STREAM *p, const uint8_t* str, const uint16_t len) diff --git a/lib/uart.c b/lib/uart.c index 8657fa1..fec7817 100644 --- a/lib/uart.c +++ b/lib/uart.c @@ -11,7 +11,7 @@ // Shared stream instance static STREAM _uart_singleton; -STREAM* uart; +STREAM* uart = &_uart_singleton; void _uart_init_do(uint16_t ubrr) @@ -28,8 +28,6 @@ void _uart_init_do(uint16_t ubrr) _uart_singleton.tx = &uart_tx; _uart_singleton.rx = &uart_rx; - - uart = &_uart_singleton; }