Add ISO9660 filesystem driver and ATAPI support

This commit is contained in:
spicyjpeg 2024-05-26 17:31:17 +02:00
parent b921d6e7b4
commit f9a7df6de8
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
18 changed files with 8449 additions and 7635 deletions

View File

@ -87,6 +87,7 @@ addExecutable(
main 80080000 801dfff0
src/common/args.cpp
src/common/file.cpp
src/common/file9660.cpp
src/common/filefat.cpp
src/common/filezip.cpp
src/common/gpu.cpp
@ -125,21 +126,21 @@ addExecutable(
target_compile_definitions(
main PRIVATE
$<IF:$<CONFIG:Debug>,
ENABLE_LOGGING=1
ENABLE_FILE_WRITING=1
ENABLE_LOG_BUFFER=1
ENABLE_PS1_CONTROLLER=1
#ENABLE_X76F100_DRIVER=1
ENABLE_DUMMY_DRIVER=1
ENABLE_FULL_IDE_DRIVER=1
ENABLE_I2C_LOGGING=1
,
ENABLE_LOGGING=1
ENABLE_FILE_WRITING=1
ENABLE_LOG_BUFFER=1
ENABLE_LOGGING=1
ENABLE_PS1_CONTROLLER=1
#ENABLE_X76F100_DRIVER=1
,
#ENABLE_DUMMY_DRIVER=1
ENABLE_FULL_IDE_DRIVER=1
#ENABLE_I2C_LOGGING=1
ENABLE_LOG_BUFFER=1
ENABLE_LOGGING=1
ENABLE_PS1_CONTROLLER=1
#ENABLE_X76F100_DRIVER=1
>
)

View File

@ -10,6 +10,19 @@
namespace file {
/* File fragment table */
uint64_t FileFragmentTable::get(uint64_t sector) const {
auto fragment = as<const FileFragment>();
while (sector > fragment->length) {
sector -= fragment->length;
fragment++;
}
return fragment->lba + sector;
}
/* Base file and directory classes */
File::~File(void) {
@ -27,7 +40,6 @@ size_t HostFile::read(void *output, size_t length) {
return size_t(actualLength);
}
#ifdef ENABLE_FILE_WRITING
size_t HostFile::write(const void *input, size_t length) {
int actualLength = pcdrvWrite(_fd, input, length);
@ -38,7 +50,6 @@ size_t HostFile::write(const void *input, size_t length) {
return size_t(actualLength);
}
#endif
uint64_t HostFile::seek(uint64_t offset) {
int actualOffset = pcdrvSeek(_fd, int(offset), PCDRV_SEEK_SET);
@ -84,8 +95,8 @@ size_t Provider::loadData(util::Data &output, const char *path) {
if (!_file)
return 0;
//assert(file->length <= SIZE_MAX);
if (!output.allocate(size_t(_file->length))) {
//assert(_file->size <= SIZE_MAX);
if (!output.allocate(size_t(_file->size))) {
_file->close();
delete _file;
return 0;
@ -113,7 +124,6 @@ size_t Provider::loadData(void *output, size_t length, const char *path) {
}
size_t Provider::saveData(const void *input, size_t length, const char *path) {
#ifdef ENABLE_FILE_WRITING
auto _file = openFile(path, WRITE | ALLOW_CREATE);
if (!_file)
@ -124,9 +134,6 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
_file->close();
delete _file;
return actualLength;
#else
return 0;
#endif
}
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
@ -210,7 +217,6 @@ public:
};
size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
#ifdef ENABLE_FILE_WRITING
auto _file = openFile(path, WRITE | ALLOW_CREATE);
if (!_file)
@ -259,9 +265,6 @@ size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
delete _file;
return length;
#else
return 0;
#endif
}
bool HostProvider::init(void) {
@ -272,14 +275,10 @@ bool HostProvider::init(void) {
return false;
}
type = HOST;
return true;
}
FileSystemType HostProvider::getFileSystemType(void) {
return HOST;
}
#ifdef ENABLE_FILE_WRITING
bool HostProvider::createDirectory(const char *path) {
int fd = pcdrvCreate(path, DIRECTORY);
@ -291,7 +290,6 @@ bool HostProvider::createDirectory(const char *path) {
pcdrvClose(fd);
return true;
}
#endif
File *HostProvider::openFile(const char *path, uint32_t flags) {
PCDRVOpenMode mode = PCDRV_MODE_READ;
@ -310,8 +308,8 @@ File *HostProvider::openFile(const char *path, uint32_t flags) {
auto file = new HostFile();
file->_fd = fd;
file->length = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
file->_fd = fd;
file->size = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
return file;

View File

@ -47,15 +47,31 @@ enum FileAttributeFlag {
struct FileInfo {
public:
char name[MAX_NAME_LENGTH];
uint64_t length;
uint64_t size;
uint32_t attributes;
};
/* File fragment table */
struct FileFragment {
public:
uint64_t lba, length;
};
class FileFragmentTable : public util::Data {
public:
inline uint64_t operator[](uint64_t sector) const {
return get(sector);
}
uint64_t get(uint64_t sector) const;
};
/* Base file and directory classes */
class File {
public:
uint64_t length;
uint64_t size;
virtual ~File(void);
@ -74,9 +90,7 @@ private:
public:
size_t read(void *output, size_t length);
#ifdef ENABLE_FILE_WRITING
size_t write(const void *input, size_t length);
#endif
uint64_t seek(uint64_t offset);
uint64_t tell(void) const;
void close(void);
@ -96,11 +110,13 @@ extern uint32_t currentSPUOffset;
class Provider {
public:
char volumeLabel[MAX_NAME_LENGTH];
uint32_t serialNumber;
FileSystemType type;
uint32_t serialNumber;
uint64_t capacity;
char volumeLabel[MAX_NAME_LENGTH];
inline Provider(void)
: serialNumber(0) {
: type(NONE), serialNumber(0), capacity(0) {
volumeLabel[0] = 0;
}
template<class T> inline size_t loadStruct(T &output, const char *path) {
@ -113,12 +129,14 @@ public:
virtual ~Provider(void);
virtual void close(void) {}
virtual FileSystemType getFileSystemType(void) { return NONE; }
virtual uint64_t getCapacity(void) { return 0; }
virtual uint64_t getFreeSpace(void) { return 0; }
virtual bool getFileInfo(FileInfo &output, const char *path) { return false; }
virtual bool getFileInfo(FileInfo &output, const char *path) {
return false;
}
virtual bool getFileFragments(FileFragmentTable &output, const char *path) {
return false;
}
virtual Directory *openDirectory(const char *path) { return nullptr; }
virtual bool createDirectory(const char *path) { return false; }
@ -136,11 +154,7 @@ class HostProvider : public Provider {
public:
bool init(void);
FileSystemType getFileSystemType(void);
#ifdef ENABLE_FILE_WRITING
bool createDirectory(const char *path);
#endif
File *openFile(const char *path, uint32_t flags);
};

366
src/common/file9660.cpp Normal file
View File

@ -0,0 +1,366 @@
#include <stddef.h>
#include <stdint.h>
#include "common/file.hpp"
#include "common/file9660.hpp"
#include "common/ide.hpp"
#include "common/util.hpp"
namespace file {
/* Utilities */
static void _copyPVDString(char *output, const ISOCharA *input, size_t length) {
// The strings in the PVD are padded with spaces. To make them printable,
// any span of consecutive space characters at the end is replaced with null
// bytes.
bool isPadding = true;
output += length;
input += length;
*output = 0;
for (; length; length--) {
char ch = *(--input);
if (isPadding && !__builtin_isgraph(ch))
ch = 0;
else
isPadding = false;
*(--output) = ch;
}
}
static size_t _comparePath(const ISORecord &record, const char *path) {
auto ptr = path;
while ((*ptr == '/') || (*ptr == '\\'))
ptr++;
auto recordName = record.getName();
auto nameLength = record.nameLength;
for (; nameLength && (*recordName != ';'); nameLength--) {
if (*(recordName++) != __builtin_toupper(*(ptr++)))
return 0;
}
return ptr - path;
}
static bool _recordToFileInfo(FileInfo &output, const ISORecord &record) {
auto recordName = record.getName();
auto nameLength = record.nameLength;
// Ignore names "\x00" and "\x01", which represent the current and parent
// directories respectively.
if ((*recordName == 0x00) || (*recordName == 0x01))
return false;
auto outputName = output.name;
for (; nameLength && (*recordName != ';'); nameLength--)
*(outputName++) = *(recordName++);
*outputName = 0;
output.size = record.length.le;
output.attributes = READ_ONLY | ARCHIVE;
if (!(record.flags & ISO_RECORD_EXISTENCE))
output.attributes |= HIDDEN;
if (record.flags & ISO_RECORD_DIRECTORY)
output.attributes |= DIRECTORY;
return true;
}
bool ISOVolumeDesc::validateMagic(void) const {
return (util::hash(magic, sizeof(magic)) == "CD001"_h) && (version == 1);
}
/* ISO9660 file and directory classes */
bool ISO9660File::_loadSector(uint32_t lba) {
if (lba == _bufferedLBA)
return true;
if (_device->read(_sectorBuffer, lba, 1))
return false;
_bufferedLBA = lba;
return true;
}
size_t ISO9660File::read(void *output, size_t length) {
auto offset = uint32_t(_offset);
// Do not read any data past the end of the file.
if (offset > (size_t(size) - length))
length = size_t(size) - _offset;
if (!length)
return 0;
auto ptr = reinterpret_cast<uintptr_t>(output);
auto remaining = length;
while (remaining > 0) {
auto currentPtr = reinterpret_cast<void *>(ptr);
uint32_t lba = offset / ide::ATAPI_SECTOR_SIZE + _startLBA;
size_t sectorOffset = offset % ide::ATAPI_SECTOR_SIZE;
// If the output pointer is on a sector boundary and satisfies the IDE
// driver's alignment requirements, read as many full sectors as
// possible without going through the sector buffer.
if (!sectorOffset && _device->isPointerAligned(currentPtr)) {
auto numSectors = remaining / ide::ATAPI_SECTOR_SIZE;
auto spanLength = numSectors * ide::ATAPI_SECTOR_SIZE;
if (numSectors > 0) {
if (_device->read(currentPtr, lba, numSectors))
return false;
offset += spanLength;
ptr += spanLength;
remaining -= spanLength;
continue;
}
}
// In all other cases, read one sector at a time into the buffer and
// copy it over.
auto chunkLength =
util::min(remaining, ide::ATAPI_SECTOR_SIZE - sectorOffset);
if (!_loadSector(lba))
return false;
__builtin_memcpy(currentPtr, &_sectorBuffer[sectorOffset], chunkLength);
offset += chunkLength;
ptr += chunkLength;
remaining -= chunkLength;
}
_offset += length;
return length;
}
uint64_t ISO9660File::seek(uint64_t offset) {
_offset = util::min(offset, size);
return _offset;
}
uint64_t ISO9660File::tell(void) const {
return _offset;
}
bool ISO9660Directory::getEntry(FileInfo &output) {
while (_ptr < _dataEnd) {
auto &record = *reinterpret_cast<const ISORecord *>(_ptr);
// Skip any null padding bytes inserted between entries to prevent them
// from crossing sector boundaries.
if (!(record.recordLength)) {
_ptr += 2;
continue;
}
_ptr += record.getRecordLength();
if (_recordToFileInfo(output, record))
return true;
}
return false;
}
void ISO9660Directory::close(void) {
_records.destroy();
}
/* ISO9660 filesystem provider */
static constexpr uint32_t _VOLUME_DESC_START_LBA = 0x10;
static constexpr uint32_t _VOLUME_DESC_END_LBA = 0x20;
bool ISO9660Provider::_readData(
util::Data &output, uint32_t lba, size_t numSectors
) {
if (!output.allocate(numSectors * ide::ATAPI_SECTOR_SIZE))
return false;
if (_device->read(output.ptr, lba, numSectors))
return false;
return true;
}
bool ISO9660Provider::_getRecord(
ISORecordBuffer &output, const ISORecord &root, const char *path
) {
util::Data records;
auto numSectors =
(root.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
if (!_readData(records, root.lba.le, numSectors))
return false;
// Iterate over all records in the directory.
auto ptr = reinterpret_cast<uintptr_t>(records.ptr);
auto dataEnd = ptr + root.length.le;
while (ptr < dataEnd) {
auto &record = *reinterpret_cast<const ISORecord *>(ptr);
// Skip any null padding bytes inserted between entries to prevent them
// from crossing sector boundaries.
if (!(record.recordLength)) {
ptr += 2;
continue;
}
auto nameLength = _comparePath(record, path);
if (!nameLength) {
ptr += record.getRecordLength();
continue;
}
// If the name matches, move onto the next component of the path and
// recursively search the subdirectory (if any).
path += record.nameLength;
records.destroy();
if (*path && (record.flags & ISO_RECORD_DIRECTORY)) {
return _getRecord(output, record, path);
} else {
output.copyFrom(&record);
return true;
}
}
records.destroy();
LOG("%s not found", path);
return false;
}
bool ISO9660Provider::init(const char *drive) {
int driveID = drive[0] - '0';
if ((driveID < 0) || (driveID >= int(util::countOf(ide::devices))))
return false;
_device = &ide::devices[driveID];
// Locate and parse the primary volume descriptor.
ISOPrimaryVolumeDesc pvd;
for (
uint32_t lba = _VOLUME_DESC_START_LBA; lba < _VOLUME_DESC_END_LBA; lba++
) {
if (_device->read(&pvd, lba, 1))
return false;
if (!pvd.validateMagic()) {
LOG("invalid ISO descriptor, lba=0x%x", lba);
return false;
}
if (pvd.type == ISO_TYPE_TERMINATOR)
break;
if (pvd.type != ISO_TYPE_PRIMARY)
continue;
if (pvd.isoVersion != 1) {
LOG("unsupported ISO version 0x%02x", pvd.isoVersion);
return false;
}
if (pvd.sectorLength.le != ide::ATAPI_SECTOR_SIZE) {
LOG("unsupported ISO sector size %d", pvd.sectorLength.le);
return false;
}
_copyPVDString(volumeLabel, pvd.volume, sizeof(pvd.volume));
__builtin_memcpy(&_root, &pvd.root, sizeof(_root));
type = ISO9660;
capacity = uint64_t(pvd.volumeLength.le) * ide::ATAPI_SECTOR_SIZE;
LOG("mounted ISO: %s, drive=%d", volumeLabel, driveID);
return true;
}
LOG("no ISO PVD found");
return false;
}
void ISO9660Provider::close(void) {
type = NONE;
capacity = 0;
_device = nullptr;
}
bool ISO9660Provider::getFileInfo(FileInfo &output, const char *path) {
ISORecordBuffer record;
if (!_getRecord(record, _root, path))
return false;
return _recordToFileInfo(output, record);
}
bool ISO9660Provider::getFileFragments(
FileFragmentTable &output, const char *path
) {
ISORecordBuffer record;
if (!_getRecord(record, _root, path))
return false;
// ISO9660 files are always contiguous and non-fragmented, so only a single
// fragment is needed.
if (!output.allocate<FileFragment>(1))
return false;
auto fragment = output.as<FileFragment>();
fragment->lba = record.lba.le;
fragment->length =
(record.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
return true;
}
Directory *ISO9660Provider::openDirectory(const char *path) {
ISORecordBuffer record;
if (!_getRecord(record, _root, path))
return nullptr;
if (!(record.flags & ISO_RECORD_DIRECTORY))
return nullptr;
auto dir = new ISO9660Directory();
size_t dirLength =
(record.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
if (!_readData(dir->_records, record.lba.le, dirLength)) {
LOG("read failed, path=%s", path);
delete dir;
return nullptr;
}
dir->_ptr = reinterpret_cast<uintptr_t>(dir->_records.ptr);
dir->_dataEnd = dir->_ptr + record.length.le;
return dir;
}
File *ISO9660Provider::openFile(const char *path, uint32_t flags) {
ISORecordBuffer record;
if (!_getRecord(record, _root, path))
return nullptr;
if (record.flags & ISO_RECORD_DIRECTORY)
return nullptr;
return new ISO9660File(_device, record);
}
}

186
src/common/file9660.hpp Normal file
View File

@ -0,0 +1,186 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "common/file.hpp"
#include "common/ide.hpp"
#include "common/util.hpp"
namespace file {
/* ISO9660 data types */
template<typename T> struct [[gnu::packed]] ISOInt {
T le, be;
};
struct [[gnu::packed]] ISODate {
uint8_t year, month, day, hour, minute, second, timezone;
};
using ISOUint16 = ISOInt<uint16_t>;
using ISOUint32 = ISOInt<uint32_t>;
using ISOCharA = uint8_t;
using ISOCharD = uint8_t;
/* ISO9660 data structures (see https://wiki.osdev.org/ISO_9660) */
static constexpr size_t ISO9660_MAX_NAME_LENGTH = 32;
enum ISORecordFlag : uint8_t {
ISO_RECORD_EXISTENCE = 1 << 0,
ISO_RECORD_DIRECTORY = 1 << 1,
ISO_RECORD_ASSOCIATED = 1 << 2,
ISO_RECORD_EXT_ATTR = 1 << 3,
ISO_RECORD_PROTECTION = 1 << 4,
ISO_RECORD_MULTI_EXTENT = 1 << 7
};
class [[gnu::packed]] ISORecord {
public:
uint8_t recordLength; // 0x00
uint8_t extendedAttrLength; // 0x01
ISOUint32 lba; // 0x02-0x09
ISOUint32 length; // 0x0a-0x11
ISODate date; // 0x12-0x18
uint8_t flags; // 0x19
uint8_t interleaveLength; // 0x1a
uint8_t interleaveGapLength; // 0x1b
ISOUint16 volumeNumber; // 0x1c-0x1f
uint8_t nameLength; // 0x20
inline size_t getRecordLength(void) const {
return (recordLength + 1) & ~1;
}
inline const ISOCharD *getName(void) const {
return &(this->recordLength) + sizeof(ISORecord);
}
inline const uint8_t *getSystemUseData(void) const {
return getName() + ((nameLength + 1) & ~1);
}
inline void copyFrom(const void *source) {
__builtin_memcpy(this, source, sizeof(ISORecord));
}
};
class [[gnu::packed]] ISORecordBuffer : public ISORecord {
public:
ISOCharD name[ISO9660_MAX_NAME_LENGTH];
};
enum ISOVolumeDescType : uint8_t {
ISO_TYPE_BOOT_RECORD = 0x00,
ISO_TYPE_PRIMARY = 0x01,
ISO_TYPE_SUPPLEMENTAL = 0x02,
ISO_TYPE_VOLUME_PARTITION = 0x03,
ISO_TYPE_TERMINATOR = 0xff
};
class [[gnu::packed]] ISOVolumeDesc {
public:
uint8_t type; // 0x000
uint8_t magic[5]; // 0x001-0x005
uint8_t version; // 0x006
bool validateMagic(void) const;
};
struct [[gnu::packed]] ISOPrimaryVolumeDesc : public ISOVolumeDesc {
public:
uint8_t _reserved;
ISOCharA system[32]; // 0x008-0x027
ISOCharD volume[32]; // 0x028-0x047
uint8_t _reserved2[8];
ISOUint32 volumeLength; // 0x050-0x057
uint8_t _reserved3[32];
ISOUint16 numVolumes; // 0x078-0x07b
ISOUint16 volumeNumber; // 0x07c-0x07f
ISOUint16 sectorLength; // 0x080-0x083
ISOUint32 pathTableLength; // 0x084-0x08b
uint32_t pathTableLEOffsets[2]; // 0x08c-0x093
uint32_t pathTableBEOffsets[2]; // 0x094-0x09b
ISORecord root; // 0x09c-0x0bc
uint8_t rootName; // 0x09d
ISOCharD volumeSet[128]; // 0x0be-0x13d
ISOCharA publisher[128]; // 0x13e-0x1bd
ISOCharA dataPreparer[128]; // 0x1be-0x23d
ISOCharA application[128]; // 0x23e-0x2bd
ISOCharD copyrightFile[37]; // 0x2be-0x2e2
ISOCharD abstractFile[37]; // 0x2e3-0x307
ISOCharD bibliographicFile[37]; // 0x308-0x32c
uint8_t creationDate[17]; // 0x32d-0x33d
uint8_t modificationDate[17]; // 0x33e-0x34e
uint8_t expirationDate[17]; // 0x34f-0x35f
uint8_t effectiveDate[17]; // 0x360-0x370
uint8_t isoVersion; // 0x371
uint8_t _reserved4;
uint8_t extensionData[512]; // 0x373-0x572
uint8_t _reserved5[653];
};
/* ISO9660 file and directory classes */
class ISO9660File : public File {
friend class ISO9660Provider;
private:
ide::Device *_device;
uint32_t _startLBA;
uint64_t _offset;
uint32_t _bufferedLBA;
uint8_t _sectorBuffer[ide::ATAPI_SECTOR_SIZE];
bool _loadSector(uint32_t lba);
public:
inline ISO9660File(ide::Device *device, const ISORecord &record)
: _device(device), _startLBA(record.lba.le), _offset(0), _bufferedLBA(0) {
size = record.length.le;
}
size_t read(void *output, size_t length);
uint64_t seek(uint64_t offset);
uint64_t tell(void) const;
};
class ISO9660Directory : public Directory {
friend class ISO9660Provider;
private:
util::Data _records;
uintptr_t _ptr, _dataEnd;
public:
bool getEntry(FileInfo &output);
void close(void);
};
/* ISO9660 filesystem provider */
class ISO9660Provider : public Provider {
private:
ide::Device *_device;
ISORecord _root;
bool _readData(util::Data &output, uint32_t lba, size_t numSectors);
bool _getRecord(
ISORecordBuffer &output, const ISORecord &root, const char *path
);
public:
inline ISO9660Provider(void)
: _device(nullptr) {}
bool init(const char *drive);
void close(void);
bool getFileInfo(FileInfo &output, const char *path);
bool getFileFragments(FileFragmentTable &output, const char *path);
Directory *openDirectory(const char *path);
File *openFile(const char *path, uint32_t flags);
};
}

View File

@ -48,7 +48,6 @@ size_t FATFile::read(void *output, size_t length) {
return uint64_t(actualLength);
}
#ifdef ENABLE_FILE_WRITING
size_t FATFile::write(const void *input, size_t length) {
size_t actualLength;
auto error = f_write(&_fd, input, length, &actualLength);
@ -60,7 +59,6 @@ size_t FATFile::write(const void *input, size_t length) {
return uint64_t(actualLength);
}
#endif
uint64_t FATFile::seek(uint64_t offset) {
auto error = f_lseek(&_fd, offset);
@ -93,7 +91,7 @@ bool FATDirectory::getEntry(FileInfo &output) {
return false;
__builtin_strncpy(output.name, info.fname, sizeof(output.name));
output.length = info.fsize;
output.size = info.fsize;
output.attributes = info.fattrib;
return true;
@ -115,8 +113,12 @@ bool FATProvider::init(const char *drive) {
return false;
}
type = FileSystemType(_fs.fs_type);
capacity = uint64_t(_fs.n_fatent - 2) * _fs.csize * _fs.ssize;
f_getlabel(_drive, volumeLabel, &serialNumber);
LOG("FAT mount ok, drive=%s", drive);
LOG("mounted FAT: %s, drive=%s", volumeLabel, drive);
return true;
}
@ -128,23 +130,12 @@ void FATProvider::close(void) {
return;
}
type = NONE;
capacity = 0;
LOG("FAT unmount ok, drive=%s", _drive);
}
FileSystemType FATProvider::getFileSystemType(void) {
return FileSystemType(_fs.fs_type);
}
uint64_t FATProvider::getCapacity(void) {
if (!_fs.fs_type)
return 0;
size_t clusterSize = size_t(_fs.csize) * size_t(_fs.ssize);
return uint64_t(_fs.n_fatent - 2) * uint64_t(clusterSize);
}
#ifdef ENABLE_FILE_WRITING
uint64_t FATProvider::getFreeSpace(void) {
if (!_fs.fs_type)
return 0;
@ -162,7 +153,6 @@ uint64_t FATProvider::getFreeSpace(void) {
return uint64_t(count) * uint64_t(clusterSize);
}
#endif
bool FATProvider::_selectDrive(void) {
if (!_fs.fs_type)
@ -184,12 +174,47 @@ bool FATProvider::getFileInfo(FileInfo &output, const char *path) {
}
__builtin_strncpy(output.name, info.fname, sizeof(output.name));
output.length = info.fsize;
output.size = info.fsize;
output.attributes = info.fattrib;
return true;
}
bool FATProvider::getFileFragments(
FileFragmentTable &output, const char *path
) {
if (!_selectDrive())
return false;
FIL fd;
size_t length = 0;
auto error = f_open(&fd, path, READ);
if (error)
goto _openError;
// Note that this function is not normally part of FatFs.
error = f_getlbas(&fd, nullptr, 0, &length);
if (error)
goto _fileError;
if (!output.allocate<uint64_t>(length)) {
f_close(&fd);
return false;
}
f_getlbas(&fd, output.as<uint64_t>(), 0, &length);
f_close(&fd);
return true;
_fileError:
f_close(&fd);
_openError:
LOG("%s, drive=%s", _FATFS_ERROR_NAMES[error], _drive);
return false;
}
Directory *FATProvider::openDirectory(const char *path) {
if (!_selectDrive())
return nullptr;
@ -206,7 +231,6 @@ Directory *FATProvider::openDirectory(const char *path) {
return dir;
}
#ifdef ENABLE_FILE_WRITING
bool FATProvider::createDirectory(const char *path) {
if (!_selectDrive())
return false;
@ -220,7 +244,6 @@ bool FATProvider::createDirectory(const char *path) {
return true;
}
#endif
File *FATProvider::openFile(const char *path, uint32_t flags) {
if (!_selectDrive())
@ -235,7 +258,7 @@ File *FATProvider::openFile(const char *path, uint32_t flags) {
return nullptr;
}
_file->length = f_size(&(_file->_fd));
_file->size = f_size(&(_file->_fd));
return _file;
}
@ -275,7 +298,7 @@ extern "C" DRESULT disk_read(
if (!(dev.flags & ide::DEVICE_READY))
return RES_NOTRDY;
if (dev.ideRead(data, lba, count))
if (dev.read(data, lba, count))
return RES_ERROR;
return RES_OK;
@ -290,7 +313,7 @@ extern "C" DRESULT disk_write(
return RES_NOTRDY;
if (dev.flags & ide::DEVICE_READ_ONLY)
return RES_WRPRT;
if (dev.ideWrite(data, lba, count))
if (dev.write(data, lba, count))
return RES_ERROR;
return RES_OK;
@ -303,8 +326,10 @@ extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *data) {
return RES_NOTRDY;
switch (cmd) {
#ifdef ENABLE_FULL_IDE_DRIVER
case CTRL_SYNC:
return dev.ideFlushCache() ? RES_ERROR : RES_OK;
return dev.flushCache() ? RES_ERROR : RES_OK;
#endif
case GET_SECTOR_COUNT:
__builtin_memcpy(data, &dev.capacity, sizeof(LBA_t));

View File

@ -18,9 +18,7 @@ private:
public:
size_t read(void *output, size_t length);
#ifdef ENABLE_FILE_WRITING
size_t write(const void *input, size_t length);
#endif
uint64_t seek(uint64_t offset);
uint64_t tell(void) const;
void close(void);
@ -58,18 +56,12 @@ public:
bool init(const char *drive);
void close(void);
FileSystemType getFileSystemType(void);
uint64_t getCapacity(void);
#ifdef ENABLE_FILE_WRITING
uint64_t getFreeSpace(void);
#endif
bool getFileInfo(FileInfo &output, const char *path);
bool getFileFragments(FileFragmentTable &output, const char *path);
Directory *openDirectory(const char *path);
#ifdef ENABLE_FILE_WRITING
bool createDirectory(const char *path);
#endif
File *openFile(const char *path, uint32_t flags);
};

View File

@ -66,14 +66,17 @@ bool ZIPProvider::init(File *file) {
return _file->read(output, length);
};
if (!mz_zip_reader_init(&_zip, file->length, _ZIP_FLAGS)) {
if (!mz_zip_reader_init(&_zip, file->size, _ZIP_FLAGS)) {
auto error = mz_zip_get_last_error(&_zip);
LOG("%s, file=0x%08x", _MINIZ_ZIP_ERROR_NAMES[error], file);
return false;
}
LOG("ZIP init ok, file=0x%08x", file);
type = ZIP_FILE;
capacity = _zip.m_archive_size;
LOG("mounted ZIP, file=0x%08x", file);
return true;
}
@ -88,7 +91,10 @@ bool ZIPProvider::init(const void *zipData, size_t length) {
return false;
}
LOG("ZIP init ok, ptr=0x%08x", zipData);
type = ZIP_MEMORY;
capacity = _zip.m_archive_size;
LOG("mounted ZIP, ptr=0x%08x", zipData);
return true;
}
@ -99,17 +105,9 @@ void ZIPProvider::close(void) {
_file->close();
delete _file;
}
}
FileSystemType ZIPProvider::getFileSystemType(void) {
if (!_zip.m_zip_mode)
return NONE;
return _file ? ZIP_FILE : ZIP_MEMORY;
}
uint64_t ZIPProvider::getCapacity(void) {
return _zip.m_archive_size;
type = NONE;
capacity = 0;
}
bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
@ -132,7 +130,7 @@ bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
ptr = info.m_filename;
__builtin_strncpy(output.name, ptr, sizeof(output.name));
output.length = info.m_uncomp_size;
output.size = info.m_uncomp_size;
output.attributes = READ_ONLY | ARCHIVE;
if (info.m_is_directory)

View File

@ -21,9 +21,6 @@ public:
bool init(const void *zipData, size_t length);
void close(void);
FileSystemType getFileSystemType(void);
uint64_t getCapacity(void);
bool getFileInfo(FileInfo &output, const char *path);
size_t loadData(util::Data &output, const char *path);

View File

@ -26,6 +26,25 @@ static constexpr int _RESET_STATUS_TIMEOUT = 2000000;
static constexpr int _DATA_STATUS_TIMEOUT = 2000000;
static constexpr int _DMA_TIMEOUT = 10000;
static const char *const _SENSE_KEY_NAMES[]{
"NO_SENSE",
"RECOVERED_ERROR",
"NOT_READY",
"MEDIUM_ERROR",
"HARDWARE_ERROR",
"ILLEGAL_REQUEST",
"UNIT_ATTENTION",
"DATA_PROTECT",
"BLANK_CHECK",
nullptr,
nullptr,
"ABORTED_COMMAND",
nullptr,
nullptr,
"MISCOMPARE",
nullptr
};
const char *const DEVICE_ERROR_NAMES[]{
"NO_ERROR",
"UNSUPPORTED_OP",
@ -37,6 +56,7 @@ const char *const DEVICE_ERROR_NAMES[]{
/* Utilities */
#ifdef ENABLE_FULL_IDE_DRIVER
static void _copyString(char *output, const uint16_t *input, size_t length) {
// The strings in the identification block are byte-swapped and padded with
// spaces. To make them printable, any span of consecutive space characters
@ -64,6 +84,7 @@ static void _copyString(char *output, const uint16_t *input, size_t length) {
*(--output) = b;
}
}
#endif
bool IdentifyBlock::validateChecksum(void) const {
if ((checksum & 0xff) != 0xa5)
@ -144,6 +165,7 @@ DeviceError Device::_command(uint8_t cmd, bool drdy) {
uint8_t mask = drdy ? CS0_STATUS_DRDY : 0;
error = _waitForStatus(CS0_STATUS_BSY | mask, mask, _STATUS_TIMEOUT);
if (error)
return error;
@ -157,6 +179,7 @@ DeviceError Device::_transferPIO(void *data, size_t length, bool write) {
auto error = _waitForStatus(
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
);
if (error)
return error;
@ -181,6 +204,7 @@ DeviceError Device::_transferDMA(void *data, size_t length, bool write) {
auto error = _waitForStatus(
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
);
if (error)
return error;
@ -213,6 +237,7 @@ DeviceError Device::enumerate(void) {
_select();
auto error = _waitForStatus(CS0_STATUS_BSY, 0, _RESET_STATUS_TIMEOUT);
if (error)
return error;
@ -225,10 +250,12 @@ DeviceError Device::enumerate(void) {
flags |= DEVICE_ATAPI;
error = _command(ATA_IDENTIFY_PACKET, false);
if (error)
return error;
error = _transferPIO(&block, sizeof(IdentifyBlock));
if (error)
return error;
@ -246,10 +273,12 @@ DeviceError Device::enumerate(void) {
flags |= DEVICE_HAS_PACKET16;
} else {
error = _command(ATA_IDENTIFY);
if (error)
return error;
error = _transferPIO(&block, sizeof(IdentifyBlock));
if (error)
return error;
@ -265,18 +294,22 @@ DeviceError Device::enumerate(void) {
flags |= DEVICE_HAS_FLUSH;
}
#ifdef ENABLE_FULL_IDE_DRIVER
_copyString(model, block.model, sizeof(block.model));
_copyString(revision, block.revision, sizeof(block.revision));
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model);
#endif
// Find out the fastest PIO transfer mode supported and enable it.
int mode = block.getHighestPIOMode();
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
_write(CS0_COUNT, (1 << 3) | mode);
error = _command(ATA_SET_FEATURES, false);
if (error)
return error;
@ -303,19 +336,21 @@ DeviceError Device::_ideReadWrite(
}
while (count) {
size_t length = util::min(count, maxLength);
size_t chunkLength = util::min(count, maxLength);
_setLBA(lba, count);
_setLBA(lba, chunkLength);
auto error = _command(cmd);
if (error)
return error;
// Data must be transferred one sector at a time as the drive may
// deassert DRQ between sectors.
for (size_t i = length; i; i--) {
for (size_t i = chunkLength; i; i--) {
error = _transferPIO(
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE, write
);
if (error)
return error;
@ -325,16 +360,64 @@ DeviceError Device::_ideReadWrite(
error = _waitForStatus(
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY, _STATUS_TIMEOUT
);
if (error)
return error;
count -= length;
count -= chunkLength;
}
return NO_ERROR;
}
DeviceError Device::ideIdle(bool standby) {
DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
Packet packet;
packet.setRead(lba, count);
auto error = atapiPacket(packet);
if (error)
return error;
// Data must be transferred one sector at a time as the drive may deassert
// DRQ between sectors.
for (; count; count--) {
error = _transferPIO(
reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE
);
if (error)
return error;
ptr += ATAPI_SECTOR_SIZE;
}
return _waitForStatus(CS0_STATUS_BSY, 0, _STATUS_TIMEOUT);
}
#ifdef ENABLE_FULL_IDE_DRIVER
DeviceError Device::_atapiRequestSense(void){
Packet packet;
packet.setRequestSense();
auto error = atapiPacket(packet);
if (error)
return error;
SenseData sense;
error = _transferPIO(&sense, sizeof(sense));
if (error)
return error;
LOG("%s (0x%02x)", _SENSE_KEY_NAMES[sense.senseKey & 0xf], sense.senseKey);
LOG("asc=0x%02x, ascq=0x%02x", sense.asc, sense.ascQualifier);
return NO_ERROR;
}
DeviceError Device::goIdle(bool standby) {
_select();
auto error = _command(standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
@ -353,7 +436,7 @@ DeviceError Device::ideIdle(bool standby) {
return error;
}
DeviceError Device::ideFlushCache(void) {
DeviceError Device::flushCache(void) {
if (!(flags & DEVICE_HAS_FLUSH))
return NO_ERROR;
//return UNSUPPORTED_OP;
@ -364,6 +447,7 @@ DeviceError Device::ideFlushCache(void) {
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
);
}
#endif
DeviceError Device::atapiPacket(Packet &packet, size_t transferLength) {
if (!(flags & DEVICE_ATAPI))
@ -373,15 +457,49 @@ DeviceError Device::atapiPacket(Packet &packet, size_t transferLength) {
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
auto error = _command(ATA_PACKET, false);
if (error)
return error;
error = _transferPIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
if (error)
return error;
return _waitForStatus(CS0_STATUS_BSY, 0, _STATUS_TIMEOUT);
}
DeviceError Device::read(void *data, uint64_t lba, size_t count) {
util::assertAligned<uint32_t>(data);
if (flags & DEVICE_ATAPI) {
auto error = _atapiRead(
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
);
#ifdef ENABLE_FULL_IDE_DRIVER
// Log sense information in case of read errors.
if (error)
_atapiRequestSense();
#endif
return error;
} else {
return _ideReadWrite(
reinterpret_cast<uintptr_t>(data), lba, count, false
);
}
}
DeviceError Device::write(const void *data, uint64_t lba, size_t count) {
util::assertAligned<uint32_t>(data);
if (flags & (DEVICE_READ_ONLY | DEVICE_ATAPI))
return UNSUPPORTED_OP;
return _ideReadWrite(reinterpret_cast<uintptr_t>(data), lba, count, true);
}
}

View File

@ -139,6 +139,7 @@ enum ATAPISenseKey : uint8_t {
SENSE_KEY_ILLEGAL_REQUEST = 0x5,
SENSE_KEY_UNIT_ATTENTION = 0x6,
SENSE_KEY_DATA_PROTECT = 0x7,
SENSE_KEY_BLANK_CHECK = 0x8,
SENSE_KEY_ABORTED_COMMAND = 0xb,
SENSE_KEY_MISCOMPARE = 0xe
};
@ -236,7 +237,32 @@ public:
int getHighestPIOMode(void) const;
};
class [[gnu::packed]] Packet {
/* ATAPI data structures */
class SenseData {
public:
uint8_t errorCode; // 0
uint8_t _reserved; // 1
uint8_t senseKey; // 2
uint8_t info[4]; // 3-6
uint8_t additionalLength; // 7
uint8_t commandSpecificInfo[4]; // 8-11
uint8_t asc; // 12
uint8_t ascQualifier; // 13
uint8_t unitCode; // 14
uint8_t senseKeySpecificHeader; // 15
uint8_t senseKeySpecific[2]; // 16-17
inline uint32_t getErrorLBA(void) const {
return 0
| (info[0] << 24)
| (info[1] << 16)
| (info[2] << 8)
| (info[3] << 0);
}
};
class Packet {
public:
uint8_t command;
uint8_t param[11];
@ -268,6 +294,12 @@ public:
param[1] = (value >> 8) & 0xff;
param[2] = (value >> 0) & 0xff;
}
inline void setRequestSense(uint8_t additionalLength = 0) {
util::clear(*this);
command = ATAPI_REQUEST_SENSE;
param[3] = sizeof(SenseData) + additionalLength;
}
};
/* Device class */
@ -325,11 +357,17 @@ private:
DeviceError _ideReadWrite(
uintptr_t ptr, uint64_t lba, size_t count, bool write
);
DeviceError _atapiRead(uintptr_t ptr, uint32_t lba, size_t count);
#ifdef ENABLE_FULL_IDE_DRIVER
DeviceError _atapiRequestSense(void);
#endif
public:
uint32_t flags;
#ifdef ENABLE_FULL_IDE_DRIVER
char model[41], revision[9], serialNumber[21];
#endif
uint64_t capacity;
inline Device(uint32_t flags)
@ -338,19 +376,27 @@ public:
inline size_t getSectorSize(void) const {
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
}
inline DeviceError ideRead(void *data, uint64_t lba, size_t count) {
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, false);
}
inline DeviceError ideWrite(const void *data, uint64_t lba, size_t count) {
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, true);
inline bool isPointerAligned(const void *ptr) {
// DMA transfers require 4-byte alignment, while PIO transfers require
// 2-byte alignment.
#if 0
return bool(!(reinterpret_cast<uintptr_t>(ptr) % alignof(uint32_t)));
#else
return bool(!(reinterpret_cast<uintptr_t>(ptr) % alignof(uint16_t)));
#endif
}
DeviceError enumerate(void);
DeviceError ideIdle(bool standby = false);
DeviceError ideFlushCache(void);
#ifdef ENABLE_FULL_IDE_DRIVER
DeviceError goIdle(bool standby = false);
DeviceError flushCache(void);
#endif
DeviceError atapiPacket(
Packet &packet, size_t transferLength = ATAPI_SECTOR_SIZE
);
DeviceError read(void *data, uint64_t lba, size_t count);
DeviceError write(const void *data, uint64_t lba, size_t count);
};
extern const char *const DEVICE_ERROR_NAMES[];

View File

@ -87,7 +87,7 @@ void setRTCTime(const util::Date &value, bool stop) {
//assert((value.year >= 1970) && (value.year <= 2069));
int _year = value.year % 100;
int weekday = value.getDayOfWeek();
int weekday = value.getDayOfWeek() + 1;
int year = (_year % 10) | (((_year / 10) & 15) << 4);
int month = (value.month % 10) | (((value.month / 10) & 1) << 4);
@ -123,7 +123,7 @@ bool isRTCBatteryLow(void) {
enum BitstreamTagType : uint8_t {
_TAG_SOURCE_FILE = 'a',
_TAG_CHIP_TYPE = 'b',
_TAG_PART_NAME = 'b',
_TAG_BUILD_DATE = 'c',
_TAG_BUILD_TIME = 'd',
_TAG_DATA = 'e'
@ -194,10 +194,10 @@ bool loadRawBitstream(const uint8_t *data, size_t length) {
uint8_t id1 = data[1], id2 = data[4];
void (*writeFunc)(const uint8_t *, size_t);
if (((id1 & 0x0f) == 0x04) && ((id2 & 0xf0) == 0xf0))
writeFunc = &_writeBitstreamLSB;
else if (((id1 & 0xf0) == 0x20) && ((id2 & 0x0f) == 0x0f))
if (((id1 & 0xf0) == 0x20) && ((id2 & 0x0f) == 0x0f))
writeFunc = &_writeBitstreamMSB;
else if (((id1 & 0x0f) == 0x04) && ((id2 & 0xf0) == 0xf0))
writeFunc = &_writeBitstreamLSB;
else
return false;

View File

@ -1,4 +1,5 @@
#include <stdint.h>
#include "common/args.hpp"
#include "common/io.hpp"
#include "common/rom.hpp"

View File

@ -73,16 +73,15 @@ void IDEInfoScreen::show(ui::Context &ctx, bool goBack) {
}
// FAT file system
auto &fs = APP->_fileProvider;
auto fsType = fs.getFileSystemType();
auto &fs = APP->_fileProvider;
_PRINT(STR("IDEInfoScreen.fat.header"));
if (fsType)
if (fs.type)
_PRINT(
STR("IDEInfoScreen.fat.info"), _FAT_TYPES[fsType], fs.volumeLabel,
STR("IDEInfoScreen.fat.info"), _FAT_TYPES[fs.type], fs.volumeLabel,
fs.serialNumber >> 16, fs.serialNumber & 0xffff,
fs.getCapacity() / 0x100000, fs.getFreeSpace() / 0x100000
fs.capacity / 0x100000, fs.getFreeSpace() / 0x100000
);
else
_PRINT(STR("IDEInfoScreen.fat.error"));

View File

@ -289,7 +289,7 @@ bool App::_romRestoreWorker(void) {
util::Hash message;
message = (_file->length > regionLength)
message = (_file->size > regionLength)
? "App.romRestoreWorker.overflow"_h
: "App.romRestoreWorker.success"_h;

14238
src/vendor/ff.c vendored

File diff suppressed because it is too large Load Diff

858
src/vendor/ff.h vendored
View File

@ -1,425 +1,433 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.15 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2022, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/
/----------------------------------------------------------------------------*/
#ifndef FF_DEFINED
#define FF_DEFINED 80286 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "vendorconfig.h" /* FatFs configuration options */
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */
typedef unsigned long DWORD; /* 32-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#endif
typedef QWORD FSIZE_t;
#if FF_LBA64
typedef QWORD LBA_t;
#else
typedef DWORD LBA_t;
#endif
#else
#if FF_LBA64
#error exFAT needs to be enabled when enable 64-bit LBA
#endif
typedef DWORD FSIZE_t;
typedef DWORD LBA_t;
#endif
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] status (b0:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
#if FF_MAX_SS != FF_MIN_SS
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
#endif
#if FF_USE_LFN
WCHAR* lfnbuf; /* LFN working buffer */
#endif
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
#endif
#if FF_FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */
#if FF_FS_EXFAT
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Number of sectors per FAT */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
/* Object ID and allocation information (FFOBJID) */
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume's mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
#if FF_FS_EXFAT
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
#endif
#if FF_FS_LOCK
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
#endif
} FFOBJID;
/* File object structure (FIL) */
typedef struct {
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
BYTE flag; /* File status flags */
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
#if !FF_FS_READONLY
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
#endif
#if FF_USE_FASTSEEK
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
#endif
} FIL;
/* Directory object structure (DIR) */
typedef struct {
FFOBJID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
LBA_t sect; /* Current sector (0:Read operation has terminated) */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if FF_USE_LFN
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
#endif
#if FF_USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
#endif
} DIR;
/* File information structure (FILINFO) */
typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
#endif
} FILINFO;
/* Format parameter structure (MKFS_PARM) */
typedef struct {
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
BYTE n_fat; /* Number of FATs */
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
} MKFS_PARM;
/* File function return code (FRESULT) */
typedef enum {
FR_OK = 0, /* (0) Succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
/*--------------------------------------------------------------*/
/* FatFs Module Application Interface */
/*--------------------------------------------------------------*/
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FIL* fp); /* Truncate the file */
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
FRESULT f_closedir (DIR* dp); /* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
/* Some API fucntions are implemented as macro */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
#define f_size(fp) ((fp)->obj.objsize)
#define f_rewind(fp) f_lseek((fp), 0)
#define f_rewinddir(dp) f_readdir((dp), 0)
#define f_rmdir(path) f_unlink(path)
#define f_unmount(path) f_mount(0, path, 0)
/*--------------------------------------------------------------*/
/* Additional Functions */
/*--------------------------------------------------------------*/
/* RTC function (provided by user) */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void); /* Get current time */
#endif
/* LFN support functions (defined in ffunicode.c) */
#if FF_USE_LFN >= 1
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
/* O/S dependent functions (samples available in ffsystem.c) */
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
#if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */
void ff_mutex_give (int vol); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and Offset Address */
/*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01
#define FA_WRITE 0x02
#define FA_OPEN_EXISTING 0x00
#define FA_CREATE_NEW 0x04
#define FA_CREATE_ALWAYS 0x08
#define FA_OPEN_ALWAYS 0x10
#define FA_OPEN_APPEND 0x30
/* Fast seek controls (2nd argument of f_lseek) */
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_EXFAT 0x04
#define FM_ANY 0x07
#define FM_SFD 0x08
/* Filesystem type (FATFS.fs_type) */
#define FS_FAT12 1
#define FS_FAT16 2
#define FS_FAT32 3
#define FS_EXFAT 4
/* File attribute bits for directory entry (FILINFO.fattrib) */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_DIR 0x10 /* Directory */
#define AM_ARC 0x20 /* Archive */
#ifdef __cplusplus
}
#endif
#endif /* FF_DEFINED */
/*
* This library has been modified to add a function for building file fragment
* tables (similar to the existing "fastseek" functionality). Additionally, all
* patches available on http://elm-chan.org/fsw/ff/patches.html have been
* applied. The original license and copyright notice is below.
*/
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.15 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2022, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/
/----------------------------------------------------------------------------*/
#ifndef FF_DEFINED
#define FF_DEFINED 80286 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "vendorconfig.h" /* FatFs configuration options */
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */
typedef unsigned long DWORD; /* 32-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#endif
typedef QWORD FSIZE_t;
#if FF_LBA64
typedef QWORD LBA_t;
#else
typedef DWORD LBA_t;
#endif
#else
#if FF_LBA64
#error exFAT needs to be enabled when enable 64-bit LBA
#endif
typedef DWORD FSIZE_t;
typedef DWORD LBA_t;
#endif
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] status (b0:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
#if FF_MAX_SS != FF_MIN_SS
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
#endif
#if FF_USE_LFN
WCHAR* lfnbuf; /* LFN working buffer */
#endif
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
#endif
#if FF_FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */
#if FF_FS_EXFAT
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Number of sectors per FAT */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
/* Object ID and allocation information (FFOBJID) */
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume's mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
#if FF_FS_EXFAT
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
#endif
#if FF_FS_LOCK
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
#endif
} FFOBJID;
/* File object structure (FIL) */
typedef struct {
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
BYTE flag; /* File status flags */
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
#if !FF_FS_READONLY
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
#endif
#if FF_USE_FASTSEEK
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
#endif
} FIL;
/* Directory object structure (DIR) */
typedef struct {
FFOBJID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
LBA_t sect; /* Current sector (0:Read operation has terminated) */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if FF_USE_LFN
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
#endif
#if FF_USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
#endif
} DIR;
/* File information structure (FILINFO) */
typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
#endif
} FILINFO;
/* Format parameter structure (MKFS_PARM) */
typedef struct {
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
BYTE n_fat; /* Number of FATs */
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
} MKFS_PARM;
/* File function return code (FRESULT) */
typedef enum {
FR_OK = 0, /* (0) Succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
/*--------------------------------------------------------------*/
/* FatFs Module Application Interface */
/*--------------------------------------------------------------*/
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FIL* fp); /* Truncate the file */
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
FRESULT f_closedir (DIR* dp); /* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
FRESULT f_getlbas (FIL* fp, LBA_t* tbl, UINT ofs, UINT* len); /* Get list of LBAs used by file */
/* Some API fucntions are implemented as macro */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
#define f_size(fp) ((fp)->obj.objsize)
#define f_rewind(fp) f_lseek((fp), 0)
#define f_rewinddir(dp) f_readdir((dp), 0)
#define f_rmdir(path) f_unlink(path)
#define f_unmount(path) f_mount(0, path, 0)
/*--------------------------------------------------------------*/
/* Additional Functions */
/*--------------------------------------------------------------*/
/* RTC function (provided by user) */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void); /* Get current time */
#endif
/* LFN support functions (defined in ffunicode.c) */
#if FF_USE_LFN >= 1
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
/* O/S dependent functions (samples available in ffsystem.c) */
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
#if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */
void ff_mutex_give (int vol); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and Offset Address */
/*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01
#define FA_WRITE 0x02
#define FA_OPEN_EXISTING 0x00
#define FA_CREATE_NEW 0x04
#define FA_CREATE_ALWAYS 0x08
#define FA_OPEN_ALWAYS 0x10
#define FA_OPEN_APPEND 0x30
/* Fast seek controls (2nd argument of f_lseek) */
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_EXFAT 0x04
#define FM_ANY 0x07
#define FM_SFD 0x08
/* Filesystem type (FATFS.fs_type) */
#define FS_FAT12 1
#define FS_FAT16 2
#define FS_FAT32 3
#define FS_EXFAT 4
/* File attribute bits for directory entry (FILINFO.fattrib) */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_DIR 0x10 /* Directory */
#define AM_ARC 0x20 /* Archive */
#ifdef __cplusplus
}
#endif
#endif /* FF_DEFINED */

View File

@ -3,12 +3,7 @@
/* FatFs configuration */
#ifdef ENABLE_FILE_WRITING
#define FF_FS_READONLY 0
#else
#define FF_FS_READONLY 1
#endif
#define FF_FS_READONLY 0
#define FF_FS_MINIMIZE 0
#define FF_USE_FIND 0
#define FF_USE_MKFS 0