135 lines
3.1 KiB
C++
135 lines
3.1 KiB
C++
#pragma once
|
|
|
|
#include "Types.h"
|
|
#include "../../Debug.h"
|
|
|
|
/**
|
|
* wrapper class
|
|
* provides byte-based read/write access
|
|
* to block-based devices which only support
|
|
* reading/writing aligned blocks
|
|
*/
|
|
template <typename Dev> 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
|
|
uint8_t buf[SEC_SIZE];
|
|
|
|
while(size) {
|
|
|
|
if (offset || size < SEC_SIZE) { // non-aligned / non-full-block write
|
|
|
|
// read the whole sector
|
|
if (!readBlock(addrLBA, buf)) {return written;}
|
|
|
|
// merge in the new data
|
|
const uint32_t toModify = min(SEC_SIZE-offset, size);
|
|
for (uint16_t i = 0; i < toModify; ++i) {buf[i+offset] = src[i+written];}
|
|
offset = 0;
|
|
|
|
// write back the modified sector
|
|
if (!writeBlock(addrLBA, buf)) {return written;}
|
|
|
|
++addrLBA;
|
|
size -= toModify;
|
|
written += toModify;
|
|
|
|
} else {
|
|
|
|
// write a full block
|
|
if (!writeBlock(addrLBA, &src[written])) {return written;}
|
|
|
|
++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;}
|
|
++addrLBA;
|
|
size -= SEC_SIZE;
|
|
read += SEC_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return read;
|
|
|
|
}
|
|
|
|
/** read a single block of SEC_SIZE bytes. addr = byteAddr/512 */
|
|
bool readBlock(LBA512 addr, uint8_t* dst) {
|
|
return dev.readBlock(addr, dst);
|
|
}
|
|
|
|
/** 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) {
|
|
uint8_t buf[SEC_SIZE];
|
|
if (!dev.readBlock(addr, buf)) {return false;}
|
|
for (int i = 0; i < len; ++i) {
|
|
*dst = 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;}
|
|
|
|
};
|