#pragma once #include "Types.h" #include "../../Debug.h" #include /** * wrapper class * provides byte-based read/write access * to block-based devices which only support * reading/writing aligned blocks */ template class AccessHelper { static constexpr const uint32_t SEC_SIZE = 512; static constexpr const char* NAME = "AccessHlp"; Dev& dev; public: AccessHelper(Dev& dev) : dev(dev) { ; } /** write size bytes starting at addr using data from src, supports writing partial blocks by reading them first */ uint32_t write(AbsPos addr, uint32_t size, const uint8_t* src) { Log::addInfo(NAME, "write(%d @ %d)", size, addr); uint32_t written = 0; LBA512 addrLBA = addr / SEC_SIZE; // LBA address uint16_t offset = (addr - addrLBA*SEC_SIZE);// 0 when addr is SEC_SIZE-byte aligned while(size) { if (offset || size < SEC_SIZE) { // non-aligned / non-full-block write // read the whole sector if (!readBlockToCache(addrLBA)) {return written;} // merge in the new data const uint32_t toModify = min(SEC_SIZE-offset, size); for (uint16_t i = 0; i < toModify; ++i) {cache.buf[i+offset] = src[i+written];} offset = 0; // write back the modified sector if (!writeBlock(addrLBA, cache.buf)) {return written;} ++addrLBA; size -= toModify; written += toModify; } else { // write a full block if (!writeBlock(addrLBA, &src[written])) {return written;} // written block was in cache? -> drop if (addrLBA == cache.lba) {cache.lba = 0xFFFFFFFF;} ++addrLBA; size -= SEC_SIZE; written += SEC_SIZE; } } return written; } /** read size bytes starting at addr into dst */ uint32_t read(AbsPos addr, uint32_t size, uint8_t* dst) { Log::addInfo(NAME, "read(%d @ %d)", size, addr); uint32_t read = 0; LBA512 addrLBA = addr / SEC_SIZE; // LBA address uint16_t offset = (addr - addrLBA*SEC_SIZE);// 0 when addr is SEC_SIZE-byte aligned, otherwise within [1:511] while(size) { if (offset || size < SEC_SIZE) { // non-aligned read / non-full-block read const uint32_t toRead = min(SEC_SIZE-offset, size); if (!readBlock(addrLBA, &dst[read], offset, toRead)) {return read;} offset = 0; // all following reads are aligned ++addrLBA; size -= toRead; read += toRead; } else { // full block read //if (!readBlock(addrLBA, &dst[read])) {return read;} if (!readBlockToCache(addrLBA)) {return read;} memcpy(&dst[read], cache.buf, SEC_SIZE); ++addrLBA; size -= SEC_SIZE; read += SEC_SIZE; } } return read; } struct Cache { LBA512 lba = 0xFFFFFFFF; uint8_t buf[SEC_SIZE]; } cache; /** read a single block of SEC_SIZE bytes into the CACHE. addr = byteAddr/512 */ bool readBlockToCache(LBA512 addr) { if (cache.lba == addr) {return true;} // already cached if (dev.readBlock(addr, cache.buf)) { cache.lba = addr; return true; } return false; } /** read a single block of SEC_SIZE bytes. addr = byteAddr/512, write only a fraction of the 512 bytes into dst (skip+len) */ bool readBlock(LBA512 addr, uint8_t* dst, uint16_t skip, uint16_t len) { if (!readBlockToCache(addr)) {return false;} for (int i = 0; i < len; ++i) { *dst = cache.buf[i+skip]; ++dst; } return true; } /** write a single block of 512 bytes. addr = byteAddr/512 */ bool writeBlock(LBA512 addr, const uint8_t* src) { return dev.writeBlock(addr, src); } private: static inline uint32_t min(uint32_t a, uint32_t b) {return (a < b) ? a : b;} };