mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-03-01 15:30:31 +01:00
Add ISO9660 filesystem driver and ATAPI support
This commit is contained in:
parent
b921d6e7b4
commit
f9a7df6de8
@ -87,6 +87,7 @@ addExecutable(
|
|||||||
main 80080000 801dfff0
|
main 80080000 801dfff0
|
||||||
src/common/args.cpp
|
src/common/args.cpp
|
||||||
src/common/file.cpp
|
src/common/file.cpp
|
||||||
|
src/common/file9660.cpp
|
||||||
src/common/filefat.cpp
|
src/common/filefat.cpp
|
||||||
src/common/filezip.cpp
|
src/common/filezip.cpp
|
||||||
src/common/gpu.cpp
|
src/common/gpu.cpp
|
||||||
@ -125,21 +126,21 @@ addExecutable(
|
|||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
main PRIVATE
|
main PRIVATE
|
||||||
$<IF:$<CONFIG:Debug>,
|
$<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_DUMMY_DRIVER=1
|
||||||
|
ENABLE_FULL_IDE_DRIVER=1
|
||||||
ENABLE_I2C_LOGGING=1
|
ENABLE_I2C_LOGGING=1
|
||||||
,
|
|
||||||
ENABLE_LOGGING=1
|
|
||||||
ENABLE_FILE_WRITING=1
|
|
||||||
ENABLE_LOG_BUFFER=1
|
ENABLE_LOG_BUFFER=1
|
||||||
|
ENABLE_LOGGING=1
|
||||||
ENABLE_PS1_CONTROLLER=1
|
ENABLE_PS1_CONTROLLER=1
|
||||||
#ENABLE_X76F100_DRIVER=1
|
#ENABLE_X76F100_DRIVER=1
|
||||||
|
,
|
||||||
#ENABLE_DUMMY_DRIVER=1
|
#ENABLE_DUMMY_DRIVER=1
|
||||||
|
ENABLE_FULL_IDE_DRIVER=1
|
||||||
#ENABLE_I2C_LOGGING=1
|
#ENABLE_I2C_LOGGING=1
|
||||||
|
ENABLE_LOG_BUFFER=1
|
||||||
|
ENABLE_LOGGING=1
|
||||||
|
ENABLE_PS1_CONTROLLER=1
|
||||||
|
#ENABLE_X76F100_DRIVER=1
|
||||||
>
|
>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,6 +10,19 @@
|
|||||||
|
|
||||||
namespace file {
|
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 */
|
/* Base file and directory classes */
|
||||||
|
|
||||||
File::~File(void) {
|
File::~File(void) {
|
||||||
@ -27,7 +40,6 @@ size_t HostFile::read(void *output, size_t length) {
|
|||||||
return size_t(actualLength);
|
return size_t(actualLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
size_t HostFile::write(const void *input, size_t length) {
|
size_t HostFile::write(const void *input, size_t length) {
|
||||||
int actualLength = pcdrvWrite(_fd, input, 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);
|
return size_t(actualLength);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
uint64_t HostFile::seek(uint64_t offset) {
|
uint64_t HostFile::seek(uint64_t offset) {
|
||||||
int actualOffset = pcdrvSeek(_fd, int(offset), PCDRV_SEEK_SET);
|
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)
|
if (!_file)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
//assert(file->length <= SIZE_MAX);
|
//assert(_file->size <= SIZE_MAX);
|
||||||
if (!output.allocate(size_t(_file->length))) {
|
if (!output.allocate(size_t(_file->size))) {
|
||||||
_file->close();
|
_file->close();
|
||||||
delete _file;
|
delete _file;
|
||||||
return 0;
|
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) {
|
size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
||||||
|
|
||||||
if (!_file)
|
if (!_file)
|
||||||
@ -124,9 +134,6 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
|||||||
_file->close();
|
_file->close();
|
||||||
delete _file;
|
delete _file;
|
||||||
return actualLength;
|
return actualLength;
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
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) {
|
size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
||||||
|
|
||||||
if (!_file)
|
if (!_file)
|
||||||
@ -259,9 +265,6 @@ size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
|||||||
delete _file;
|
delete _file;
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HostProvider::init(void) {
|
bool HostProvider::init(void) {
|
||||||
@ -272,14 +275,10 @@ bool HostProvider::init(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = HOST;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemType HostProvider::getFileSystemType(void) {
|
|
||||||
return HOST;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
bool HostProvider::createDirectory(const char *path) {
|
bool HostProvider::createDirectory(const char *path) {
|
||||||
int fd = pcdrvCreate(path, DIRECTORY);
|
int fd = pcdrvCreate(path, DIRECTORY);
|
||||||
|
|
||||||
@ -291,7 +290,6 @@ bool HostProvider::createDirectory(const char *path) {
|
|||||||
pcdrvClose(fd);
|
pcdrvClose(fd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
File *HostProvider::openFile(const char *path, uint32_t flags) {
|
File *HostProvider::openFile(const char *path, uint32_t flags) {
|
||||||
PCDRVOpenMode mode = PCDRV_MODE_READ;
|
PCDRVOpenMode mode = PCDRV_MODE_READ;
|
||||||
@ -310,8 +308,8 @@ File *HostProvider::openFile(const char *path, uint32_t flags) {
|
|||||||
|
|
||||||
auto file = new HostFile();
|
auto file = new HostFile();
|
||||||
|
|
||||||
file->_fd = fd;
|
file->_fd = fd;
|
||||||
file->length = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
|
file->size = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
|
||||||
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
|
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
@ -47,15 +47,31 @@ enum FileAttributeFlag {
|
|||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
public:
|
public:
|
||||||
char name[MAX_NAME_LENGTH];
|
char name[MAX_NAME_LENGTH];
|
||||||
uint64_t length;
|
uint64_t size;
|
||||||
uint32_t attributes;
|
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 */
|
/* Base file and directory classes */
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
public:
|
public:
|
||||||
uint64_t length;
|
uint64_t size;
|
||||||
|
|
||||||
virtual ~File(void);
|
virtual ~File(void);
|
||||||
|
|
||||||
@ -74,9 +90,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
size_t read(void *output, size_t length);
|
size_t read(void *output, size_t length);
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
size_t write(const void *input, size_t length);
|
size_t write(const void *input, size_t length);
|
||||||
#endif
|
|
||||||
uint64_t seek(uint64_t offset);
|
uint64_t seek(uint64_t offset);
|
||||||
uint64_t tell(void) const;
|
uint64_t tell(void) const;
|
||||||
void close(void);
|
void close(void);
|
||||||
@ -96,11 +110,13 @@ extern uint32_t currentSPUOffset;
|
|||||||
|
|
||||||
class Provider {
|
class Provider {
|
||||||
public:
|
public:
|
||||||
char volumeLabel[MAX_NAME_LENGTH];
|
FileSystemType type;
|
||||||
uint32_t serialNumber;
|
uint32_t serialNumber;
|
||||||
|
uint64_t capacity;
|
||||||
|
char volumeLabel[MAX_NAME_LENGTH];
|
||||||
|
|
||||||
inline Provider(void)
|
inline Provider(void)
|
||||||
: serialNumber(0) {
|
: type(NONE), serialNumber(0), capacity(0) {
|
||||||
volumeLabel[0] = 0;
|
volumeLabel[0] = 0;
|
||||||
}
|
}
|
||||||
template<class T> inline size_t loadStruct(T &output, const char *path) {
|
template<class T> inline size_t loadStruct(T &output, const char *path) {
|
||||||
@ -113,12 +129,14 @@ public:
|
|||||||
virtual ~Provider(void);
|
virtual ~Provider(void);
|
||||||
|
|
||||||
virtual void close(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 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 Directory *openDirectory(const char *path) { return nullptr; }
|
||||||
virtual bool createDirectory(const char *path) { return false; }
|
virtual bool createDirectory(const char *path) { return false; }
|
||||||
|
|
||||||
@ -136,11 +154,7 @@ class HostProvider : public Provider {
|
|||||||
public:
|
public:
|
||||||
bool init(void);
|
bool init(void);
|
||||||
|
|
||||||
FileSystemType getFileSystemType(void);
|
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
bool createDirectory(const char *path);
|
bool createDirectory(const char *path);
|
||||||
#endif
|
|
||||||
|
|
||||||
File *openFile(const char *path, uint32_t flags);
|
File *openFile(const char *path, uint32_t flags);
|
||||||
};
|
};
|
||||||
|
366
src/common/file9660.cpp
Normal file
366
src/common/file9660.cpp
Normal 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
186
src/common/file9660.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -48,7 +48,6 @@ size_t FATFile::read(void *output, size_t length) {
|
|||||||
return uint64_t(actualLength);
|
return uint64_t(actualLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
size_t FATFile::write(const void *input, size_t length) {
|
size_t FATFile::write(const void *input, size_t length) {
|
||||||
size_t actualLength;
|
size_t actualLength;
|
||||||
auto error = f_write(&_fd, input, length, &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);
|
return uint64_t(actualLength);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
uint64_t FATFile::seek(uint64_t offset) {
|
uint64_t FATFile::seek(uint64_t offset) {
|
||||||
auto error = f_lseek(&_fd, offset);
|
auto error = f_lseek(&_fd, offset);
|
||||||
@ -93,7 +91,7 @@ bool FATDirectory::getEntry(FileInfo &output) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
__builtin_strncpy(output.name, info.fname, sizeof(output.name));
|
__builtin_strncpy(output.name, info.fname, sizeof(output.name));
|
||||||
output.length = info.fsize;
|
output.size = info.fsize;
|
||||||
output.attributes = info.fattrib;
|
output.attributes = info.fattrib;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -115,8 +113,12 @@ bool FATProvider::init(const char *drive) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = FileSystemType(_fs.fs_type);
|
||||||
|
capacity = uint64_t(_fs.n_fatent - 2) * _fs.csize * _fs.ssize;
|
||||||
|
|
||||||
f_getlabel(_drive, volumeLabel, &serialNumber);
|
f_getlabel(_drive, volumeLabel, &serialNumber);
|
||||||
LOG("FAT mount ok, drive=%s", drive);
|
|
||||||
|
LOG("mounted FAT: %s, drive=%s", volumeLabel, drive);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,23 +130,12 @@ void FATProvider::close(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = NONE;
|
||||||
|
capacity = 0;
|
||||||
|
|
||||||
LOG("FAT unmount ok, drive=%s", _drive);
|
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) {
|
uint64_t FATProvider::getFreeSpace(void) {
|
||||||
if (!_fs.fs_type)
|
if (!_fs.fs_type)
|
||||||
return 0;
|
return 0;
|
||||||
@ -162,7 +153,6 @@ uint64_t FATProvider::getFreeSpace(void) {
|
|||||||
|
|
||||||
return uint64_t(count) * uint64_t(clusterSize);
|
return uint64_t(count) * uint64_t(clusterSize);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool FATProvider::_selectDrive(void) {
|
bool FATProvider::_selectDrive(void) {
|
||||||
if (!_fs.fs_type)
|
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));
|
__builtin_strncpy(output.name, info.fname, sizeof(output.name));
|
||||||
output.length = info.fsize;
|
output.size = info.fsize;
|
||||||
output.attributes = info.fattrib;
|
output.attributes = info.fattrib;
|
||||||
|
|
||||||
return true;
|
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) {
|
Directory *FATProvider::openDirectory(const char *path) {
|
||||||
if (!_selectDrive())
|
if (!_selectDrive())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -206,7 +231,6 @@ Directory *FATProvider::openDirectory(const char *path) {
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
bool FATProvider::createDirectory(const char *path) {
|
bool FATProvider::createDirectory(const char *path) {
|
||||||
if (!_selectDrive())
|
if (!_selectDrive())
|
||||||
return false;
|
return false;
|
||||||
@ -220,7 +244,6 @@ bool FATProvider::createDirectory(const char *path) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
||||||
if (!_selectDrive())
|
if (!_selectDrive())
|
||||||
@ -235,7 +258,7 @@ File *FATProvider::openFile(const char *path, uint32_t flags) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
_file->length = f_size(&(_file->_fd));
|
_file->size = f_size(&(_file->_fd));
|
||||||
return _file;
|
return _file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +298,7 @@ extern "C" DRESULT disk_read(
|
|||||||
|
|
||||||
if (!(dev.flags & ide::DEVICE_READY))
|
if (!(dev.flags & ide::DEVICE_READY))
|
||||||
return RES_NOTRDY;
|
return RES_NOTRDY;
|
||||||
if (dev.ideRead(data, lba, count))
|
if (dev.read(data, lba, count))
|
||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
@ -290,7 +313,7 @@ extern "C" DRESULT disk_write(
|
|||||||
return RES_NOTRDY;
|
return RES_NOTRDY;
|
||||||
if (dev.flags & ide::DEVICE_READ_ONLY)
|
if (dev.flags & ide::DEVICE_READ_ONLY)
|
||||||
return RES_WRPRT;
|
return RES_WRPRT;
|
||||||
if (dev.ideWrite(data, lba, count))
|
if (dev.write(data, lba, count))
|
||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
@ -303,8 +326,10 @@ extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *data) {
|
|||||||
return RES_NOTRDY;
|
return RES_NOTRDY;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
case CTRL_SYNC:
|
case CTRL_SYNC:
|
||||||
return dev.ideFlushCache() ? RES_ERROR : RES_OK;
|
return dev.flushCache() ? RES_ERROR : RES_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
case GET_SECTOR_COUNT:
|
case GET_SECTOR_COUNT:
|
||||||
__builtin_memcpy(data, &dev.capacity, sizeof(LBA_t));
|
__builtin_memcpy(data, &dev.capacity, sizeof(LBA_t));
|
||||||
|
@ -18,9 +18,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
size_t read(void *output, size_t length);
|
size_t read(void *output, size_t length);
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
size_t write(const void *input, size_t length);
|
size_t write(const void *input, size_t length);
|
||||||
#endif
|
|
||||||
uint64_t seek(uint64_t offset);
|
uint64_t seek(uint64_t offset);
|
||||||
uint64_t tell(void) const;
|
uint64_t tell(void) const;
|
||||||
void close(void);
|
void close(void);
|
||||||
@ -58,18 +56,12 @@ public:
|
|||||||
|
|
||||||
bool init(const char *drive);
|
bool init(const char *drive);
|
||||||
void close(void);
|
void close(void);
|
||||||
|
|
||||||
FileSystemType getFileSystemType(void);
|
|
||||||
uint64_t getCapacity(void);
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
uint64_t getFreeSpace(void);
|
uint64_t getFreeSpace(void);
|
||||||
#endif
|
|
||||||
|
|
||||||
bool getFileInfo(FileInfo &output, const char *path);
|
bool getFileInfo(FileInfo &output, const char *path);
|
||||||
|
bool getFileFragments(FileFragmentTable &output, const char *path);
|
||||||
Directory *openDirectory(const char *path);
|
Directory *openDirectory(const char *path);
|
||||||
#ifdef ENABLE_FILE_WRITING
|
|
||||||
bool createDirectory(const char *path);
|
bool createDirectory(const char *path);
|
||||||
#endif
|
|
||||||
|
|
||||||
File *openFile(const char *path, uint32_t flags);
|
File *openFile(const char *path, uint32_t flags);
|
||||||
};
|
};
|
||||||
|
@ -66,14 +66,17 @@ bool ZIPProvider::init(File *file) {
|
|||||||
return _file->read(output, length);
|
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);
|
auto error = mz_zip_get_last_error(&_zip);
|
||||||
|
|
||||||
LOG("%s, file=0x%08x", _MINIZ_ZIP_ERROR_NAMES[error], file);
|
LOG("%s, file=0x%08x", _MINIZ_ZIP_ERROR_NAMES[error], file);
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +91,10 @@ bool ZIPProvider::init(const void *zipData, size_t length) {
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,17 +105,9 @@ void ZIPProvider::close(void) {
|
|||||||
_file->close();
|
_file->close();
|
||||||
delete _file;
|
delete _file;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemType ZIPProvider::getFileSystemType(void) {
|
type = NONE;
|
||||||
if (!_zip.m_zip_mode)
|
capacity = 0;
|
||||||
return NONE;
|
|
||||||
|
|
||||||
return _file ? ZIP_FILE : ZIP_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t ZIPProvider::getCapacity(void) {
|
|
||||||
return _zip.m_archive_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
|
bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
|
||||||
@ -132,7 +130,7 @@ bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
|
|||||||
ptr = info.m_filename;
|
ptr = info.m_filename;
|
||||||
|
|
||||||
__builtin_strncpy(output.name, ptr, sizeof(output.name));
|
__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;
|
output.attributes = READ_ONLY | ARCHIVE;
|
||||||
|
|
||||||
if (info.m_is_directory)
|
if (info.m_is_directory)
|
||||||
|
@ -21,9 +21,6 @@ public:
|
|||||||
bool init(const void *zipData, size_t length);
|
bool init(const void *zipData, size_t length);
|
||||||
void close(void);
|
void close(void);
|
||||||
|
|
||||||
FileSystemType getFileSystemType(void);
|
|
||||||
uint64_t getCapacity(void);
|
|
||||||
|
|
||||||
bool getFileInfo(FileInfo &output, const char *path);
|
bool getFileInfo(FileInfo &output, const char *path);
|
||||||
|
|
||||||
size_t loadData(util::Data &output, const char *path);
|
size_t loadData(util::Data &output, const char *path);
|
||||||
|
@ -26,6 +26,25 @@ static constexpr int _RESET_STATUS_TIMEOUT = 2000000;
|
|||||||
static constexpr int _DATA_STATUS_TIMEOUT = 2000000;
|
static constexpr int _DATA_STATUS_TIMEOUT = 2000000;
|
||||||
static constexpr int _DMA_TIMEOUT = 10000;
|
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[]{
|
const char *const DEVICE_ERROR_NAMES[]{
|
||||||
"NO_ERROR",
|
"NO_ERROR",
|
||||||
"UNSUPPORTED_OP",
|
"UNSUPPORTED_OP",
|
||||||
@ -37,6 +56,7 @@ const char *const DEVICE_ERROR_NAMES[]{
|
|||||||
|
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
|
|
||||||
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
static void _copyString(char *output, const uint16_t *input, size_t length) {
|
static void _copyString(char *output, const uint16_t *input, size_t length) {
|
||||||
// The strings in the identification block are byte-swapped and padded with
|
// The strings in the identification block are byte-swapped and padded with
|
||||||
// spaces. To make them printable, any span of consecutive space characters
|
// 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;
|
*(--output) = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool IdentifyBlock::validateChecksum(void) const {
|
bool IdentifyBlock::validateChecksum(void) const {
|
||||||
if ((checksum & 0xff) != 0xa5)
|
if ((checksum & 0xff) != 0xa5)
|
||||||
@ -144,6 +165,7 @@ DeviceError Device::_command(uint8_t cmd, bool drdy) {
|
|||||||
uint8_t mask = drdy ? CS0_STATUS_DRDY : 0;
|
uint8_t mask = drdy ? CS0_STATUS_DRDY : 0;
|
||||||
|
|
||||||
error = _waitForStatus(CS0_STATUS_BSY | mask, mask, _STATUS_TIMEOUT);
|
error = _waitForStatus(CS0_STATUS_BSY | mask, mask, _STATUS_TIMEOUT);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -157,6 +179,7 @@ DeviceError Device::_transferPIO(void *data, size_t length, bool write) {
|
|||||||
auto error = _waitForStatus(
|
auto error = _waitForStatus(
|
||||||
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
|
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -181,6 +204,7 @@ DeviceError Device::_transferDMA(void *data, size_t length, bool write) {
|
|||||||
auto error = _waitForStatus(
|
auto error = _waitForStatus(
|
||||||
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
|
CS0_STATUS_DRQ, CS0_STATUS_DRQ, _DATA_STATUS_TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -213,6 +237,7 @@ DeviceError Device::enumerate(void) {
|
|||||||
_select();
|
_select();
|
||||||
|
|
||||||
auto error = _waitForStatus(CS0_STATUS_BSY, 0, _RESET_STATUS_TIMEOUT);
|
auto error = _waitForStatus(CS0_STATUS_BSY, 0, _RESET_STATUS_TIMEOUT);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -225,10 +250,12 @@ DeviceError Device::enumerate(void) {
|
|||||||
flags |= DEVICE_ATAPI;
|
flags |= DEVICE_ATAPI;
|
||||||
|
|
||||||
error = _command(ATA_IDENTIFY_PACKET, false);
|
error = _command(ATA_IDENTIFY_PACKET, false);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = _transferPIO(&block, sizeof(IdentifyBlock));
|
error = _transferPIO(&block, sizeof(IdentifyBlock));
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -246,10 +273,12 @@ DeviceError Device::enumerate(void) {
|
|||||||
flags |= DEVICE_HAS_PACKET16;
|
flags |= DEVICE_HAS_PACKET16;
|
||||||
} else {
|
} else {
|
||||||
error = _command(ATA_IDENTIFY);
|
error = _command(ATA_IDENTIFY);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = _transferPIO(&block, sizeof(IdentifyBlock));
|
error = _transferPIO(&block, sizeof(IdentifyBlock));
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -265,18 +294,22 @@ DeviceError Device::enumerate(void) {
|
|||||||
flags |= DEVICE_HAS_FLUSH;
|
flags |= DEVICE_HAS_FLUSH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
_copyString(model, block.model, sizeof(block.model));
|
_copyString(model, block.model, sizeof(block.model));
|
||||||
_copyString(revision, block.revision, sizeof(block.revision));
|
_copyString(revision, block.revision, sizeof(block.revision));
|
||||||
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
||||||
|
|
||||||
LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model);
|
LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Find out the fastest PIO transfer mode supported and enable it.
|
// Find out the fastest PIO transfer mode supported and enable it.
|
||||||
int mode = block.getHighestPIOMode();
|
int mode = block.getHighestPIOMode();
|
||||||
|
|
||||||
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
||||||
_write(CS0_COUNT, (1 << 3) | mode);
|
_write(CS0_COUNT, (1 << 3) | mode);
|
||||||
|
|
||||||
error = _command(ATA_SET_FEATURES, false);
|
error = _command(ATA_SET_FEATURES, false);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -303,19 +336,21 @@ DeviceError Device::_ideReadWrite(
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (count) {
|
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);
|
auto error = _command(cmd);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
// Data must be transferred one sector at a time as the drive may
|
// Data must be transferred one sector at a time as the drive may
|
||||||
// deassert DRQ between sectors.
|
// deassert DRQ between sectors.
|
||||||
for (size_t i = length; i; i--) {
|
for (size_t i = chunkLength; i; i--) {
|
||||||
error = _transferPIO(
|
error = _transferPIO(
|
||||||
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE, write
|
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE, write
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -325,16 +360,64 @@ DeviceError Device::_ideReadWrite(
|
|||||||
error = _waitForStatus(
|
error = _waitForStatus(
|
||||||
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY, _STATUS_TIMEOUT
|
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY, _STATUS_TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
count -= length;
|
count -= chunkLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO_ERROR;
|
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();
|
_select();
|
||||||
|
|
||||||
auto error = _command(standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
|
auto error = _command(standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
|
||||||
@ -353,7 +436,7 @@ DeviceError Device::ideIdle(bool standby) {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::ideFlushCache(void) {
|
DeviceError Device::flushCache(void) {
|
||||||
if (!(flags & DEVICE_HAS_FLUSH))
|
if (!(flags & DEVICE_HAS_FLUSH))
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
//return UNSUPPORTED_OP;
|
//return UNSUPPORTED_OP;
|
||||||
@ -364,6 +447,7 @@ DeviceError Device::ideFlushCache(void) {
|
|||||||
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
|
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
DeviceError Device::atapiPacket(Packet &packet, size_t transferLength) {
|
DeviceError Device::atapiPacket(Packet &packet, size_t transferLength) {
|
||||||
if (!(flags & DEVICE_ATAPI))
|
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_L, (transferLength >> 0) & 0xff);
|
||||||
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
||||||
|
|
||||||
auto error = _command(ATA_PACKET, false);
|
auto error = _command(ATA_PACKET, false);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = _transferPIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
error = _transferPIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0, _STATUS_TIMEOUT);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,7 @@ enum ATAPISenseKey : uint8_t {
|
|||||||
SENSE_KEY_ILLEGAL_REQUEST = 0x5,
|
SENSE_KEY_ILLEGAL_REQUEST = 0x5,
|
||||||
SENSE_KEY_UNIT_ATTENTION = 0x6,
|
SENSE_KEY_UNIT_ATTENTION = 0x6,
|
||||||
SENSE_KEY_DATA_PROTECT = 0x7,
|
SENSE_KEY_DATA_PROTECT = 0x7,
|
||||||
|
SENSE_KEY_BLANK_CHECK = 0x8,
|
||||||
SENSE_KEY_ABORTED_COMMAND = 0xb,
|
SENSE_KEY_ABORTED_COMMAND = 0xb,
|
||||||
SENSE_KEY_MISCOMPARE = 0xe
|
SENSE_KEY_MISCOMPARE = 0xe
|
||||||
};
|
};
|
||||||
@ -236,7 +237,32 @@ public:
|
|||||||
int getHighestPIOMode(void) const;
|
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:
|
public:
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
uint8_t param[11];
|
uint8_t param[11];
|
||||||
@ -268,6 +294,12 @@ public:
|
|||||||
param[1] = (value >> 8) & 0xff;
|
param[1] = (value >> 8) & 0xff;
|
||||||
param[2] = (value >> 0) & 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 */
|
/* Device class */
|
||||||
@ -325,11 +357,17 @@ private:
|
|||||||
DeviceError _ideReadWrite(
|
DeviceError _ideReadWrite(
|
||||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
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:
|
public:
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
char model[41], revision[9], serialNumber[21];
|
char model[41], revision[9], serialNumber[21];
|
||||||
|
#endif
|
||||||
uint64_t capacity;
|
uint64_t capacity;
|
||||||
|
|
||||||
inline Device(uint32_t flags)
|
inline Device(uint32_t flags)
|
||||||
@ -338,19 +376,27 @@ public:
|
|||||||
inline size_t getSectorSize(void) const {
|
inline size_t getSectorSize(void) const {
|
||||||
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
inline DeviceError ideRead(void *data, uint64_t lba, size_t count) {
|
inline bool isPointerAligned(const void *ptr) {
|
||||||
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, false);
|
// DMA transfers require 4-byte alignment, while PIO transfers require
|
||||||
}
|
// 2-byte alignment.
|
||||||
inline DeviceError ideWrite(const void *data, uint64_t lba, size_t count) {
|
#if 0
|
||||||
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, true);
|
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 enumerate(void);
|
||||||
DeviceError ideIdle(bool standby = false);
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
DeviceError ideFlushCache(void);
|
DeviceError goIdle(bool standby = false);
|
||||||
|
DeviceError flushCache(void);
|
||||||
|
#endif
|
||||||
DeviceError atapiPacket(
|
DeviceError atapiPacket(
|
||||||
Packet &packet, size_t transferLength = ATAPI_SECTOR_SIZE
|
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[];
|
extern const char *const DEVICE_ERROR_NAMES[];
|
||||||
|
@ -87,7 +87,7 @@ void setRTCTime(const util::Date &value, bool stop) {
|
|||||||
//assert((value.year >= 1970) && (value.year <= 2069));
|
//assert((value.year >= 1970) && (value.year <= 2069));
|
||||||
|
|
||||||
int _year = value.year % 100;
|
int _year = value.year % 100;
|
||||||
int weekday = value.getDayOfWeek();
|
int weekday = value.getDayOfWeek() + 1;
|
||||||
|
|
||||||
int year = (_year % 10) | (((_year / 10) & 15) << 4);
|
int year = (_year % 10) | (((_year / 10) & 15) << 4);
|
||||||
int month = (value.month % 10) | (((value.month / 10) & 1) << 4);
|
int month = (value.month % 10) | (((value.month / 10) & 1) << 4);
|
||||||
@ -123,7 +123,7 @@ bool isRTCBatteryLow(void) {
|
|||||||
|
|
||||||
enum BitstreamTagType : uint8_t {
|
enum BitstreamTagType : uint8_t {
|
||||||
_TAG_SOURCE_FILE = 'a',
|
_TAG_SOURCE_FILE = 'a',
|
||||||
_TAG_CHIP_TYPE = 'b',
|
_TAG_PART_NAME = 'b',
|
||||||
_TAG_BUILD_DATE = 'c',
|
_TAG_BUILD_DATE = 'c',
|
||||||
_TAG_BUILD_TIME = 'd',
|
_TAG_BUILD_TIME = 'd',
|
||||||
_TAG_DATA = 'e'
|
_TAG_DATA = 'e'
|
||||||
@ -194,10 +194,10 @@ bool loadRawBitstream(const uint8_t *data, size_t length) {
|
|||||||
uint8_t id1 = data[1], id2 = data[4];
|
uint8_t id1 = data[1], id2 = data[4];
|
||||||
void (*writeFunc)(const uint8_t *, size_t);
|
void (*writeFunc)(const uint8_t *, size_t);
|
||||||
|
|
||||||
if (((id1 & 0x0f) == 0x04) && ((id2 & 0xf0) == 0xf0))
|
if (((id1 & 0xf0) == 0x20) && ((id2 & 0x0f) == 0x0f))
|
||||||
writeFunc = &_writeBitstreamLSB;
|
|
||||||
else if (((id1 & 0xf0) == 0x20) && ((id2 & 0x0f) == 0x0f))
|
|
||||||
writeFunc = &_writeBitstreamMSB;
|
writeFunc = &_writeBitstreamMSB;
|
||||||
|
else if (((id1 & 0x0f) == 0x04) && ((id2 & 0xf0) == 0xf0))
|
||||||
|
writeFunc = &_writeBitstreamLSB;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include "common/args.hpp"
|
#include "common/args.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/rom.hpp"
|
#include "common/rom.hpp"
|
||||||
|
@ -73,16 +73,15 @@ void IDEInfoScreen::show(ui::Context &ctx, bool goBack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FAT file system
|
// FAT file system
|
||||||
auto &fs = APP->_fileProvider;
|
auto &fs = APP->_fileProvider;
|
||||||
auto fsType = fs.getFileSystemType();
|
|
||||||
|
|
||||||
_PRINT(STR("IDEInfoScreen.fat.header"));
|
_PRINT(STR("IDEInfoScreen.fat.header"));
|
||||||
|
|
||||||
if (fsType)
|
if (fs.type)
|
||||||
_PRINT(
|
_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.serialNumber >> 16, fs.serialNumber & 0xffff,
|
||||||
fs.getCapacity() / 0x100000, fs.getFreeSpace() / 0x100000
|
fs.capacity / 0x100000, fs.getFreeSpace() / 0x100000
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
_PRINT(STR("IDEInfoScreen.fat.error"));
|
_PRINT(STR("IDEInfoScreen.fat.error"));
|
||||||
|
@ -289,7 +289,7 @@ bool App::_romRestoreWorker(void) {
|
|||||||
|
|
||||||
util::Hash message;
|
util::Hash message;
|
||||||
|
|
||||||
message = (_file->length > regionLength)
|
message = (_file->size > regionLength)
|
||||||
? "App.romRestoreWorker.overflow"_h
|
? "App.romRestoreWorker.overflow"_h
|
||||||
: "App.romRestoreWorker.success"_h;
|
: "App.romRestoreWorker.success"_h;
|
||||||
|
|
||||||
|
78
src/vendor/ff.c
vendored
78
src/vendor/ff.c
vendored
@ -1,5 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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 w/patch1 /
|
/ FatFs - Generic FAT Filesystem Module R0.15 w/patch3 /
|
||||||
/-----------------------------------------------------------------------------/
|
/-----------------------------------------------------------------------------/
|
||||||
/
|
/
|
||||||
/ Copyright (C) 2022, ChaN, all right reserved.
|
/ Copyright (C) 2022, ChaN, all right reserved.
|
||||||
@ -468,10 +475,11 @@ static WORD Fsid; /* Filesystem mount ID */
|
|||||||
static BYTE CurrVol; /* Current drive set by f_chdrive() */
|
static BYTE CurrVol; /* Current drive set by f_chdrive() */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FF_FS_LOCK != 0
|
#if FF_FS_LOCK
|
||||||
static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
|
static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
|
||||||
#if FF_FS_REENTRANT
|
#if FF_FS_REENTRANT
|
||||||
static BYTE SysLock; /* System lock flag (0:no mutex, 1:unlocked, 2:locked) */
|
static volatile BYTE SysLock; /* System lock flag to protect Files[] (0:no mutex, 1:unlocked, 2:locked) */
|
||||||
|
static volatile BYTE SysLockVolume; /* Volume id who is locking Files[] */
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -905,6 +913,7 @@ static int lock_volume ( /* 1:Ok, 0:timeout */
|
|||||||
if (rv && syslock) { /* System lock reqiered? */
|
if (rv && syslock) { /* System lock reqiered? */
|
||||||
rv = ff_mutex_take(FF_VOLUMES); /* Lock the system */
|
rv = ff_mutex_take(FF_VOLUMES); /* Lock the system */
|
||||||
if (rv) {
|
if (rv) {
|
||||||
|
SysLockVolume = fs->ldrv;
|
||||||
SysLock = 2; /* System lock succeeded */
|
SysLock = 2; /* System lock succeeded */
|
||||||
} else {
|
} else {
|
||||||
ff_mutex_give(fs->ldrv); /* Failed system lock */
|
ff_mutex_give(fs->ldrv); /* Failed system lock */
|
||||||
@ -924,7 +933,7 @@ static void unlock_volume (
|
|||||||
{
|
{
|
||||||
if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
|
if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
|
||||||
#if FF_FS_LOCK
|
#if FF_FS_LOCK
|
||||||
if (SysLock == 2) { /* Is the system locked? */
|
if (SysLock == 2 && SysLockVolume == fs->ldrv) { /* Unlock system if it has been locked by this task */
|
||||||
SysLock = 1;
|
SysLock = 1;
|
||||||
ff_mutex_give(FF_VOLUMES);
|
ff_mutex_give(FF_VOLUMES);
|
||||||
}
|
}
|
||||||
@ -5460,6 +5469,9 @@ FRESULT f_setlabel (
|
|||||||
/* Get logical drive */
|
/* Get logical drive */
|
||||||
res = mount_volume(&label, &fs, FA_WRITE);
|
res = mount_volume(&label, &fs, FA_WRITE);
|
||||||
if (res != FR_OK) LEAVE_FF(fs, res);
|
if (res != FR_OK) LEAVE_FF(fs, res);
|
||||||
|
#if FF_STR_VOLUME_ID == 2
|
||||||
|
for ( ; *label == '/'; label++) ; /* Snip the separators off */
|
||||||
|
#endif
|
||||||
|
|
||||||
#if FF_FS_EXFAT
|
#if FF_FS_EXFAT
|
||||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||||
@ -7082,3 +7094,61 @@ FRESULT f_setcp (
|
|||||||
}
|
}
|
||||||
#endif /* FF_CODE_PAGE == 0 */
|
#endif /* FF_CODE_PAGE == 0 */
|
||||||
|
|
||||||
|
|
||||||
|
#if FF_FS_MINIMIZE <= 1
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Get list of LBAs used by file */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
FRESULT f_getlbas (
|
||||||
|
FIL* fp,
|
||||||
|
LBA_t* tbl,
|
||||||
|
UINT ofs,
|
||||||
|
UINT* len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
FRESULT res;
|
||||||
|
FATFS *fs;
|
||||||
|
DWORD cl, pcl, ncl, tcl, tlen, ulen;
|
||||||
|
|
||||||
|
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||||
|
if (res == FR_OK) res = (FRESULT)fp->err;
|
||||||
|
#if FF_FS_EXFAT && !FF_FS_READONLY
|
||||||
|
if (res == FR_OK && fs->fs_type == FS_EXFAT) {
|
||||||
|
res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (res != FR_OK) LEAVE_FF(fs, res);
|
||||||
|
|
||||||
|
tlen = *len + ofs; /* Given table size and required table size */
|
||||||
|
ulen = 2;
|
||||||
|
cl = fp->obj.sclust; /* Origin of the chain */
|
||||||
|
if (cl != 0) {
|
||||||
|
do {
|
||||||
|
// TODO ofs
|
||||||
|
/* Get a fragment */
|
||||||
|
tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */
|
||||||
|
do {
|
||||||
|
pcl = cl; ncl++;
|
||||||
|
cl = get_fat(&fp->obj, cl);
|
||||||
|
if (cl <= 1) ABORT(fs, FR_INT_ERR);
|
||||||
|
if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
|
||||||
|
} while (cl == pcl + 1);
|
||||||
|
if (tbl && (ulen >= ofs) && (ulen <= tlen)) { /* Store the length and top of the fragment */
|
||||||
|
*tbl++ = (LBA_t)fs->csize * ncl;
|
||||||
|
*tbl++ = clst2sect(fs, tcl);
|
||||||
|
}
|
||||||
|
} while (cl < fs->n_fatent); /* Repeat until end of chain */
|
||||||
|
}
|
||||||
|
*len = ulen - ofs; /* Number of items used */
|
||||||
|
if (tbl && (ulen >= ofs) && (ulen <= tlen)) {
|
||||||
|
*tbl++ = 0; /* Terminate table */
|
||||||
|
*tbl = 0;
|
||||||
|
} else {
|
||||||
|
res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */
|
||||||
|
}
|
||||||
|
|
||||||
|
LEAVE_FF(fs, res);
|
||||||
|
}
|
||||||
|
#endif /* FF_FS_MINIMIZE <= 1 */
|
||||||
|
|
||||||
|
8
src/vendor/ff.h
vendored
8
src/vendor/ff.h
vendored
@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* 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 /
|
/ FatFs - Generic FAT Filesystem module R0.15 /
|
||||||
/-----------------------------------------------------------------------------/
|
/-----------------------------------------------------------------------------/
|
||||||
@ -331,6 +338,7 @@ 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_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 */
|
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 */
|
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 */
|
/* Some API fucntions are implemented as macro */
|
||||||
|
|
||||||
|
7
src/vendor/vendorconfig.h
vendored
7
src/vendor/vendorconfig.h
vendored
@ -3,12 +3,7 @@
|
|||||||
|
|
||||||
/* FatFs configuration */
|
/* FatFs configuration */
|
||||||
|
|
||||||
#ifdef ENABLE_FILE_WRITING
|
#define FF_FS_READONLY 0
|
||||||
#define FF_FS_READONLY 0
|
|
||||||
#else
|
|
||||||
#define FF_FS_READONLY 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FF_FS_MINIMIZE 0
|
#define FF_FS_MINIMIZE 0
|
||||||
#define FF_USE_FIND 0
|
#define FF_USE_FIND 0
|
||||||
#define FF_USE_MKFS 0
|
#define FF_USE_MKFS 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user