mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-03-01 07:20:42 +01:00
Bump to 1.0.1, rework storage and filesystem APIs
This commit is contained in:
parent
e013ad1d9f
commit
3512329fbc
@ -19,7 +19,7 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake")
|
||||
project(
|
||||
573in1
|
||||
LANGUAGES C CXX ASM
|
||||
VERSION 1.0.0
|
||||
VERSION 1.0.1
|
||||
DESCRIPTION "Konami System 573 maintenance tool"
|
||||
HOMEPAGE_URL "https://github.com/spicyjpeg/573in1"
|
||||
)
|
||||
@ -112,11 +112,14 @@ endfunction()
|
||||
# (0x90000 bytes).
|
||||
addPS1Executable(
|
||||
main 800a0000 801dfff0
|
||||
src/common/file/fat.cpp
|
||||
src/common/file/file.cpp
|
||||
src/common/file/iso9660.cpp
|
||||
src/common/file/misc.cpp
|
||||
src/common/file/zip.cpp
|
||||
src/common/fs/fat.cpp
|
||||
src/common/fs/file.cpp
|
||||
src/common/fs/iso9660.cpp
|
||||
src/common/fs/misc.cpp
|
||||
src/common/fs/zip.cpp
|
||||
src/common/storage/ata.cpp
|
||||
src/common/storage/atapi.cpp
|
||||
src/common/storage/device.cpp
|
||||
src/common/util/hash.cpp
|
||||
src/common/util/log.cpp
|
||||
src/common/util/misc.cpp
|
||||
@ -126,7 +129,6 @@ addPS1Executable(
|
||||
src/common/args.cpp
|
||||
src/common/gpu.cpp
|
||||
src/common/gpufont.cpp
|
||||
src/common/ide.cpp
|
||||
src/common/io.cpp
|
||||
src/common/ioboard.cpp
|
||||
src/common/pad.cpp
|
||||
@ -167,7 +169,7 @@ target_compile_definitions(
|
||||
ENABLE_CART_DATA_LOGGING=1
|
||||
ENABLE_IO_LOGGING=1
|
||||
ENABLE_ROM_LOGGING=1
|
||||
ENABLE_IDE_LOGGING=1
|
||||
ENABLE_STORAGE_LOGGING=1
|
||||
ENABLE_FS_LOGGING=1
|
||||
# Security cartridge driver options
|
||||
#ENABLE_DUMMY_CART_DRIVER=1
|
||||
@ -175,7 +177,6 @@ target_compile_definitions(
|
||||
#ENABLE_X76F100_CART_DRIVER=1
|
||||
ENABLE_ZS01_CART_DRIVER=1
|
||||
# Misc. options
|
||||
ENABLE_FULL_IDE_DRIVER=1
|
||||
ENABLE_LOG_BUFFER=1
|
||||
#ENABLE_ARGV=1
|
||||
#ENABLE_PCDRV=1
|
||||
@ -202,6 +203,9 @@ target_compile_definitions(
|
||||
function(addLauncher address stackTop)
|
||||
addPS1Executable(
|
||||
launcher${address} ${address} ${stackTop}
|
||||
#src/common/storage/ata.cpp
|
||||
#src/common/storage/atapi.cpp
|
||||
#src/common/storage/device.cpp
|
||||
src/common/util/hash.cpp
|
||||
src/common/util/misc.cpp
|
||||
src/common/args.cpp
|
||||
|
@ -5,8 +5,8 @@
|
||||
},
|
||||
|
||||
"App": {
|
||||
"ideInitWorker": {
|
||||
"init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
|
||||
"startupWorker": {
|
||||
"ideInit": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
|
||||
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
|
||||
},
|
||||
"fileInitWorker": {
|
||||
@ -320,8 +320,9 @@
|
||||
"noFS": "no disc or unsupported FS",
|
||||
|
||||
"noDeviceError": "No drives have been detected on the IDE bus. Make sure the drives are receiving power and appropriately configured as primary or secondary.\n\nPress the Test button to view debug logs.",
|
||||
"hostError": "Failed to initialize the PCDRV interface. Ensure PCDRV is enabled and configured properly at the debugger or emulator side.\n\nPress the Test button to view debug logs.",
|
||||
"ataError": "Failed to initialize the drive or access the filesystem on it. Turn off the system and make sure the drive is connected to the IDE bus, appropriately configured as primary or secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs.",
|
||||
"atapiError": "Failed to initialize the CD-ROM drive or access the filesystem on it. Ensure a disc is inserted and formatted with a valid ISO9660 filesystem. Your drive may also be incompatible with the ATAPI driver used by 573in1.\n\nPress the Test button to view debug logs.",
|
||||
"ideError": "Failed to initialize the drive or access the filesystem on it. Turn off the system and make sure the drive is connected to the IDE bus, appropriately configured as primary or secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs.",
|
||||
"noFilesError": "No files or directories have been found in the root of the selected drive's filesystem."
|
||||
},
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
},
|
||||
|
||||
"App": {
|
||||
"ideInitWorker": {
|
||||
"init": "Inizializzazione bus IDE in corso...\nNon spegnere il 573 o rimuovere dischi.",
|
||||
"startupWorker": {
|
||||
"ideInit": "Inizializzazione bus IDE in corso...\nNon spegnere il 573 o rimuovere dischi.",
|
||||
"autoboot": "Ricerca di eseguibili da avviare in corso...\nNon spegnere il 573 o rimuovere dischi."
|
||||
},
|
||||
"fileInitWorker": {
|
||||
@ -320,8 +320,9 @@
|
||||
"noFS": "nessun disco o FS non supportato",
|
||||
|
||||
"noDeviceError": "Non è stato rilevato alcun dispositivo sul bus IDE. Assicurati che le unità siano alimentate e abbiano i jumper (primario o secondario) appropriati.\n\nPremi il pulsante Test per mostrare i log di debug.",
|
||||
"hostError": "Impossibile inizializzare l'interfaccia PCDRV. Assicurati che il filesystem PCDRV sia attivato e configurato appropriatamente nel debugger o nell'emulatore.\n\nPremi il pulsante Test per mostrare i log di debug.",
|
||||
"ataError": "Impossibile inizializzare l'unità o accedere al suo filesystem. Spegni il 573 e assicurati che il dispositivo sia connesso al bus IDE, abbia i jumper appropriati e sia formattato con una sola partizione FAT16, FAT32 o exFAT.\n\nPremi il pulsante Test per mostrare i log di debug.",
|
||||
"atapiError": "Impossibile inizializzare l'unità CD-ROM o accedere al suo filesystem. Assicurati che un disco sia inserito e formattato come ISO9660. Il lettore ottico potrebbe anche essere incompatibile con il driver ATAPI usato da 573in1.\n\nPremi il pulsante Test per mostrare i log di debug.",
|
||||
"ideError": "Impossibile inizializzare l'unità o accedere al suo filesystem. Spegni il 573 e assicurati che il dispositivo sia connesso al bus IDE, abbia i jumper appropriati e sia formattato con una sola partizione FAT16, FAT32 o exFAT.\n\nPremi il pulsante Test per mostrare i log di debug.",
|
||||
"noFilesError": "Il filesystem dell'unità selezionata è vuoto e non contiene file o subdirectory."
|
||||
},
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/util/misc.hpp"
|
||||
|
||||
namespace args {
|
||||
@ -52,9 +52,9 @@ public:
|
||||
void *loadAddress;
|
||||
int device; // 0-63 = flash, -1 or -2 = IDE
|
||||
|
||||
size_t numArgs, numFragments;
|
||||
const char *executableArgs[util::MAX_EXECUTABLE_ARGS];
|
||||
file::FileFragment fragments[MAX_LAUNCHER_FRAGMENTS];
|
||||
size_t numArgs, numFragments;
|
||||
const char *executableArgs[util::MAX_EXECUTABLE_ARGS];
|
||||
fs::FileFragment fragments[MAX_LAUNCHER_FRAGMENTS];
|
||||
|
||||
ExecutableLauncherArgs(void);
|
||||
bool parseArgument(const char *arg);
|
||||
|
@ -16,17 +16,17 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/fat.hpp"
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/fat.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/misc.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "ps1/system.h"
|
||||
#include "vendor/diskio.h"
|
||||
#include "vendor/ff.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
static const char *const _FATFS_ERROR_NAMES[]{
|
||||
"OK",
|
||||
@ -120,25 +120,23 @@ void FATDirectory::close(void) {
|
||||
|
||||
/* FAT filesystem provider */
|
||||
|
||||
bool FATProvider::init(int drive) {
|
||||
bool FATProvider::init(storage::Device &dev, int mutexID) {
|
||||
if (type)
|
||||
return false;
|
||||
|
||||
_drive[0] = drive + '0';
|
||||
|
||||
auto error = f_mount(&_fs, _drive, 1);
|
||||
auto error = f_mount(&_fs, &dev, mutexID, 1);
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], _drive);
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], dev.model);
|
||||
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(&_fs, volumeLabel, &serialNumber);
|
||||
|
||||
LOG_FS("mounted FAT: %s", _drive);
|
||||
LOG_FS("mounted FAT: %s", volumeLabel);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -146,17 +144,17 @@ void FATProvider::close(void) {
|
||||
if (!type)
|
||||
return;
|
||||
|
||||
auto error = f_unmount(_drive);
|
||||
auto error = f_unmount(&_fs);
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], _drive);
|
||||
LOG_FS("%s", _FATFS_ERROR_NAMES[error]);
|
||||
return;
|
||||
}
|
||||
|
||||
type = NONE;
|
||||
capacity = 0;
|
||||
|
||||
LOG_FS("unmounted FAT: %s", _drive);
|
||||
LOG_FS("unmounted FAT: %s", volumeLabel);
|
||||
}
|
||||
|
||||
uint64_t FATProvider::getFreeSpace(void) {
|
||||
@ -164,11 +162,10 @@ uint64_t FATProvider::getFreeSpace(void) {
|
||||
return 0;
|
||||
|
||||
uint32_t count;
|
||||
FATFS *dummy;
|
||||
auto error = f_getfree(_drive, &count, &dummy);
|
||||
auto error = f_getfree(&_fs, &count);
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], _drive);
|
||||
LOG_FS("%s", _FATFS_ERROR_NAMES[error]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -177,22 +174,14 @@ uint64_t FATProvider::getFreeSpace(void) {
|
||||
return uint64_t(count) * uint64_t(clusterSize);
|
||||
}
|
||||
|
||||
bool FATProvider::_selectDrive(void) {
|
||||
if (!_fs.fs_type)
|
||||
return false;
|
||||
|
||||
return !f_chdrive(_drive);
|
||||
}
|
||||
|
||||
bool FATProvider::getFileInfo(FileInfo &output, const char *path) {
|
||||
if (!_selectDrive())
|
||||
return false;
|
||||
|
||||
FILINFO info;
|
||||
auto error = f_stat(path, &info);
|
||||
auto error = f_stat(&_fs, path, &info);
|
||||
|
||||
if (error) {
|
||||
//LOG_FS("%s: %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
#if 0
|
||||
LOG_FS("%s: %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -206,11 +195,8 @@ bool FATProvider::getFileInfo(FileInfo &output, const char *path) {
|
||||
bool FATProvider::getFileFragments(
|
||||
FileFragmentTable &output, const char *path
|
||||
) {
|
||||
if (!_selectDrive())
|
||||
return false;
|
||||
|
||||
FIL fd;
|
||||
auto error = f_open(&fd, path, READ);
|
||||
auto error = f_open(&_fs, &fd, path, READ);
|
||||
|
||||
if (!error) {
|
||||
size_t length;
|
||||
@ -231,19 +217,16 @@ bool FATProvider::getFileFragments(
|
||||
f_close(&fd);
|
||||
}
|
||||
|
||||
LOG_FS("%s, %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
LOG_FS("%s, %s", _FATFS_ERROR_NAMES[error], path);
|
||||
return false;
|
||||
}
|
||||
|
||||
Directory *FATProvider::openDirectory(const char *path) {
|
||||
if (!_selectDrive())
|
||||
return nullptr;
|
||||
|
||||
auto dir = new FATDirectory();
|
||||
auto error = f_opendir(&(dir->_fd), path);
|
||||
auto error = f_opendir(&_fs, &(dir->_fd), path);
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
||||
delete dir;
|
||||
return nullptr;
|
||||
}
|
||||
@ -252,13 +235,10 @@ Directory *FATProvider::openDirectory(const char *path) {
|
||||
}
|
||||
|
||||
bool FATProvider::createDirectory(const char *path) {
|
||||
if (!_selectDrive())
|
||||
return false;
|
||||
|
||||
auto error = f_mkdir(path);
|
||||
auto error = f_mkdir(&_fs, path);
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -266,14 +246,11 @@ bool FATProvider::createDirectory(const char *path) {
|
||||
}
|
||||
|
||||
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
||||
if (!_selectDrive())
|
||||
return nullptr;
|
||||
|
||||
auto _file = new FATFile();
|
||||
auto error = f_open(&(_file->_fd), path, uint8_t(flags));
|
||||
auto error = f_open(&_fs, &(_file->_fd), path, uint8_t(flags));
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s%s", _FATFS_ERROR_NAMES[error], _drive, path);
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
||||
delete _file;
|
||||
return nullptr;
|
||||
}
|
||||
@ -288,82 +265,65 @@ static constexpr int _MUTEX_TIMEOUT = 30000000;
|
||||
|
||||
static uint32_t _fatMutex = 0;
|
||||
|
||||
extern "C" DSTATUS disk_initialize(uint8_t drive) {
|
||||
#if 0
|
||||
auto &dev = ide::devices[drive];
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY)) {
|
||||
if (dev.enumerate())
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
#endif
|
||||
|
||||
return disk_status(drive);
|
||||
}
|
||||
|
||||
extern "C" DSTATUS disk_status(uint8_t drive) {
|
||||
auto &dev = ide::devices[drive];
|
||||
extern "C" DSTATUS disk_status(PDRV_t drive) {
|
||||
auto dev = reinterpret_cast<storage::Device *>(drive);
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
if (!(dev->type))
|
||||
flags |= STA_NOINIT;
|
||||
if (!dev.capacity)
|
||||
if (!dev->capacity)
|
||||
flags |= STA_NODISK;
|
||||
if (dev.flags & ide::DEVICE_READ_ONLY)
|
||||
if (dev->flags & storage::READ_ONLY)
|
||||
flags |= STA_PROTECT;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
extern "C" DRESULT disk_read(
|
||||
uint8_t drive, uint8_t *data, LBA_t lba, size_t count
|
||||
PDRV_t drive, uint8_t *data, LBA_t lba, size_t count
|
||||
) {
|
||||
auto &dev = ide::devices[drive];
|
||||
auto dev = reinterpret_cast<storage::Device *>(drive);
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
return RES_NOTRDY;
|
||||
if (dev.readData(data, lba, count))
|
||||
return RES_ERROR;
|
||||
|
||||
return RES_OK;
|
||||
return dev->read(data, lba, count) ? RES_ERROR : RES_OK;
|
||||
}
|
||||
|
||||
extern "C" DRESULT disk_write(
|
||||
uint8_t drive, const uint8_t *data, LBA_t lba, size_t count
|
||||
PDRV_t drive, const uint8_t *data, LBA_t lba, size_t count
|
||||
) {
|
||||
auto &dev = ide::devices[drive];
|
||||
auto dev = reinterpret_cast<storage::Device *>(drive);
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
return RES_NOTRDY;
|
||||
if (dev.flags & ide::DEVICE_READ_ONLY)
|
||||
if (dev->flags & storage::READ_ONLY)
|
||||
return RES_WRPRT;
|
||||
if (dev.writeData(data, lba, count))
|
||||
return RES_ERROR;
|
||||
|
||||
return RES_OK;
|
||||
return dev->write(data, lba, count) ? RES_ERROR : RES_OK;
|
||||
}
|
||||
|
||||
extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *data) {
|
||||
auto &dev = ide::devices[drive];
|
||||
extern "C" DRESULT disk_ioctl(PDRV_t drive, uint8_t cmd, void *data) {
|
||||
auto dev = reinterpret_cast<storage::Device *>(drive);
|
||||
auto lbas = reinterpret_cast<LBA_t *>(data);
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
if (!(dev->type))
|
||||
return RES_NOTRDY;
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||
case CTRL_SYNC:
|
||||
return dev.flushCache() ? RES_ERROR : RES_OK;
|
||||
#endif
|
||||
return dev->flushCache() ? RES_ERROR : RES_OK;
|
||||
|
||||
case GET_SECTOR_COUNT:
|
||||
__builtin_memcpy(data, &dev.capacity, sizeof(LBA_t));
|
||||
*lbas = dev->capacity;
|
||||
return RES_OK;
|
||||
|
||||
case GET_SECTOR_SIZE:
|
||||
//case GET_BLOCK_SIZE:
|
||||
*reinterpret_cast<uint16_t *>(data) = dev.getSectorSize();
|
||||
*reinterpret_cast<uint16_t *>(data) = dev->sectorLength;
|
||||
return RES_OK;
|
||||
|
||||
case GET_BLOCK_SIZE:
|
||||
*reinterpret_cast<uint32_t *>(data) = dev->sectorLength;
|
||||
return RES_OK;
|
||||
|
||||
case CTRL_TRIM:
|
||||
return dev->trim(lbas[0], lbas[1] - lbas[0]) ? RES_ERROR : RES_OK;
|
||||
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
@ -18,10 +18,11 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "vendor/ff.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* FAT file and directory classes */
|
||||
|
||||
@ -55,19 +56,13 @@ public:
|
||||
class FATProvider : public Provider {
|
||||
private:
|
||||
FATFS _fs;
|
||||
char _drive[4];
|
||||
|
||||
bool _selectDrive(void);
|
||||
|
||||
public:
|
||||
inline FATProvider(void) {
|
||||
_fs.fs_type = 0;
|
||||
_drive[0] = '#';
|
||||
_drive[1] = ':';
|
||||
_drive[2] = 0;
|
||||
}
|
||||
|
||||
bool init(int drive);
|
||||
bool init(storage::Device &dev, int mutexID);
|
||||
void close(void);
|
||||
uint64_t getFreeSpace(void);
|
||||
|
@ -18,12 +18,12 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* File fragment table */
|
||||
|
||||
@ -64,7 +64,7 @@ size_t Provider::loadData(util::Data &output, const char *path) {
|
||||
if (!_file)
|
||||
return 0;
|
||||
|
||||
//assert(_file->size <= SIZE_MAX);
|
||||
assert(_file->size <= SIZE_MAX);
|
||||
if (!output.allocate(size_t(_file->size))) {
|
||||
_file->close();
|
||||
delete _file;
|
||||
@ -84,7 +84,7 @@ size_t Provider::loadData(void *output, size_t length, const char *path) {
|
||||
if (!_file)
|
||||
return 0;
|
||||
|
||||
//assert(file->size >= length);
|
||||
assert(_file->size >= length);
|
||||
size_t actualLength = _file->read(output, length);
|
||||
|
||||
_file->close();
|
@ -23,7 +23,7 @@
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/spu.hpp"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* Common structures */
|
||||
|
@ -16,14 +16,14 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/iso9660.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/iso9660.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/ide.hpp"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* Utilities */
|
||||
|
||||
@ -219,7 +219,7 @@ bool ISOVolumeDesc::validateMagic(void) const {
|
||||
bool ISO9660File::_loadSector(uint32_t lba) {
|
||||
if (lba == _bufferedLBA)
|
||||
return true;
|
||||
if (_device->readData(_sectorBuffer, lba, 1))
|
||||
if (_dev->read(_sectorBuffer, lba, 1))
|
||||
return false;
|
||||
|
||||
_bufferedLBA = lba;
|
||||
@ -241,18 +241,18 @@ size_t ISO9660File::read(void *output, size_t 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;
|
||||
uint32_t lba = offset / _dev->sectorLength + _startLBA;
|
||||
size_t sectorOffset = offset % _dev->sectorLength;
|
||||
|
||||
// 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 (!sectorOffset && !(ptr % 4)) {
|
||||
auto numSectors = remaining / _dev->sectorLength;
|
||||
auto spanLength = numSectors * _dev->sectorLength;
|
||||
|
||||
if (numSectors > 0) {
|
||||
if (_device->readData(currentPtr, lba, numSectors))
|
||||
if (_dev->read(currentPtr, lba, numSectors))
|
||||
return false;
|
||||
|
||||
offset += spanLength;
|
||||
@ -265,7 +265,7 @@ size_t ISO9660File::read(void *output, size_t length) {
|
||||
// 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);
|
||||
util::min(remaining, _dev->sectorLength - sectorOffset);
|
||||
|
||||
if (!_loadSector(lba))
|
||||
return false;
|
||||
@ -323,9 +323,9 @@ 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))
|
||||
if (!output.allocate(numSectors * _dev->sectorLength))
|
||||
return false;
|
||||
if (_device->readData(output.ptr, lba, numSectors))
|
||||
if (_dev->read(output.ptr, lba, numSectors))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -344,7 +344,7 @@ bool ISO9660Provider::_getRecord(
|
||||
|
||||
util::Data records;
|
||||
auto numSectors =
|
||||
(root.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
|
||||
(root.length.le + _dev->sectorLength - 1) / _dev->sectorLength;
|
||||
|
||||
if (!_readData(records, root.lba.le, numSectors))
|
||||
return false;
|
||||
@ -384,43 +384,49 @@ bool ISO9660Provider::_getRecord(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ISO9660Provider::init(int drive) {
|
||||
_device = &ide::devices[drive];
|
||||
bool ISO9660Provider::init(storage::Device &dev) {
|
||||
_dev = &dev;
|
||||
|
||||
// Locate and parse the primary volume descriptor.
|
||||
ISOPrimaryVolumeDesc pvd;
|
||||
size_t numPVDSectors = util::min(
|
||||
sizeof(ISOPrimaryVolumeDesc) / _dev->sectorLength, size_t(1)
|
||||
);
|
||||
|
||||
for (
|
||||
uint32_t lba = _VOLUME_DESC_START_LBA; lba < _VOLUME_DESC_END_LBA; lba++
|
||||
) {
|
||||
if (_device->readData(&pvd, lba, 1))
|
||||
uint8_t pvdBuffer[storage::MAX_SECTOR_LENGTH];
|
||||
|
||||
if (_dev->read(pvdBuffer, lba, numPVDSectors))
|
||||
return false;
|
||||
if (!pvd.validateMagic()) {
|
||||
|
||||
auto pvd = reinterpret_cast<const ISOPrimaryVolumeDesc *>(pvdBuffer);
|
||||
|
||||
if (!pvd->validateMagic()) {
|
||||
LOG_FS("invalid ISO descriptor, lba=0x%x", lba);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pvd.type == ISO_TYPE_TERMINATOR)
|
||||
if (pvd->type == ISO_TYPE_TERMINATOR)
|
||||
break;
|
||||
if (pvd.type != ISO_TYPE_PRIMARY)
|
||||
if (pvd->type != ISO_TYPE_PRIMARY)
|
||||
continue;
|
||||
|
||||
if (pvd.isoVersion != 1) {
|
||||
LOG_FS("unsupported ISO version 0x%02x", pvd.isoVersion);
|
||||
if (pvd->isoVersion != 1) {
|
||||
LOG_FS("unsupported ISO version 0x%02x", pvd->isoVersion);
|
||||
return false;
|
||||
}
|
||||
if (pvd.sectorLength.le != ide::ATAPI_SECTOR_SIZE) {
|
||||
LOG_FS("unsupported ISO sector size %d", pvd.sectorLength.le);
|
||||
if (pvd->sectorLength.le != _dev->sectorLength) {
|
||||
LOG_FS("mismatching ISO sector size: %d", pvd->sectorLength.le);
|
||||
return false;
|
||||
}
|
||||
|
||||
_copyPVDString(volumeLabel, pvd.volume, sizeof(pvd.volume));
|
||||
__builtin_memcpy(&_root, &pvd.root, sizeof(_root));
|
||||
_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;
|
||||
capacity = uint64_t(pvd->volumeLength.le) * _dev->sectorLength;
|
||||
|
||||
LOG_FS("mounted ISO: %d", drive);
|
||||
LOG_FS("mounted ISO: %s", volumeLabel);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -431,7 +437,7 @@ bool ISO9660Provider::init(int drive) {
|
||||
void ISO9660Provider::close(void) {
|
||||
type = NONE;
|
||||
capacity = 0;
|
||||
_device = nullptr;
|
||||
_dev = nullptr;
|
||||
}
|
||||
|
||||
bool ISO9660Provider::getFileInfo(FileInfo &output, const char *path) {
|
||||
@ -459,7 +465,7 @@ bool ISO9660Provider::getFileFragments(
|
||||
auto fragment = output.as<FileFragment>();
|
||||
fragment->lba = record.lba.le;
|
||||
fragment->length =
|
||||
(record.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
|
||||
(record.length.le + _dev->sectorLength - 1) / _dev->sectorLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -473,7 +479,7 @@ Directory *ISO9660Provider::openDirectory(const char *path) {
|
||||
|
||||
auto dir = new ISO9660Directory();
|
||||
size_t dirLength =
|
||||
(record.length.le + ide::ATAPI_SECTOR_SIZE - 1) / ide::ATAPI_SECTOR_SIZE;
|
||||
(record.length.le + _dev->sectorLength - 1) / _dev->sectorLength;
|
||||
|
||||
if (!_readData(dir->_records, record.lba.le, dirLength)) {
|
||||
LOG_FS("read failed: %s", path);
|
||||
@ -496,7 +502,7 @@ File *ISO9660Provider::openFile(const char *path, uint32_t flags) {
|
||||
if (record.flags & ISO_RECORD_DIRECTORY)
|
||||
return nullptr;
|
||||
|
||||
return new ISO9660File(_device, record);
|
||||
return new ISO9660File(_dev, record);
|
||||
}
|
||||
|
||||
}
|
@ -18,11 +18,11 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/ide.hpp"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* ISO9660 data types */
|
||||
|
||||
@ -191,18 +191,18 @@ class ISO9660File : public File {
|
||||
friend class ISO9660Provider;
|
||||
|
||||
private:
|
||||
ide::Device *_device;
|
||||
uint32_t _startLBA;
|
||||
storage::Device *_dev;
|
||||
uint32_t _startLBA;
|
||||
|
||||
uint64_t _offset;
|
||||
uint32_t _bufferedLBA;
|
||||
uint8_t _sectorBuffer[ide::ATAPI_SECTOR_SIZE];
|
||||
uint8_t _sectorBuffer[storage::MAX_SECTOR_LENGTH];
|
||||
|
||||
bool _loadSector(uint32_t lba);
|
||||
|
||||
public:
|
||||
inline ISO9660File(ide::Device *device, const ISORecord &record)
|
||||
: _device(device), _startLBA(record.lba.le), _offset(0), _bufferedLBA(0) {
|
||||
inline ISO9660File(storage::Device *dev, const ISORecord &record)
|
||||
: _dev(dev), _startLBA(record.lba.le), _offset(0), _bufferedLBA(0) {
|
||||
size = record.length.le;
|
||||
}
|
||||
|
||||
@ -227,8 +227,8 @@ public:
|
||||
|
||||
class ISO9660Provider : public Provider {
|
||||
private:
|
||||
ide::Device *_device;
|
||||
ISORecord _root;
|
||||
storage::Device *_dev;
|
||||
ISORecord _root;
|
||||
|
||||
bool _readData(util::Data &output, uint32_t lba, size_t numSectors);
|
||||
bool _getRecord(
|
||||
@ -237,9 +237,9 @@ private:
|
||||
|
||||
public:
|
||||
inline ISO9660Provider(void)
|
||||
: _device(nullptr) {}
|
||||
: _dev(nullptr) {}
|
||||
|
||||
bool init(int drive);
|
||||
bool init(storage::Device &dev);
|
||||
void close(void);
|
||||
|
||||
bool getFileInfo(FileInfo &output, const char *path);
|
@ -16,14 +16,14 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/misc.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/misc.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "ps1/pcdrv.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* PCDRV utilities */
|
||||
|
||||
@ -106,6 +106,8 @@ bool HostProvider::init(void) {
|
||||
}
|
||||
|
||||
type = HOST;
|
||||
__builtin_strncpy(volumeLabel, "PCDRV", sizeof(volumeLabel));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -246,6 +248,22 @@ bool VFSProvider::unmount(const char *prefix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VFSProvider::unmount(Provider *provider) {
|
||||
for (auto &mp : _mountPoints) {
|
||||
if (mp.provider != provider)
|
||||
continue;
|
||||
|
||||
mp.prefix = 0;
|
||||
mp.pathOffset = 0;
|
||||
mp.provider = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_FS("FS was not mapped");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VFSProvider::getFileInfo(FileInfo &output, const char *path) {
|
||||
auto mp = _getMounted(path);
|
||||
|
@ -18,12 +18,12 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "ps1/pcdrv.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* PCDRV driver */
|
||||
|
||||
@ -90,6 +90,7 @@ public:
|
||||
|
||||
bool mount(const char *prefix, Provider *provider, bool force = false);
|
||||
bool unmount(const char *prefix);
|
||||
bool unmount(Provider *provider);
|
||||
|
||||
bool getFileInfo(FileInfo &output, const char *path);
|
||||
bool getFileFragments(FileFragmentTable &output, const char *path);
|
@ -16,13 +16,13 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/zip.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/zip.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "vendor/miniz.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
static const char *const _MINIZ_ZIP_ERROR_NAMES[]{
|
||||
"NO_ERROR",
|
@ -18,10 +18,10 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "vendor/miniz.h"
|
||||
|
||||
namespace file {
|
||||
namespace fs {
|
||||
|
||||
/* ZIP directory class */
|
||||
|
@ -105,12 +105,12 @@ static DeviceError _senseDataToError(const SenseData &data) {
|
||||
auto asc = data.getPackedASC();
|
||||
auto lba = data.getErrorLBA();
|
||||
|
||||
LOG_IDE("%s", _SENSE_KEY_NAMES[key]);
|
||||
LOG_IDE("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey);
|
||||
LOG_IDE("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier);
|
||||
LOG_STORAGE("%s", _SENSE_KEY_NAMES[key]);
|
||||
LOG_STORAGE("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey);
|
||||
LOG_STORAGE("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier);
|
||||
|
||||
if (lba) {
|
||||
LOG_IDE("lba=0x%08x", lba);
|
||||
LOG_STORAGE("lba=0x%08x", lba);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
@ -154,7 +154,7 @@ bool IdentifyBlock::validateChecksum(void) const {
|
||||
))) & 0xff;
|
||||
|
||||
if (value != (checksum >> 8)) {
|
||||
LOG_IDE("mismatch, exp=0x%02x, got=0x%02x", value, checksum >> 8);
|
||||
LOG_STORAGE("mismatch, exp=0x%02x, got=0x%02x", value, checksum >> 8);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ DeviceError Device::_waitForIdle(bool drdy, int timeout, bool ignoreError) {
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG_IDE("timeout, ignore=%d", ignoreError);
|
||||
LOG_STORAGE("timeout, ignore=%d", ignoreError);
|
||||
_handleTimeout();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
@ -308,7 +308,7 @@ DeviceError Device::_waitForDRQ(int timeout, bool ignoreError) {
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG_IDE("timeout, ignore=%d", ignoreError);
|
||||
LOG_STORAGE("timeout, ignore=%d", ignoreError);
|
||||
_handleTimeout();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
@ -318,7 +318,7 @@ void Device::_handleError(void) {
|
||||
lastErrorReg = _read(CS0_ERROR);
|
||||
lastCountReg = _read(CS0_COUNT);
|
||||
|
||||
LOG_IDE(
|
||||
LOG_STORAGE(
|
||||
"%d, st=0x%02x, err=0x%02x, cnt=0x%02x", getDriveIndex(), lastStatusReg,
|
||||
lastErrorReg, lastCountReg
|
||||
);
|
||||
@ -336,7 +336,7 @@ void Device::_handleTimeout(void) {
|
||||
lastErrorReg = _read(CS0_ERROR);
|
||||
lastCountReg = _read(CS0_COUNT);
|
||||
|
||||
LOG_IDE(
|
||||
LOG_STORAGE(
|
||||
"%d, st=0x%02x, err=0x%02x, cnt=0x%02x", getDriveIndex(), lastStatusReg,
|
||||
lastErrorReg, lastCountReg
|
||||
);
|
||||
@ -355,7 +355,7 @@ DeviceError Device::_resetDrive(void) {
|
||||
_select(0);
|
||||
|
||||
if (_waitForIdle(false, _DETECT_TIMEOUT, true)) {
|
||||
LOG_IDE("drive %d select timeout", getDriveIndex());
|
||||
LOG_STORAGE("drive %d select timeout", getDriveIndex());
|
||||
return NO_DRIVE;
|
||||
}
|
||||
|
||||
@ -384,7 +384,7 @@ DeviceError Device::_resetDrive(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG_IDE("drive %d not responding", getDriveIndex());
|
||||
LOG_STORAGE("drive %d not responding", getDriveIndex());
|
||||
return NO_DRIVE;
|
||||
#else
|
||||
return NO_ERROR;
|
||||
@ -511,13 +511,13 @@ DeviceError Device::_atapiRequestSense(void) {
|
||||
size_t length = _getCylinder();
|
||||
|
||||
_readPIO(&lastSenseData, length);
|
||||
LOG_IDE("data ok, length=0x%x", length);
|
||||
LOG_STORAGE("data ok, length=0x%x", length);
|
||||
} else {
|
||||
// If the request sense command fails, fall back to reading the sense
|
||||
// key from the error register.
|
||||
lastSenseData.senseKey = lastErrorReg >> 4;
|
||||
|
||||
LOG_IDE("%s", getErrorString(error));
|
||||
LOG_STORAGE("%s", getErrorString(error));
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
}
|
||||
|
||||
@ -530,7 +530,7 @@ DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) {
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
LOG_IDE("cmd=0x%02x, length=0x%x", packet.command, dataLength);
|
||||
LOG_STORAGE("cmd=0x%02x, length=0x%x", packet.command, dataLength);
|
||||
|
||||
// Keep resending the command as long as the drive reports it is in progress
|
||||
// of becoming ready (i.e. spinning up).
|
||||
@ -565,12 +565,12 @@ DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) {
|
||||
|
||||
// If an error occurred, fetch sense data to determine whether to resend
|
||||
// the command.
|
||||
LOG_IDE("%s, cmd=0x%02x", getErrorString(error), packet.command);
|
||||
LOG_STORAGE("%s, cmd=0x%02x", getErrorString(error), packet.command);
|
||||
|
||||
error = _atapiRequestSense();
|
||||
|
||||
if (error && (error != NOT_YET_READY)) {
|
||||
LOG_IDE("%s (from sense)", getErrorString(error));
|
||||
LOG_STORAGE("%s (from sense)", getErrorString(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -580,7 +580,7 @@ DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) {
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG_IDE("retry timeout, cmd=0x%02x", packet.command);
|
||||
LOG_STORAGE("retry timeout, cmd=0x%02x", packet.command);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
@ -650,7 +650,7 @@ DeviceError Device::enumerate(void) {
|
||||
|
||||
// Parse the identification block.
|
||||
if (flags & DEVICE_ATAPI) {
|
||||
LOG_IDE("ATAPI drive found");
|
||||
LOG_STORAGE("ATAPI drive found");
|
||||
|
||||
if (
|
||||
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||
@ -663,7 +663,7 @@ DeviceError Device::enumerate(void) {
|
||||
)
|
||||
flags |= DEVICE_HAS_PACKET16;
|
||||
} else {
|
||||
LOG_IDE("ATA drive found");
|
||||
LOG_STORAGE("ATA drive found");
|
||||
|
||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||
flags |= DEVICE_HAS_LBA48;
|
||||
@ -700,7 +700,7 @@ DeviceError Device::enumerate(void) {
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
LOG_IDE("drive %d ready, mode=PIO%d", getDriveIndex(), mode);
|
||||
LOG_STORAGE("drive %d ready, mode=PIO%d", getDriveIndex(), mode);
|
||||
flags |= DEVICE_READY;
|
||||
|
||||
// Make sure any pending ATAPI sense data is cleared.
|
||||
|
230
src/common/storage/ata.cpp
Normal file
230
src/common/storage/ata.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/storage/ata.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
|
||||
/*
|
||||
* Based on the following specifications:
|
||||
*
|
||||
* - "AT Attachment with Packet Interface - 6", 2001-06-26
|
||||
* - "CF+ and CompactFlash Specification Revision 3.0", 2004-12-23
|
||||
*
|
||||
* https://www.cs.utexas.edu/~dahlin/Classes/UGOS/reading/ide.html
|
||||
*/
|
||||
|
||||
namespace storage {
|
||||
|
||||
static constexpr size_t _SECTOR_LENGTH = 512;
|
||||
|
||||
/* ATA utilities */
|
||||
|
||||
DeviceError ATADevice::_setLBA(uint64_t lba, size_t count, int timeout) {
|
||||
if (flags & SUPPORTS_EXT_LBA) {
|
||||
assert(lba < (1ULL << 48));
|
||||
assert(count <= (1 << 16));
|
||||
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_set(CS0_COUNT, (count >> 8) & 0xff);
|
||||
_set(CS0_SECTOR, (lba >> 24) & 0xff);
|
||||
_set(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
||||
_set(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
||||
} else {
|
||||
assert(lba < (1ULL << 28));
|
||||
assert(count <= (1 << 8));
|
||||
|
||||
_select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15));
|
||||
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
_set(CS0_COUNT, (count >> 0) & 0xff);
|
||||
_set(CS0_SECTOR, (lba >> 0) & 0xff);
|
||||
_set(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
||||
_set(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DeviceError ATADevice::_transfer(
|
||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||
) {
|
||||
uint8_t cmd;
|
||||
size_t maxLength;
|
||||
|
||||
if (flags & SUPPORTS_EXT_LBA) {
|
||||
cmd = write ? ATA_WRITE_SECTORS_EXT : ATA_READ_SECTORS_EXT;
|
||||
maxLength = 1 << 16;
|
||||
} else {
|
||||
cmd = write ? ATA_WRITE_SECTORS : ATA_READ_SECTORS;
|
||||
maxLength = 1 << 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
size_t chunkLength = util::min(count, maxLength);
|
||||
auto error = _setLBA(lba, chunkLength);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_set(CS0_COMMAND, cmd);
|
||||
|
||||
// Data must be transferred one sector at a time as the drive may
|
||||
// deassert DRQ between sectors.
|
||||
for (size_t i = chunkLength; i; i--, ptr += _SECTOR_LENGTH) {
|
||||
auto error = _waitForDRQ();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (write)
|
||||
_writePIO(reinterpret_cast<const void *>(ptr), _SECTOR_LENGTH);
|
||||
else
|
||||
_readPIO(reinterpret_cast<void *>(ptr), _SECTOR_LENGTH);
|
||||
}
|
||||
|
||||
lba += chunkLength;
|
||||
count -= chunkLength;
|
||||
}
|
||||
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
/* ATA block device class */
|
||||
|
||||
static constexpr int _DETECT_TIMEOUT = 2500000;
|
||||
|
||||
DeviceError ATADevice::enumerate(void) {
|
||||
// NOTE: the primary drive may respond to all secondary drive register
|
||||
// accesses, with the exception of command writes, if no secondary drive is
|
||||
// actually present. A strict timeout is used in the commands below in order
|
||||
// to prevent blocking for too long.
|
||||
IDEIdentifyBlock block;
|
||||
|
||||
_set(CS0_COMMAND, ATA_IDENTIFY);
|
||||
|
||||
if (_waitForDRQ(_DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
|
||||
_readPIO(&block, sizeof(IDEIdentifyBlock));
|
||||
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
|
||||
type = ATA;
|
||||
sectorLength = _SECTOR_LENGTH;
|
||||
|
||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||
flags |= SUPPORTS_EXT_LBA;
|
||||
capacity = block.getSectorCountExt();
|
||||
} else {
|
||||
capacity = block.getSectorCount();
|
||||
}
|
||||
|
||||
if (block.commandSetFlags[1] & (1 << 12))
|
||||
flags |= SUPPORTS_FLUSH;
|
||||
|
||||
LOG_STORAGE("drive %d is ATA", _getDriveIndex());
|
||||
return _setup(block);
|
||||
}
|
||||
|
||||
DeviceError ATADevice::poll(void) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
return _waitForIdle(true);
|
||||
}
|
||||
|
||||
void ATADevice::handleInterrupt(void) {
|
||||
// TODO: use interrupts to yield instead of busy waiting
|
||||
}
|
||||
|
||||
DeviceError ATADevice::read(void *data, uint64_t lba, size_t count) {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
return _transfer(reinterpret_cast<uintptr_t>(data), lba, count, false);
|
||||
}
|
||||
|
||||
DeviceError ATADevice::write(const void *data, uint64_t lba, size_t count) {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
return _transfer(reinterpret_cast<uintptr_t>(data), lba, count, true);
|
||||
}
|
||||
|
||||
DeviceError ATADevice::trim(uint64_t lba, size_t count) {
|
||||
// TODO: implement
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
|
||||
DeviceError ATADevice::flushCache(void) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
if (!(flags & SUPPORTS_FLUSH))
|
||||
#if 0
|
||||
return UNSUPPORTED_OP;
|
||||
#else
|
||||
return NO_ERROR;
|
||||
#endif
|
||||
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_set(
|
||||
CS0_COMMAND,
|
||||
(flags & SUPPORTS_EXT_LBA) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
|
||||
);
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
DeviceError ATADevice::goIdle(bool standby) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_set(CS0_COMMAND, standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
}
|
104
src/common/storage/ata.hpp
Normal file
104
src/common/storage/ata.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/storage/device.hpp"
|
||||
|
||||
namespace storage {
|
||||
|
||||
/* ATA command definitions */
|
||||
|
||||
enum ATACommand : uint8_t {
|
||||
ATA_NOP = 0x00, // ATAPI
|
||||
ATA_DEVICE_RESET = 0x08, // ATAPI
|
||||
ATA_READ_SECTORS = 0x20, // ATA
|
||||
ATA_READ_SECTORS_EXT = 0x24, // ATA
|
||||
ATA_READ_DMA_EXT = 0x25, // ATA
|
||||
ATA_READ_DMA_QUEUED_EXT = 0x26, // ATA
|
||||
ATA_WRITE_SECTORS = 0x30, // ATA
|
||||
ATA_WRITE_SECTORS_EXT = 0x34, // ATA
|
||||
ATA_WRITE_DMA_EXT = 0x35, // ATA
|
||||
ATA_WRITE_DMA_QUEUED_EXT = 0x36, // ATA
|
||||
ATA_SEEK = 0x70, // ATA
|
||||
ATA_EXECUTE_DIAGNOSTIC = 0x90, // ATA/ATAPI
|
||||
ATA_PACKET = 0xa0, // ATAPI
|
||||
ATA_IDENTIFY_PACKET = 0xa1, // ATAPI
|
||||
ATA_SERVICE = 0xa2, // ATA/ATAPI
|
||||
ATA_DEVICE_CONFIG = 0xb1, // ATA
|
||||
ATA_ERASE_SECTORS = 0xc0, // ATA
|
||||
ATA_READ_DMA_QUEUED = 0xc7, // ATA
|
||||
ATA_READ_DMA = 0xc8, // ATA
|
||||
ATA_WRITE_DMA = 0xca, // ATA
|
||||
ATA_WRITE_DMA_QUEUED = 0xcc, // ATA
|
||||
ATA_STANDBY_IMMEDIATE = 0xe0, // ATA/ATAPI
|
||||
ATA_IDLE_IMMEDIATE = 0xe1, // ATA/ATAPI
|
||||
ATA_STANDBY = 0xe2, // ATA
|
||||
ATA_IDLE = 0xe3, // ATA
|
||||
ATA_CHECK_POWER_MODE = 0xe5, // ATA/ATAPI
|
||||
ATA_SLEEP = 0xe6, // ATA/ATAPI
|
||||
ATA_FLUSH_CACHE = 0xe7, // ATA
|
||||
ATA_FLUSH_CACHE_EXT = 0xea, // ATA
|
||||
ATA_IDENTIFY = 0xec, // ATA
|
||||
ATA_SET_FEATURES = 0xef // ATA/ATAPI
|
||||
};
|
||||
|
||||
enum ATAFeature : uint8_t {
|
||||
ATA_FEATURE_8BIT_DATA = 0x01,
|
||||
ATA_FEATURE_WRITE_CACHE = 0x02,
|
||||
ATA_FEATURE_TRANSFER_MODE = 0x03,
|
||||
ATA_FEATURE_APM = 0x05,
|
||||
ATA_FEATURE_AAM = 0x42,
|
||||
ATA_FEATURE_RELEASE_IRQ = 0x5d,
|
||||
ATA_FEATURE_SERVICE_IRQ = 0x5e,
|
||||
ATA_FEATURE_DISABLE = 0x80
|
||||
};
|
||||
|
||||
enum ATATransferModeFlag : uint8_t {
|
||||
ATA_TRANSFER_MODE_PIO_DEFAULT = 0 << 3,
|
||||
ATA_TRANSFER_MODE_PIO = 1 << 3,
|
||||
ATA_TRANSFER_MODE_DMA = 1 << 5,
|
||||
ATA_TRANSFER_MODE_UDMA = 1 << 6
|
||||
};
|
||||
|
||||
/* ATA block device class */
|
||||
|
||||
class ATADevice : public IDEDevice {
|
||||
private:
|
||||
DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||
DeviceError _transfer(
|
||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||
);
|
||||
|
||||
public:
|
||||
inline ATADevice(int index)
|
||||
: IDEDevice(index) {}
|
||||
|
||||
DeviceError enumerate(void);
|
||||
DeviceError poll(void);
|
||||
void handleInterrupt(void);
|
||||
|
||||
DeviceError read(void *data, uint64_t lba, size_t count);
|
||||
DeviceError write(const void *data, uint64_t lba, size_t count);
|
||||
DeviceError trim(uint64_t lba, size_t count);
|
||||
DeviceError flushCache(void);
|
||||
|
||||
DeviceError goIdle(bool standby = false);
|
||||
};
|
||||
|
||||
}
|
329
src/common/storage/atapi.cpp
Normal file
329
src/common/storage/atapi.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/storage/ata.hpp"
|
||||
#include "common/storage/atapi.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "ps1/system.h"
|
||||
|
||||
/*
|
||||
* Based on the following specifications:
|
||||
*
|
||||
* - "AT Attachment with Packet Interface - 6", 2001-06-26
|
||||
* - SFF-8020i "ATA Packet Interface for CD-ROMs 2.6", 1996-01-22 (seems to be
|
||||
* rather inaccurate about the IDE side of things, but some drives actually
|
||||
* implement those inaccuracies!)
|
||||
*
|
||||
* https://web.archive.org/web/20060427142409/http://www.stanford.edu/~csapuntz/blackmagic.html
|
||||
*/
|
||||
|
||||
namespace storage {
|
||||
|
||||
static constexpr size_t _SECTOR_LENGTH = 2048;
|
||||
|
||||
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",
|
||||
"UNKNOWN_9",
|
||||
"UNKNOWN_A",
|
||||
"ABORTED_COMMAND",
|
||||
"UNKNOWN_C",
|
||||
"UNKNOWN_D",
|
||||
"MISCOMPARE",
|
||||
"UNKNOWN_F"
|
||||
};
|
||||
|
||||
/* Utilities */
|
||||
|
||||
static DeviceError _senseDataToError(const ATAPISenseData &data) {
|
||||
auto key = data.senseKey & 15;
|
||||
auto asc = data.getPackedASC();
|
||||
auto lba = data.getErrorLBA();
|
||||
|
||||
LOG_STORAGE("%s", _SENSE_KEY_NAMES[key]);
|
||||
LOG_STORAGE("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey);
|
||||
LOG_STORAGE("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier);
|
||||
|
||||
if (lba)
|
||||
LOG_STORAGE("lba=0x%08x", lba);
|
||||
|
||||
switch (key) {
|
||||
case SENSE_KEY_NO_SENSE:
|
||||
case SENSE_KEY_RECOVERED_ERROR:
|
||||
return NO_ERROR;
|
||||
|
||||
case SENSE_KEY_NOT_READY:
|
||||
return (
|
||||
!asc ||
|
||||
(asc == ASC_NOT_READY) ||
|
||||
(asc == ASC_NOT_READY_IN_PROGRESS)
|
||||
)
|
||||
? NOT_YET_READY
|
||||
: DISC_ERROR;
|
||||
|
||||
case SENSE_KEY_MEDIUM_ERROR:
|
||||
case SENSE_KEY_DATA_PROTECT:
|
||||
return DISC_ERROR;
|
||||
|
||||
case SENSE_KEY_UNIT_ATTENTION:
|
||||
return (asc == ASC_RESET_OCCURRED)
|
||||
? NOT_YET_READY
|
||||
: DISC_CHANGED;
|
||||
|
||||
case SENSE_KEY_ABORTED_COMMAND:
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
default:
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATAPI error handling */
|
||||
|
||||
static constexpr int _ATAPI_READY_TIMEOUT = 30000000;
|
||||
static constexpr int _ATAPI_POLL_DELAY = 500000;
|
||||
static constexpr int _REQ_SENSE_TIMEOUT = 500000;
|
||||
|
||||
// ATAPI devices will set the CHK (ERR) status flag whenever new sense data is
|
||||
// available in response to a command. In such cases, the error should be
|
||||
// cleared by sending a "request sense" command.
|
||||
DeviceError ATAPIDevice::_requestSense(void) {
|
||||
ATAPIPacket packet;
|
||||
|
||||
packet.setRequestSense();
|
||||
_select(0);
|
||||
|
||||
auto error = _waitForIdle(false, _REQ_SENSE_TIMEOUT, true);
|
||||
|
||||
if (!error) {
|
||||
_set(CS0_FEATURES, 0);
|
||||
#if 0
|
||||
_setCylinder(sizeof(SenseData));
|
||||
#else
|
||||
_setCylinder(_SECTOR_LENGTH);
|
||||
#endif
|
||||
_set(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||
}
|
||||
if (!error) {
|
||||
_writePIO(&packet, _getPacketLength());
|
||||
|
||||
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||
}
|
||||
|
||||
util::clear(lastSenseData);
|
||||
|
||||
if (!error) {
|
||||
size_t length = _getCylinder();
|
||||
|
||||
_readPIO(&lastSenseData, length);
|
||||
LOG_STORAGE("data ok, length=0x%x", length);
|
||||
} else {
|
||||
// If the request sense command fails, fall back to reading the sense
|
||||
// key from the error register.
|
||||
lastSenseData.senseKey = _lastErrorReg >> 4;
|
||||
|
||||
LOG_STORAGE("%s", getErrorString(error));
|
||||
_set(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
}
|
||||
|
||||
return _senseDataToError(lastSenseData);
|
||||
}
|
||||
|
||||
DeviceError ATAPIDevice::_issuePacket(
|
||||
const ATAPIPacket &packet, size_t dataLength
|
||||
) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
LOG_STORAGE("cmd=0x%02x, length=0x%x", packet.command, dataLength);
|
||||
|
||||
// Keep resending the command as long as the drive reports it is in progress
|
||||
// of becoming ready (i.e. spinning up).
|
||||
for (
|
||||
int timeout = _ATAPI_READY_TIMEOUT; timeout > 0;
|
||||
timeout -= _ATAPI_POLL_DELAY
|
||||
) {
|
||||
_select(0);
|
||||
|
||||
auto error = _waitForIdle();
|
||||
|
||||
if (!error) {
|
||||
_set(CS0_FEATURES, 0);
|
||||
#if 0
|
||||
_setCylinder(dataLength);
|
||||
#else
|
||||
_setCylinder(_SECTOR_LENGTH);
|
||||
#endif
|
||||
_set(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _waitForDRQ();
|
||||
}
|
||||
if (!error) {
|
||||
_writePIO(&packet, _getPacketLength());
|
||||
|
||||
error = dataLength
|
||||
? _waitForDRQ()
|
||||
: _waitForIdle();
|
||||
}
|
||||
if (!error)
|
||||
return NO_ERROR;
|
||||
|
||||
// If an error occurred, fetch sense data to determine whether to resend
|
||||
// the command.
|
||||
LOG_STORAGE("%s, cmd=0x%02x", getErrorString(error), packet.command);
|
||||
|
||||
error = _requestSense();
|
||||
|
||||
if (error && (error != NOT_YET_READY)) {
|
||||
LOG_STORAGE("%s (from sense)", getErrorString(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
delayMicroseconds(_ATAPI_POLL_DELAY);
|
||||
}
|
||||
|
||||
LOG_STORAGE("retry timeout, cmd=0x%02x", packet.command);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
/* ATAPI block device class */
|
||||
|
||||
static constexpr int _DETECT_TIMEOUT = 2500000;
|
||||
|
||||
DeviceError ATAPIDevice::enumerate(void) {
|
||||
// NOTE: the primary drive may respond to all secondary drive register
|
||||
// accesses, with the exception of command writes, if no secondary drive is
|
||||
// actually present. A strict timeout is used in the commands below in order
|
||||
// to prevent blocking for too long.
|
||||
IDEIdentifyBlock block;
|
||||
|
||||
_set(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
||||
|
||||
if (_waitForDRQ(_DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
|
||||
_readPIO(&block, sizeof(IDEIdentifyBlock));
|
||||
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
|
||||
if (
|
||||
(block.deviceFlags & IDE_IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||
!= IDE_IDENTIFY_DEV_ATAPI_TYPE_CDROM
|
||||
) {
|
||||
LOG_STORAGE("ignoring non-CD-ROM drive %d", _getDriveIndex());
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
|
||||
// TODO: actually fetch the capacity from the drive
|
||||
type = ATAPI;
|
||||
flags = READ_ONLY;
|
||||
capacity = 0;
|
||||
sectorLength = _SECTOR_LENGTH;
|
||||
|
||||
if (
|
||||
(block.deviceFlags & IDE_IDENTIFY_DEV_PACKET_LENGTH_BITMASK)
|
||||
== IDE_IDENTIFY_DEV_PACKET_LENGTH_16
|
||||
)
|
||||
flags |= REQUIRES_EXT_PACKET;
|
||||
|
||||
LOG_STORAGE("drive %d is ATAPI", _getDriveIndex());
|
||||
return _setup(block);
|
||||
}
|
||||
|
||||
DeviceError ATAPIDevice::poll(void) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
ATAPIPacket packet;
|
||||
|
||||
packet.setTestUnitReady();
|
||||
return _issuePacket(packet);
|
||||
}
|
||||
|
||||
void ATAPIDevice::handleInterrupt(void) {
|
||||
// TODO: use interrupts to yield instead of busy waiting
|
||||
}
|
||||
|
||||
DeviceError ATAPIDevice::read(void *data, uint64_t lba, size_t count) {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
ATAPIPacket packet;
|
||||
|
||||
packet.setRead(lba, count);
|
||||
|
||||
auto error = _issuePacket(packet, _SECTOR_LENGTH);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// Data must be transferred one sector at a time as the drive may deassert
|
||||
// DRQ between sectors.
|
||||
auto ptr = reinterpret_cast<uintptr_t>(data);
|
||||
|
||||
for (; count; count--) {
|
||||
auto error = _waitForDRQ();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
size_t chunkLength = _getCylinder();
|
||||
|
||||
_readPIO(reinterpret_cast<void *>(ptr), chunkLength);
|
||||
ptr += chunkLength;
|
||||
}
|
||||
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
DeviceError ATAPIDevice::goIdle(bool standby) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
ATAPIPacket packet;
|
||||
|
||||
packet.setStartStopUnit(START_STOP_MODE_STOP_SPINDLE);
|
||||
return _issuePacket(packet);
|
||||
}
|
||||
|
||||
DeviceError ATAPIDevice::eject(bool close) {
|
||||
if (!type)
|
||||
return NO_DRIVE;
|
||||
|
||||
ATAPIPacket packet;
|
||||
auto mode =
|
||||
close ? START_STOP_MODE_CLOSE_TRAY : START_STOP_MODE_OPEN_TRAY;
|
||||
|
||||
packet.setStartStopUnit(mode);
|
||||
return _issuePacket(packet);
|
||||
}
|
||||
|
||||
}
|
264
src/common/storage/atapi.hpp
Normal file
264
src/common/storage/atapi.hpp
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
|
||||
namespace storage {
|
||||
|
||||
/* ATAPI (SCSI) command definitions */
|
||||
|
||||
enum ATAPICommand : uint8_t {
|
||||
ATAPI_TEST_UNIT_READY = 0x00,
|
||||
ATAPI_REQUEST_SENSE = 0x03,
|
||||
ATAPI_INQUIRY = 0x12,
|
||||
ATAPI_START_STOP_UNIT = 0x1b,
|
||||
ATAPI_PREVENT_REMOVAL = 0x1e,
|
||||
ATAPI_READ_CAPACITY = 0x25,
|
||||
ATAPI_READ10 = 0x28,
|
||||
ATAPI_SEEK = 0x2b,
|
||||
ATAPI_READ_SUBCHANNEL = 0x42,
|
||||
ATAPI_READ_TOC = 0x43,
|
||||
ATAPI_READ_HEADER = 0x44,
|
||||
ATAPI_PLAY_AUDIO = 0x45,
|
||||
ATAPI_PLAY_AUDIO_MSF = 0x47,
|
||||
ATAPI_PAUSE_RESUME = 0x4b,
|
||||
ATAPI_STOP = 0x4e,
|
||||
ATAPI_MODE_SELECT = 0x55,
|
||||
ATAPI_MODE_SENSE = 0x5a,
|
||||
ATAPI_LOAD_UNLOAD_CD = 0xa6,
|
||||
ATAPI_READ12 = 0xa8,
|
||||
ATAPI_READ_CD_MSF = 0xb9,
|
||||
ATAPI_SCAN = 0xba,
|
||||
ATAPI_SET_CD_SPEED = 0xbb,
|
||||
ATAPI_MECHANISM_STATUS = 0xbd,
|
||||
ATAPI_READ_CD = 0xbe
|
||||
};
|
||||
|
||||
enum ATAPIModePage : uint8_t {
|
||||
MODE_PAGE_ERROR_RECOVERY = 0x01,
|
||||
MODE_PAGE_CDROM = 0x0d,
|
||||
MODE_PAGE_CDROM_AUDIO = 0x0e,
|
||||
MODE_PAGE_CDROM_CAPABILITIES = 0x2a,
|
||||
MODE_PAGE_ALL = 0x3f
|
||||
};
|
||||
|
||||
enum ATAPIModePageType : uint8_t {
|
||||
MODE_PAGE_TYPE_CURRENT = 0,
|
||||
MODE_PAGE_TYPE_CHANGEABLE = 1,
|
||||
MODE_PAGE_TYPE_DEFAULT = 2,
|
||||
MODE_PAGE_TYPE_SAVED = 3
|
||||
};
|
||||
|
||||
enum ATAPIStartStopMode : uint8_t {
|
||||
START_STOP_MODE_STOP_SPINDLE = 0,
|
||||
START_STOP_MODE_START_SPINDLE = 1,
|
||||
START_STOP_MODE_OPEN_TRAY = 2,
|
||||
START_STOP_MODE_CLOSE_TRAY = 3
|
||||
};
|
||||
|
||||
/* ATAPI sense keys */
|
||||
|
||||
enum ATAPISenseKey : uint8_t {
|
||||
SENSE_KEY_NO_SENSE = 0x0,
|
||||
SENSE_KEY_RECOVERED_ERROR = 0x1,
|
||||
SENSE_KEY_NOT_READY = 0x2,
|
||||
SENSE_KEY_MEDIUM_ERROR = 0x3,
|
||||
SENSE_KEY_HARDWARE_ERROR = 0x4,
|
||||
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
|
||||
};
|
||||
|
||||
enum ATAPISenseQualifier : uint16_t {
|
||||
ASC_NO_SENSE_INFO = util::concat2(0x00, 0x00), // "NO ADDITIONAL SENSE INFORMATION"
|
||||
ASC_PLAY_IN_PROGRESS = util::concat2(0x00, 0x11), // "PLAY OPERATION IN PROGRESS"
|
||||
ASC_PLAY_PAUSED = util::concat2(0x00, 0x12), // "PLAY OPERATION PAUSED"
|
||||
ASC_PLAY_COMPLETED = util::concat2(0x00, 0x13), // "PLAY OPERATION SUCCESSFULLY COMPLETED"
|
||||
ASC_PLAY_ERROR = util::concat2(0x00, 0x14), // "PLAY OPERATION STOPPED DUE TO ERROR"
|
||||
ASC_NO_AUDIO_STATUS = util::concat2(0x00, 0x15), // "NO CURRENT AUDIO STATUS TO RETURN"
|
||||
ASC_MECHANICAL_ERROR = util::concat2(0x01, 0x00), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||
ASC_NO_SEEK_COMPLETE = util::concat2(0x02, 0x00), // "NO SEEK COMPLETE"
|
||||
ASC_NOT_READY = util::concat2(0x04, 0x00), // "LOGICAL DRIVE NOT READY - CAUSE NOT REPORTABLE"
|
||||
ASC_NOT_READY_IN_PROGRESS = util::concat2(0x04, 0x01), // "LOGICAL DRIVE NOT READY - IN PROGRESS OF BECOMING READY"
|
||||
ASC_NOT_READY_INIT_REQ = util::concat2(0x04, 0x02), // "LOGICAL DRIVE NOT READY - INITIALIZING COMMAND REQUIRED"
|
||||
ASC_NOT_READY_MANUAL_REQ = util::concat2(0x04, 0x03), // "LOGICAL DRIVE NOT READY - MANUAL INTERVENTION REQUIRED"
|
||||
ASC_LOAD_EJECT_FAILED = util::concat2(0x05, 0x01), // "MEDIA LOAD - EJECT FAILED"
|
||||
ASC_NO_REFERENCE_POSITION = util::concat2(0x06, 0x00), // "NO REFERENCE POSITION FOUND"
|
||||
ASC_TRACK_FOLLOW_ERROR = util::concat2(0x09, 0x00), // "TRACK FOLLOWING ERROR"
|
||||
ASC_TRACK_SERVO_FAILURE = util::concat2(0x09, 0x01), // "TRACKING SERVO FAILURE"
|
||||
ASC_FOCUS_SERVO_FAILURE = util::concat2(0x09, 0x02), // "FOCUS SERVO FAILURE"
|
||||
ASC_SPINDLE_SERVO_FAILURE = util::concat2(0x09, 0x03), // "SPINDLE SERVO FAILURE"
|
||||
ASC_UNRECOVERED_READ_ERROR = util::concat2(0x11, 0x00), // "UNRECOVERED READ ERROR"
|
||||
ASC_CIRC_UNRECOVERED_ERROR = util::concat2(0x11, 0x06), // "CIRC UNRECOVERED ERROR"
|
||||
ASC_POSITIONING_ERROR = util::concat2(0x15, 0x00), // "RANDOM POSITIONING ERROR"
|
||||
ASC_MECHANICAL_ERROR_2 = util::concat2(0x15, 0x01), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||
ASC_POSITIONING_ERROR_2 = util::concat2(0x15, 0x02), // "POSITIONING ERROR DETECTED BY READ OF MEDIUM"
|
||||
ASC_REC_DATA_NO_ECC = util::concat2(0x17, 0x00), // "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"
|
||||
ASC_REC_DATA_RETRIES = util::concat2(0x17, 0x01), // "RECOVERED DATA WITH RETRIES"
|
||||
ASC_REC_DATA_POS_OFFSET = util::concat2(0x17, 0x02), // "RECOVERED DATA WITH POSITIVE HEAD OFFSET"
|
||||
ASC_REC_DATA_NEG_OFFSET = util::concat2(0x17, 0x03), // "RECOVERED DATA WITH NEGATIVE HEAD OFFSET"
|
||||
ASC_REC_DATA_RETRIES_CIRC = util::concat2(0x17, 0x04), // "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"
|
||||
ASC_REC_DATA_PREV_SECTOR = util::concat2(0x17, 0x05), // "RECOVERED DATA USING PREVIOUS SECTOR ID"
|
||||
ASC_REC_DATA_ECC = util::concat2(0x18, 0x00), // "RECOVERED DATA WITH ERROR CORRECTION APPLIED"
|
||||
ASC_REC_DATA_ECC_RETRIES = util::concat2(0x18, 0x01), // "RECOVERED DATA WITH ERROR CORRECTION & RETRIES APPLIED"
|
||||
ASC_REC_DATA_REALLOCATED = util::concat2(0x18, 0x02), // "RECOVERED DATA - THE DATA WAS AUTO-REALLOCATED"
|
||||
ASC_REC_DATA_CIRC = util::concat2(0x18, 0x03), // "RECOVERED DATA WITH CIRC"
|
||||
ASC_REC_DATA_L_EC = util::concat2(0x18, 0x04), // "RECOVERED DATA WITH L-EC"
|
||||
ASC_PARAM_LENGTH_ERROR = util::concat2(0x1a, 0x00), // "PARAMETER LIST LENGTH ERROR"
|
||||
ASC_INVALID_COMMAND = util::concat2(0x20, 0x00), // "INVALID COMMAND OPERATION CODE"
|
||||
ASC_LBA_OUT_OF_RANGE = util::concat2(0x21, 0x00), // "LOGICAL BLOCK ADDRESS OUT OF RANGE"
|
||||
ASC_INVALID_PACKET_FIELD = util::concat2(0x24, 0x00), // "INVALID FIELD IN COMMAND PACKET"
|
||||
ASC_INVALID_PARAM_FIELD = util::concat2(0x26, 0x00), // "INVALID FIELD IN PARAMETER LIST"
|
||||
ASC_PARAM_NOT_SUPPORTED = util::concat2(0x26, 0x01), // "PARAMETER NOT SUPPORTED"
|
||||
ASC_PARAM_VALUE_INVALID = util::concat2(0x26, 0x02), // "PARAMETER VALUE INVALID"
|
||||
ASC_NOT_READY_TO_READY = util::concat2(0x28, 0x00), // "NOT READY TO READY TRANSITION, MEDIUM MAY HAVE CHANGED"
|
||||
ASC_RESET_OCCURRED = util::concat2(0x29, 0x00), // "POWER ON, RESET OR BUS DEVICE RESET OCCURRED"
|
||||
ASC_PARAMS_CHANGED = util::concat2(0x2a, 0x00), // "PARAMETERS CHANGED"
|
||||
ASC_MODE_PARAMS_CHANGED = util::concat2(0x2a, 0x01), // "MODE PARAMETERS CHANGED"
|
||||
ASC_INCOMPATIBLE_MEDIUM = util::concat2(0x30, 0x00), // "INCOMPATIBLE MEDIUM INSTALLED"
|
||||
ASC_UNKNOWN_FORMAT = util::concat2(0x30, 0x01), // "CANNOT READ MEDIUM - UNKNOWN FORMAT"
|
||||
ASC_INCOMPATIBLE_FORMAT = util::concat2(0x30, 0x02), // "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"
|
||||
ASC_SAVING_NOT_SUPPORTED = util::concat2(0x39, 0x00), // "SAVING PARAMETERS NOT SUPPORTED"
|
||||
ASC_MEDIUM_NOT_PRESENT = util::concat2(0x3a, 0x00), // "MEDIUM NOT PRESENT"
|
||||
ASC_CONDITIONS_CHANGED = util::concat2(0x3f, 0x00), // "ATAPI CD-ROM DRIVE OPERATING CONDITIONS HAVE CHANGED"
|
||||
ASC_MICROCODE_CHANGED = util::concat2(0x3f, 0x01), // "MICROCODE HAS BEEN CHANGED"
|
||||
ASC_INTERNAL_DRIVE_FAILURE = util::concat2(0x44, 0x00), // "INTERNAL ATAPI CD-ROM DRIVE FAILURE"
|
||||
ASC_OVERLAP_ATTEMPTED = util::concat2(0x4e, 0x00), // "OVERLAPPED COMMANDS ATTEMPTED"
|
||||
ASC_LOAD_EJECT_FAILED_2 = util::concat2(0x53, 0x00), // "MEDIA LOAD OR EJECT FAILED"
|
||||
ASC_REMOVAL_PREVENTED = util::concat2(0x53, 0x02), // "MEDIUM REMOVAL PREVENTED"
|
||||
ASC_UNABLE_TO_RECOVER_TOC = util::concat2(0x57, 0x00), // "UNABLE TO RECOVER TABLE OF CONTENTS"
|
||||
ASC_OPERATOR_REQUEST = util::concat2(0x5a, 0x00), // "OPERATOR REQUEST OR STATE CHANGE INPUT (UNSPECIFIED)"
|
||||
ASC_REMOVAL_REQUEST = util::concat2(0x5a, 0x01), // "OPERATOR MEDIUM REMOVAL REQUEST"
|
||||
ASC_END_OF_USER_AREA = util::concat2(0x63, 0x00), // "END OF USER AREA ENCOUNTERED ON THIS TRACK"
|
||||
ASC_ILLEGAL_TRACK_MODE = util::concat2(0x64, 0x00), // "ILLEGAL MODE FOR THIS TRACK"
|
||||
ASC_PLAY_ABORTED = util::concat2(0xb9, 0x00), // "PLAY OPERATION ABORTED"
|
||||
ASC_LOSS_OF_STREAMING = util::concat2(0xbf, 0x00) // "LOSS OF STREAMING"
|
||||
};
|
||||
|
||||
/* Data structures */
|
||||
|
||||
class alignas(uint32_t) ATAPISenseData {
|
||||
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 util::concat4(info[3], info[2], info[1], info[0]);
|
||||
}
|
||||
inline uint16_t getPackedASC(void) const {
|
||||
return util::concat2(asc, ascQualifier);
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(uint32_t) ATAPIPacket {
|
||||
public:
|
||||
uint8_t command;
|
||||
uint8_t param[11];
|
||||
uint8_t _reserved[4];
|
||||
|
||||
inline void setTestUnitReady(void) {
|
||||
util::clear(*this);
|
||||
}
|
||||
inline void setRequestSense(uint8_t additionalLength = 0) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_REQUEST_SENSE;
|
||||
param[3] = sizeof(ATAPISenseData) + additionalLength;
|
||||
}
|
||||
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_START_STOP_UNIT;
|
||||
param[3] = mode;
|
||||
}
|
||||
inline void setModeSense(
|
||||
ATAPIModePage page, size_t length,
|
||||
ATAPIModePageType type = MODE_PAGE_TYPE_CURRENT
|
||||
) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_MODE_SENSE;
|
||||
param[1] = (page & 0x3f) | (type << 6);
|
||||
param[6] = (length >> 8) & 0xff;
|
||||
param[7] = (length >> 0) & 0xff;
|
||||
}
|
||||
inline void setRead(uint32_t lba, size_t count) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_READ12;
|
||||
param[1] = (lba >> 24) & 0xff;
|
||||
param[2] = (lba >> 16) & 0xff;
|
||||
param[3] = (lba >> 8) & 0xff;
|
||||
param[4] = (lba >> 0) & 0xff;
|
||||
param[5] = (count >> 24) & 0xff;
|
||||
param[6] = (count >> 16) & 0xff;
|
||||
param[7] = (count >> 8) & 0xff;
|
||||
param[8] = (count >> 0) & 0xff;
|
||||
}
|
||||
inline void setSetCDSpeed(uint16_t value) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_SET_CD_SPEED;
|
||||
param[1] = (value >> 8) & 0xff;
|
||||
param[2] = (value >> 0) & 0xff;
|
||||
}
|
||||
};
|
||||
|
||||
/* ATAPI block device class */
|
||||
|
||||
class ATAPIDevice : public IDEDevice {
|
||||
private:
|
||||
inline size_t _getPacketLength(void) const {
|
||||
return (flags & REQUIRES_EXT_PACKET) ? 16 : 12;
|
||||
}
|
||||
|
||||
DeviceError _requestSense(void);
|
||||
DeviceError _issuePacket(const ATAPIPacket &packet, size_t dataLength = 0);
|
||||
|
||||
public:
|
||||
ATAPISenseData lastSenseData;
|
||||
|
||||
inline ATAPIDevice(int index)
|
||||
: IDEDevice(index) {}
|
||||
|
||||
DeviceError enumerate(void);
|
||||
DeviceError poll(void);
|
||||
void handleInterrupt(void);
|
||||
|
||||
DeviceError read(void *data, uint64_t lba, size_t count);
|
||||
|
||||
DeviceError goIdle(bool standby = false);
|
||||
DeviceError eject(bool close = false);
|
||||
};
|
||||
|
||||
}
|
318
src/common/storage/device.cpp
Normal file
318
src/common/storage/device.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/storage/ata.hpp"
|
||||
#include "common/storage/atapi.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "ps1/registers573.h"
|
||||
#include "ps1/system.h"
|
||||
|
||||
namespace storage {
|
||||
|
||||
const char *const DEVICE_ERROR_NAMES[]{
|
||||
"NO_ERROR",
|
||||
"UNSUPPORTED_OP",
|
||||
"NO_DRIVE",
|
||||
"NOT_YET_READY",
|
||||
"STATUS_TIMEOUT",
|
||||
"CHECKSUM_MISMATCH",
|
||||
"DRIVE_ERROR",
|
||||
"DISC_ERROR",
|
||||
"DISC_CHANGED"
|
||||
};
|
||||
|
||||
/* IDE identification block utilities */
|
||||
|
||||
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
|
||||
// at the end is replaced with null bytes.
|
||||
bool isPadding = true;
|
||||
|
||||
output += length;
|
||||
input += length / 2;
|
||||
*output = 0;
|
||||
|
||||
for (; length; length -= 2) {
|
||||
uint16_t packed = *(--input);
|
||||
char a = packed & 0xff, b = packed >> 8;
|
||||
|
||||
if (isPadding && !__builtin_isgraph(a))
|
||||
a = 0;
|
||||
else
|
||||
isPadding = false;
|
||||
if (isPadding && !__builtin_isgraph(b))
|
||||
b = 0;
|
||||
else
|
||||
isPadding = false;
|
||||
|
||||
*(--output) = a;
|
||||
*(--output) = b;
|
||||
}
|
||||
}
|
||||
|
||||
bool IDEIdentifyBlock::validateChecksum(void) const {
|
||||
if ((checksum & 0xff) != 0xa5)
|
||||
return true;
|
||||
|
||||
// FIXME: is this right?
|
||||
uint8_t value = (-int(util::sum(
|
||||
reinterpret_cast<const uint8_t *>(&deviceFlags),
|
||||
sizeof(IDEIdentifyBlock) - 1
|
||||
))) & 0xff;
|
||||
|
||||
if (value != (checksum >> 8)) {
|
||||
LOG_STORAGE("mismatch, exp=0x%02x, got=0x%02x", value, checksum >> 8);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int IDEIdentifyBlock::getHighestPIOMode(void) const {
|
||||
if (timingValidityFlags & (1 << 1)) {
|
||||
if (pioModeFlags & (1 << 1))
|
||||
return 4;
|
||||
if (pioModeFlags & (1 << 0))
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* IDE data transfers */
|
||||
|
||||
static constexpr int _DMA_TIMEOUT = 10000;
|
||||
|
||||
void IDEDevice::_readPIO(void *data, size_t length) const {
|
||||
length++;
|
||||
length /= 2;
|
||||
|
||||
util::assertAligned<uint16_t>(data);
|
||||
|
||||
auto ptr = reinterpret_cast<uint16_t *>(data);
|
||||
|
||||
for (; length > 0; length--)
|
||||
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
|
||||
}
|
||||
|
||||
void IDEDevice::_writePIO(const void *data, size_t length) const {
|
||||
length++;
|
||||
length /= 2;
|
||||
|
||||
util::assertAligned<uint16_t>(data);
|
||||
|
||||
auto ptr = reinterpret_cast<const uint16_t *>(data);
|
||||
|
||||
for (; length > 0; length--)
|
||||
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
|
||||
}
|
||||
|
||||
bool IDEDevice::_readDMA(void *data, size_t length) const {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_READ
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT);
|
||||
}
|
||||
|
||||
bool IDEDevice::_writeDMA(const void *data, size_t length) const {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_WRITE
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT);
|
||||
}
|
||||
|
||||
/* IDE status and error polling */
|
||||
|
||||
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||
static constexpr int _DRQ_TIMEOUT = 30000000;
|
||||
static constexpr int _DETECT_TIMEOUT = 2500000;
|
||||
|
||||
DeviceError IDEDevice::_setup(const IDEIdentifyBlock &block) {
|
||||
_copyString(model, block.model, sizeof(block.model));
|
||||
_copyString(revision, block.revision, sizeof(block.revision));
|
||||
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
||||
|
||||
// Find out the fastest PIO transfer mode supported and enable it.
|
||||
int mode = block.getHighestPIOMode();
|
||||
|
||||
_select(0);
|
||||
|
||||
auto error = _waitForIdle();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_set(CS0_FEATURES, ATA_FEATURE_TRANSFER_MODE);
|
||||
_set(CS0_COUNT, ATA_TRANSFER_MODE_PIO | mode);
|
||||
_set(CS0_COMMAND, ATA_SET_FEATURES);
|
||||
|
||||
error = _waitForIdle();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
LOG_STORAGE("drive %d ready, mode=PIO%d", _getDriveIndex(), mode);
|
||||
|
||||
// Make sure any pending ATAPI sense data is cleared.
|
||||
do {
|
||||
error = poll();
|
||||
} while ((error == NOT_YET_READY) || (error == DISC_CHANGED));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// Note that ATA drives will always assert DRDY when ready, but ATAPI drives
|
||||
// will not. This is an intentional feature meant to prevent ATA-only drivers
|
||||
// from misdetecting ATAPI drives.
|
||||
DeviceError IDEDevice::_waitForIdle(bool drdy, int timeout, bool ignoreError) {
|
||||
if (!timeout)
|
||||
timeout = _COMMAND_TIMEOUT;
|
||||
|
||||
for (; timeout > 0; timeout -= 10) {
|
||||
auto status = _get(CS0_STATUS);
|
||||
|
||||
// Only check for errors *after* BSY is cleared.
|
||||
if (!(status & CS0_STATUS_BSY)) {
|
||||
if ((status & CS0_STATUS_ERR) && !ignoreError) {
|
||||
_handleError();
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
|
||||
if ((status & CS0_STATUS_DRDY) || !drdy)
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
|
||||
LOG_STORAGE("timeout, ignore=%d", ignoreError);
|
||||
_handleError();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
DeviceError IDEDevice::_waitForDRQ(int timeout, bool ignoreError) {
|
||||
if (!timeout)
|
||||
timeout = _DRQ_TIMEOUT;
|
||||
|
||||
for (; timeout > 0; timeout -= 10) {
|
||||
auto status = _get(CS0_STATUS);
|
||||
|
||||
// Check for errors *before* DRQ is set but *after* BSY is cleared.
|
||||
// Confused yet?
|
||||
if (!(status & CS0_STATUS_BSY)) {
|
||||
if ((status & CS0_STATUS_ERR) && !ignoreError) {
|
||||
_handleError();
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status & CS0_STATUS_DRQ)
|
||||
return NO_ERROR;
|
||||
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
|
||||
LOG_STORAGE("timeout, ignore=%d", ignoreError);
|
||||
_handleError();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
void IDEDevice::_handleError(void) {
|
||||
_lastStatusReg = _get(CS0_STATUS);
|
||||
_lastErrorReg = _get(CS0_ERROR);
|
||||
_lastCountReg = _get(CS0_COUNT);
|
||||
|
||||
LOG_STORAGE(
|
||||
"%d, st=0x%02x, err=0x%02x, cnt=0x%02x", _getDriveIndex(),
|
||||
_lastStatusReg, _lastErrorReg, _lastCountReg
|
||||
);
|
||||
|
||||
// Issuing a device reset command to an ATAPI drive would result in the
|
||||
// error's sense data being lost.
|
||||
#if 0
|
||||
_set(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Device constructor */
|
||||
|
||||
static constexpr uint16_t _ATAPI_SIGNATURE = 0xeb14;
|
||||
|
||||
static constexpr int _SRST_SET_DELAY = 5000;
|
||||
static constexpr int _SRST_CLEAR_DELAY = 50000;
|
||||
|
||||
IDEDevice *newIDEDevice(int index) {
|
||||
#if 0
|
||||
SYS573_IDE_CS1_BASE[CS1_DEVICE_CTRL] =
|
||||
CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST;
|
||||
delayMicroseconds(_SRST_SET_DELAY);
|
||||
SYS573_IDE_CS1_BASE[CS1_DEVICE_CTRL] = CS1_DEVICE_CTRL_IEN;
|
||||
delayMicroseconds(_SRST_CLEAR_DELAY);
|
||||
#endif
|
||||
|
||||
if (index)
|
||||
SYS573_IDE_CS0_BASE[CS0_DEVICE_SEL] = CS0_DEVICE_SEL_SECONDARY;
|
||||
else
|
||||
SYS573_IDE_CS0_BASE[CS0_DEVICE_SEL] = CS0_DEVICE_SEL_PRIMARY;
|
||||
|
||||
for (int timeout = _DETECT_TIMEOUT; timeout > 0; timeout -= 10) {
|
||||
if (SYS573_IDE_CS0_BASE[CS0_STATUS] & CS0_STATUS_BSY) {
|
||||
delayMicroseconds(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto signature = util::concat2(
|
||||
SYS573_IDE_CS0_BASE[CS0_CYLINDER_L],
|
||||
SYS573_IDE_CS0_BASE[CS0_CYLINDER_H]
|
||||
);
|
||||
|
||||
auto dev = (signature == _ATAPI_SIGNATURE)
|
||||
? static_cast<IDEDevice *>(new ATAPIDevice(index))
|
||||
: static_cast<IDEDevice *>(new ATADevice(index));
|
||||
auto error = dev->enumerate();
|
||||
|
||||
if (error) {
|
||||
LOG_STORAGE("drive %d: %s", index, getErrorString(error));
|
||||
delete dev;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
LOG_STORAGE("drive %d timeout", index);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
293
src/common/storage/device.hpp
Normal file
293
src/common/storage/device.hpp
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/util/templates.hpp"
|
||||
#include "ps1/registers573.h"
|
||||
|
||||
namespace storage {
|
||||
|
||||
/* IDE register definitions */
|
||||
|
||||
enum IDECS0Register {
|
||||
CS0_DATA = 0,
|
||||
CS0_ERROR = 1,
|
||||
CS0_FEATURES = 1,
|
||||
CS0_COUNT = 2,
|
||||
CS0_SECTOR = 3,
|
||||
CS0_CYLINDER_L = 4,
|
||||
CS0_CYLINDER_H = 5,
|
||||
CS0_DEVICE_SEL = 6,
|
||||
CS0_STATUS = 7,
|
||||
CS0_COMMAND = 7
|
||||
};
|
||||
|
||||
enum IDECS1Register {
|
||||
CS1_ALT_STATUS = 6,
|
||||
CS1_DEVICE_CTRL = 6
|
||||
};
|
||||
|
||||
enum IDECS0StatusFlag : uint8_t {
|
||||
CS0_STATUS_ERR = 1 << 0, // Error (ATA)
|
||||
CS0_STATUS_CHK = 1 << 0, // Check condition (ATAPI)
|
||||
CS0_STATUS_DRQ = 1 << 3, // Data request
|
||||
CS0_STATUS_DSC = 1 << 4, // Device seek complete (ATA)
|
||||
CS0_STATUS_SERV = 1 << 4, // Service (ATAPI)
|
||||
CS0_STATUS_DF = 1 << 5, // Device fault
|
||||
CS0_STATUS_DRDY = 1 << 6, // Device ready
|
||||
CS0_STATUS_BSY = 1 << 7 // Busy
|
||||
};
|
||||
|
||||
enum IDECS0DeviceSelectFlag : uint8_t {
|
||||
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
|
||||
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
|
||||
CS0_DEVICE_SEL_LBA = 1 << 6
|
||||
};
|
||||
|
||||
enum IDECS1DeviceControlFlag : uint8_t {
|
||||
CS1_DEVICE_CTRL_IEN = 1 << 1, // Interrupt enable
|
||||
CS1_DEVICE_CTRL_SRST = 1 << 2, // Software reset
|
||||
CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48)
|
||||
};
|
||||
|
||||
enum IDECS0FeaturesFlag : uint8_t {
|
||||
CS0_FEATURES_DMA = 1 << 0, // Use DMA for data (ATAPI)
|
||||
CS0_FEATURES_OVL = 1 << 1 // Overlap (ATAPI)
|
||||
};
|
||||
|
||||
enum IDECS0CountFlag : uint8_t {
|
||||
CS0_COUNT_CD = 1 << 0, // Command or data (ATAPI)
|
||||
CS0_COUNT_IO = 1 << 1, // Input or output (ATAPI)
|
||||
CS0_COUNT_REL = 1 << 2 // Bus release (ATAPI)
|
||||
};
|
||||
|
||||
/* IDE identification block */
|
||||
|
||||
enum IDEIdentifyDeviceFlag : uint16_t {
|
||||
IDE_IDENTIFY_DEV_PACKET_LENGTH_BITMASK = 3 << 0,
|
||||
IDE_IDENTIFY_DEV_PACKET_LENGTH_12 = 0 << 0,
|
||||
IDE_IDENTIFY_DEV_PACKET_LENGTH_16 = 1 << 0,
|
||||
IDE_IDENTIFY_DEV_DRQ_TYPE_BITMASK = 3 << 5,
|
||||
IDE_IDENTIFY_DEV_DRQ_TYPE_SLOW = 0 << 5,
|
||||
IDE_IDENTIFY_DEV_DRQ_TYPE_INTERRUPT = 1 << 5,
|
||||
IDE_IDENTIFY_DEV_DRQ_TYPE_FAST = 2 << 5,
|
||||
IDE_IDENTIFY_DEV_REMOVABLE = 1 << 7,
|
||||
IDE_IDENTIFY_DEV_ATAPI_TYPE_BITMASK = 31 << 8,
|
||||
IDE_IDENTIFY_DEV_ATAPI_TYPE_CDROM = 5 << 8,
|
||||
IDE_IDENTIFY_DEV_ATAPI = 1 << 15
|
||||
};
|
||||
|
||||
enum IDEIdentifyCapabilitiesFlag : uint16_t {
|
||||
IDE_IDENTIFY_CAP_FLAG_DMA = 1 << 8,
|
||||
IDE_IDENTIFY_CAP_FLAG_LBA = 1 << 9,
|
||||
IDE_IDENTIFY_CAP_FLAG_IORDY_DISABLE = 1 << 10,
|
||||
IDE_IDENTIFY_CAP_FLAG_IORDY = 1 << 11,
|
||||
IDE_IDENTIFY_CAP_FLAG_ATAPI_OVERLAP = 1 << 13,
|
||||
IDE_IDENTIFY_CAP_FLAG_COMMAND_QUEUE = 1 << 14,
|
||||
IDE_IDENTIFY_CAP_FLAG_DMA_INTERLEAVE = 1 << 15
|
||||
};
|
||||
|
||||
class alignas(uint32_t) IDEIdentifyBlock {
|
||||
public:
|
||||
uint16_t deviceFlags; // 0
|
||||
uint16_t _reserved[9];
|
||||
uint16_t serialNumber[10]; // 10-19
|
||||
uint16_t _reserved2[3];
|
||||
uint16_t revision[4]; // 23-26
|
||||
uint16_t model[20]; // 27-46
|
||||
uint16_t _reserved3[2];
|
||||
uint16_t capabilities; // 49
|
||||
uint16_t _reserved4[3];
|
||||
uint16_t timingValidityFlags; // 53
|
||||
uint16_t _reserved5[5];
|
||||
uint16_t multiSectorSettings; // 59
|
||||
uint16_t sectorCount[2]; // 60-61
|
||||
uint16_t _reserved6;
|
||||
uint16_t dmaModeFlags; // 63
|
||||
uint16_t pioModeFlags; // 64
|
||||
uint16_t cycleTimings[4]; // 65-68
|
||||
uint16_t _reserved7[2];
|
||||
uint16_t atapiBusReleaseTime; // 71
|
||||
uint16_t atapiServiceTime; // 72
|
||||
uint16_t _reserved8[2];
|
||||
uint16_t queueDepth; // 75
|
||||
uint16_t _reserved9[4];
|
||||
uint16_t versionMajor; // 80
|
||||
uint16_t versionMinor; // 81
|
||||
uint16_t commandSetFlags[7]; // 82-88
|
||||
uint16_t secureEraseTimings[2]; // 89-90
|
||||
uint16_t currentAPMValue; // 91
|
||||
uint16_t _reserved10;
|
||||
uint16_t resetResult; // 93
|
||||
uint16_t currentAAMValue; // 94
|
||||
uint16_t streamSettings[5]; // 95-99
|
||||
uint16_t sectorCountExt[4]; // 100-103
|
||||
uint16_t _reserved11[23];
|
||||
uint16_t removableStatusFlags; // 127
|
||||
uint16_t securityStatus; // 128
|
||||
uint16_t _reserved12[31];
|
||||
uint16_t cfPowerMode; // 160
|
||||
uint16_t _reserved13[15];
|
||||
uint16_t mediaSerialNumber[30]; // 176-205
|
||||
uint16_t _reserved99[49];
|
||||
uint16_t checksum; // 255
|
||||
|
||||
inline uint32_t getSectorCount(void) const {
|
||||
return util::concat4(sectorCount[0], sectorCount[1]);
|
||||
}
|
||||
inline uint64_t getSectorCountExt(void) const {
|
||||
return util::concat8(
|
||||
sectorCountExt[0], sectorCountExt[1],
|
||||
sectorCountExt[2], sectorCountExt[3]
|
||||
);
|
||||
}
|
||||
|
||||
bool validateChecksum(void) const;
|
||||
int getHighestPIOMode(void) const;
|
||||
};
|
||||
|
||||
/* Base block device class */
|
||||
|
||||
static constexpr size_t MAX_SECTOR_LENGTH = 2048;
|
||||
|
||||
enum DeviceType : uint8_t {
|
||||
NONE = 0,
|
||||
ATA = 1,
|
||||
ATAPI = 2
|
||||
};
|
||||
|
||||
enum DeviceFlag {
|
||||
READ_ONLY = 1 << 0,
|
||||
SUPPORTS_TRIM = 1 << 1,
|
||||
SUPPORTS_FLUSH = 1 << 2,
|
||||
SUPPORTS_EXT_LBA = 1 << 3,
|
||||
|
||||
IS_SECONDARY = 1 << 4,
|
||||
REQUIRES_EXT_PACKET = 1 << 5
|
||||
};
|
||||
|
||||
enum DeviceError {
|
||||
NO_ERROR = 0,
|
||||
UNSUPPORTED_OP = 1,
|
||||
NO_DRIVE = 2,
|
||||
NOT_YET_READY = 3,
|
||||
STATUS_TIMEOUT = 4,
|
||||
CHECKSUM_MISMATCH = 5,
|
||||
DRIVE_ERROR = 6,
|
||||
DISC_ERROR = 7,
|
||||
DISC_CHANGED = 8
|
||||
};
|
||||
|
||||
class Device {
|
||||
public:
|
||||
DeviceType type;
|
||||
uint8_t flags;
|
||||
size_t sectorLength;
|
||||
uint64_t capacity;
|
||||
|
||||
char model[48], revision[12], serialNumber[24];
|
||||
|
||||
inline Device(uint8_t flags = 0)
|
||||
: type(NONE), flags(flags), sectorLength(0), capacity(0) {}
|
||||
|
||||
virtual ~Device(void) {}
|
||||
virtual DeviceError enumerate(void) { return UNSUPPORTED_OP; }
|
||||
virtual DeviceError poll(void) { return UNSUPPORTED_OP; }
|
||||
virtual void handleInterrupt(void) {}
|
||||
|
||||
virtual DeviceError read(void *data, uint64_t lba, size_t count) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual DeviceError write(const void *data, uint64_t lba, size_t count) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual DeviceError trim(uint64_t lba, size_t count) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual DeviceError flushCache(void) { return UNSUPPORTED_OP; }
|
||||
|
||||
virtual DeviceError goIdle(bool standby = false) { return UNSUPPORTED_OP; }
|
||||
virtual DeviceError eject(bool close = false) { return UNSUPPORTED_OP; }
|
||||
};
|
||||
|
||||
extern const char *const DEVICE_ERROR_NAMES[];
|
||||
|
||||
static inline const char *getErrorString(DeviceError error) {
|
||||
return DEVICE_ERROR_NAMES[error];
|
||||
}
|
||||
|
||||
/* Base IDE (ATA/ATAPI) block device class */
|
||||
|
||||
class IDEDevice : public Device {
|
||||
private:
|
||||
void _handleError(void);
|
||||
|
||||
protected:
|
||||
uint8_t _lastStatusReg, _lastErrorReg, _lastCountReg;
|
||||
|
||||
inline int _getDriveIndex(void) const {
|
||||
return (flags / IS_SECONDARY) & 1;
|
||||
}
|
||||
|
||||
inline void _set(IDECS0Register reg, uint8_t value) const {
|
||||
SYS573_IDE_CS0_BASE[reg] = value;
|
||||
}
|
||||
inline void _set(IDECS1Register reg, uint8_t value) const {
|
||||
SYS573_IDE_CS1_BASE[reg] = value;
|
||||
}
|
||||
inline uint8_t _get(IDECS0Register reg) const {
|
||||
return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff);
|
||||
}
|
||||
inline uint8_t _get(IDECS1Register reg) const {
|
||||
return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff);
|
||||
}
|
||||
|
||||
inline void _select(uint8_t selFlags) const {
|
||||
if (flags & IS_SECONDARY)
|
||||
_set(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||
else
|
||||
_set(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||
}
|
||||
inline void _setCylinder(uint16_t value) const {
|
||||
_set(CS0_CYLINDER_L, (value >> 0) & 0xff);
|
||||
_set(CS0_CYLINDER_H, (value >> 8) & 0xff);
|
||||
}
|
||||
inline uint16_t _getCylinder(void) const {
|
||||
return util::concat2(_get(CS0_CYLINDER_L), _get(CS0_CYLINDER_H));
|
||||
}
|
||||
|
||||
void _readPIO(void *data, size_t length) const;
|
||||
void _writePIO(const void *data, size_t length) const;
|
||||
bool _readDMA(void *data, size_t length) const;
|
||||
bool _writeDMA(const void *data, size_t length) const;
|
||||
|
||||
DeviceError _setup(const IDEIdentifyBlock &block);
|
||||
DeviceError _waitForIdle(
|
||||
bool drdy = false, int timeout = 0, bool ignoreError = false
|
||||
);
|
||||
DeviceError _waitForDRQ(int timeout = 0, bool ignoreError = false);
|
||||
|
||||
public:
|
||||
inline IDEDevice(int index)
|
||||
: Device(index * IS_SECONDARY) {}
|
||||
};
|
||||
|
||||
IDEDevice *newIDEDevice(int index);
|
||||
|
||||
}
|
@ -100,10 +100,10 @@ extern Logger logger;
|
||||
#define LOG_ROM(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_IDE_LOGGING
|
||||
#define LOG_IDE(fmt, ...) LOG("ide", fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#ifdef ENABLE_STORAGE_LOGGING
|
||||
#define LOG_STORAGE(fmt, ...) LOG("storage", fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_IDE(fmt, ...)
|
||||
#define LOG_STORAGE(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FS_LOGGING
|
||||
|
@ -16,17 +16,17 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/fat.hpp"
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/iso9660.hpp"
|
||||
#include "common/file/misc.hpp"
|
||||
#include "common/file/zip.hpp"
|
||||
#include "common/fs/fat.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/iso9660.hpp"
|
||||
#include "common/fs/misc.hpp"
|
||||
#include "common/fs/zip.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/misc.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/defs.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/spu.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
@ -87,7 +87,8 @@ const char *const IDE_MOUNT_POINTS[]{ "ide0:", "ide1:" };
|
||||
|
||||
FileIOManager::FileIOManager(void)
|
||||
: _resourceFile(nullptr), resourcePtr(nullptr), resourceLength(0) {
|
||||
__builtin_memset(ide, 0, sizeof(ide));
|
||||
__builtin_memset(ideDevices, 0, sizeof(ideDevices));
|
||||
__builtin_memset(ideProviders, 0, sizeof(ideProviders));
|
||||
|
||||
vfs.mount("resource:", &resource);
|
||||
#ifdef ENABLE_PCDRV
|
||||
@ -96,53 +97,72 @@ FileIOManager::FileIOManager(void)
|
||||
}
|
||||
|
||||
void FileIOManager::initIDE(void) {
|
||||
closeIDE();
|
||||
io::resetIDEDevices();
|
||||
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
auto &dev = ide::devices[i];
|
||||
for (size_t i = 0; i < util::countOf(ideDevices); i++) {
|
||||
auto dev = ideDevices[i];
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
if (dev)
|
||||
delete dev;
|
||||
|
||||
ideDevices[i] = storage::newIDEDevice(i);
|
||||
}
|
||||
}
|
||||
|
||||
void FileIOManager::mountIDE(void) {
|
||||
unmountIDE();
|
||||
|
||||
for (size_t i = 0; i < util::countOf(ideProviders); i++) {
|
||||
auto dev = ideDevices[i];
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
if (!(dev->type))
|
||||
continue;
|
||||
|
||||
// Note that calling vfs.mount() multiple times will *not* update any
|
||||
// already mounted device, so if two hard drives or CD-ROMs are present
|
||||
// the hdd:/cdrom: prefix will be assigned to the first one.
|
||||
if (dev.flags & ide::DEVICE_ATAPI) {
|
||||
auto iso = new file::ISO9660Provider();
|
||||
// TODO: actually detect the filesystem rather than assuming FAT or
|
||||
// ISO9660 depending on drive type
|
||||
if (dev->type == storage::ATAPI) {
|
||||
auto iso = new fs::ISO9660Provider();
|
||||
|
||||
if (!iso->init(i)) {
|
||||
if (!iso->init(*dev)) {
|
||||
delete iso;
|
||||
continue;
|
||||
}
|
||||
|
||||
ide[i] = iso;
|
||||
ideProviders[i] = iso;
|
||||
vfs.mount("cdrom:", iso);
|
||||
} else {
|
||||
auto fat = new file::FATProvider();
|
||||
auto fat = new fs::FATProvider();
|
||||
|
||||
if (!fat->init(i)) {
|
||||
if (!fat->init(*dev, i)) {
|
||||
delete fat;
|
||||
continue;
|
||||
}
|
||||
|
||||
ide[i] = fat;
|
||||
ideProviders[i] = fat;
|
||||
vfs.mount("hdd:", fat);
|
||||
}
|
||||
|
||||
vfs.mount(IDE_MOUNT_POINTS[i], ide[i], true);
|
||||
vfs.mount(IDE_MOUNT_POINTS[i], ideProviders[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
void FileIOManager::closeIDE(void) {
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
if (!ide[i])
|
||||
void FileIOManager::unmountIDE(void) {
|
||||
for (size_t i = 0; i < util::countOf(ideProviders); i++) {
|
||||
auto provider = ideProviders[i];
|
||||
|
||||
if (!provider)
|
||||
continue;
|
||||
|
||||
ide[i]->close();
|
||||
delete ide[i];
|
||||
ide[i] = nullptr;
|
||||
provider->close();
|
||||
vfs.unmount(provider);
|
||||
|
||||
vfs.unmount(IDE_MOUNT_POINTS[i]);
|
||||
delete provider;
|
||||
ideProviders[i] = nullptr;
|
||||
}
|
||||
|
||||
vfs.unmount("cdrom:");
|
||||
@ -153,7 +173,7 @@ bool FileIOManager::loadResourceFile(const char *path) {
|
||||
closeResourceFile();
|
||||
|
||||
if (path)
|
||||
_resourceFile = vfs.openFile(path, file::READ);
|
||||
_resourceFile = vfs.openFile(path, fs::READ);
|
||||
|
||||
// Fall back to the default in-memory resource archive in case of failure.
|
||||
if (_resourceFile) {
|
||||
@ -250,18 +270,18 @@ void App::_loadResources(void) {
|
||||
res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim");
|
||||
res.loadData(_stringTable, "assets/lang/en.lang");
|
||||
|
||||
file::currentSPUOffset = spu::DUMMY_BLOCK_END;
|
||||
fs::currentSPUOffset = spu::DUMMY_BLOCK_END;
|
||||
|
||||
for (int i = 0; i < ui::NUM_UI_SOUNDS; i++)
|
||||
res.loadVAG(_ctx.sounds[i], _UI_SOUND_PATHS[i]);
|
||||
}
|
||||
|
||||
bool App::_createDataDirectory(void) {
|
||||
file::FileInfo info;
|
||||
fs::FileInfo info;
|
||||
|
||||
if (!_fileIO.vfs.getFileInfo(info, EXTERNAL_DATA_DIR))
|
||||
return _fileIO.vfs.createDirectory(EXTERNAL_DATA_DIR);
|
||||
if (info.attributes & file::DIRECTORY)
|
||||
if (info.attributes & fs::DIRECTORY)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -270,7 +290,7 @@ bool App::_createDataDirectory(void) {
|
||||
bool App::_getNumberedPath(
|
||||
char *output, size_t length, const char *path, int maxIndex
|
||||
) {
|
||||
file::FileInfo info;
|
||||
fs::FileInfo info;
|
||||
|
||||
// Perform a binary search in order to quickly find the first unused path.
|
||||
int low = 0;
|
||||
@ -295,7 +315,7 @@ bool App::_getNumberedPath(
|
||||
}
|
||||
|
||||
bool App::_takeScreenshot(void) {
|
||||
char path[file::MAX_PATH_LENGTH];
|
||||
char path[fs::MAX_PATH_LENGTH];
|
||||
|
||||
if (!_createDataDirectory())
|
||||
return false;
|
||||
@ -394,8 +414,10 @@ void App::_interruptHandler(void) {
|
||||
_ctx.audioStream.handleInterrupt();
|
||||
|
||||
if (acknowledgeInterrupt(IRQ_PIO)) {
|
||||
for (auto &dev : ide::devices)
|
||||
dev.handleInterrupt();
|
||||
for (auto dev : _fileIO.ideDevices) {
|
||||
if (dev)
|
||||
dev->handleInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,7 +446,7 @@ void App::_interruptHandler(void) {
|
||||
#endif
|
||||
_ctx.overlays[2] = &_screenshotOverlay;
|
||||
|
||||
_runWorker(&App::_ideInitWorker, _warningScreen);
|
||||
_runWorker(&App::_startupWorker, _warningScreen);
|
||||
_setupInterrupts();
|
||||
|
||||
_splashOverlay.show(_ctx);
|
||||
|
@ -17,12 +17,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/misc.hpp"
|
||||
#include "common/file/zip.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/misc.hpp"
|
||||
#include "common/fs/zip.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "main/app/cartactions.hpp"
|
||||
#include "main/app/cartunlock.hpp"
|
||||
#include "main/app/main.hpp"
|
||||
@ -69,28 +69,31 @@ extern const char *const IDE_MOUNT_POINTS[];
|
||||
|
||||
class FileIOManager {
|
||||
private:
|
||||
file::File *_resourceFile;
|
||||
fs::File *_resourceFile;
|
||||
|
||||
public:
|
||||
const void *resourcePtr;
|
||||
size_t resourceLength;
|
||||
|
||||
file::Provider *ide[util::countOf(ide::devices)];
|
||||
file::ZIPProvider resource;
|
||||
fs::ZIPProvider resource;
|
||||
#ifdef ENABLE_PCDRV
|
||||
file::HostProvider host;
|
||||
fs::HostProvider host;
|
||||
#endif
|
||||
file::VFSProvider vfs;
|
||||
fs::VFSProvider vfs;
|
||||
|
||||
storage::Device *ideDevices[2];
|
||||
fs::Provider *ideProviders[2];
|
||||
|
||||
inline ~FileIOManager(void) {
|
||||
closeResourceFile();
|
||||
closeIDE();
|
||||
unmountIDE();
|
||||
}
|
||||
|
||||
FileIOManager(void);
|
||||
|
||||
void initIDE(void);
|
||||
void closeIDE(void);
|
||||
void mountIDE(void);
|
||||
void unmountIDE(void);
|
||||
bool loadResourceFile(const char *path);
|
||||
void closeResourceFile(void);
|
||||
};
|
||||
@ -192,9 +195,9 @@ private:
|
||||
#endif
|
||||
ui::ScreenshotOverlay _screenshotOverlay;
|
||||
|
||||
ui::Context &_ctx;
|
||||
file::StringTable _stringTable;
|
||||
FileIOManager _fileIO;
|
||||
ui::Context &_ctx;
|
||||
fs::StringTable _stringTable;
|
||||
FileIOManager _fileIO;
|
||||
|
||||
cart::CartDump _cartDump;
|
||||
cart::ROMHeaderDump _romHeaderDump;
|
||||
@ -246,7 +249,7 @@ private:
|
||||
bool _flashHeaderWriteWorker(void);
|
||||
|
||||
// miscworkers.cpp
|
||||
bool _ideInitWorker(void);
|
||||
bool _startupWorker(void);
|
||||
bool _fileInitWorker(void);
|
||||
bool _executableWorker(void);
|
||||
bool _atapiEjectWorker(void);
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
@ -202,7 +202,7 @@ bool App::_qrCodeWorker(void) {
|
||||
bool App::_cartDumpWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.cartDumpWorker.save"));
|
||||
|
||||
char path[file::MAX_PATH_LENGTH], code[8], region[8];
|
||||
char path[fs::MAX_PATH_LENGTH], code[8], region[8];
|
||||
size_t length = _cartDump.getDumpLength();
|
||||
|
||||
if (!_createDataDirectory())
|
||||
@ -267,7 +267,7 @@ bool App::_cartRestoreWorker(void) {
|
||||
_workerStatus.update(0, 3, WSTR("App.cartRestoreWorker.init"));
|
||||
|
||||
const char *path = _fileBrowserScreen.selectedPath;
|
||||
auto _file = _fileIO.vfs.openFile(path, file::READ);
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
|
||||
cart::CartDump newDump;
|
||||
|
||||
|
@ -17,10 +17,10 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/spu.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
@ -66,34 +66,36 @@ void IDEInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
char *ptr = _bodyText, *end = &_bodyText[sizeof(_bodyText)];
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
auto &header = _IDE_INFO_HEADERS[i];
|
||||
auto &dev = ide::devices[i];
|
||||
auto fs = APP->_fileIO.ide[i];
|
||||
auto &header = _IDE_INFO_HEADERS[i];
|
||||
auto dev = APP->_fileIO.ideDevices[i];
|
||||
auto provider = APP->_fileIO.ideProviders[i];
|
||||
|
||||
// Device information
|
||||
_PRINT(STRH(header.device));
|
||||
|
||||
if (dev.flags & ide::DEVICE_READY) {
|
||||
if (dev) {
|
||||
_PRINT(
|
||||
STR("IDEInfoScreen.device.commonInfo"), dev.model, dev.revision,
|
||||
dev.serialNumber
|
||||
STR("IDEInfoScreen.device.commonInfo"),
|
||||
dev->model,
|
||||
dev->revision,
|
||||
dev->serialNumber
|
||||
);
|
||||
|
||||
if (dev.flags & ide::DEVICE_ATAPI) {
|
||||
if (dev->type == storage::ATAPI) {
|
||||
_PRINT(
|
||||
STR("IDEInfoScreen.device.atapiInfo"),
|
||||
(dev.flags & ide::DEVICE_HAS_PACKET16) ? 16 : 12
|
||||
(dev->flags & storage::REQUIRES_EXT_PACKET) ? 16 : 12
|
||||
);
|
||||
} else {
|
||||
_PRINT(
|
||||
STR("IDEInfoScreen.device.ataInfo"),
|
||||
uint64_t(dev.capacity / (0x100000 / ide::ATA_SECTOR_SIZE)),
|
||||
(dev.flags & ide::DEVICE_HAS_LBA48) ? 48 : 28
|
||||
uint64_t(dev->capacity / (0x100000 / 512)),
|
||||
(dev->flags & storage::SUPPORTS_EXT_LBA) ? 48 : 28
|
||||
);
|
||||
|
||||
if (dev.flags & ide::DEVICE_HAS_TRIM)
|
||||
if (dev->flags & storage::SUPPORTS_TRIM)
|
||||
_PRINT(STR("IDEInfoScreen.device.hasTrim"));
|
||||
if (dev.flags & ide::DEVICE_HAS_FLUSH)
|
||||
if (dev->flags & storage::SUPPORTS_FLUSH)
|
||||
_PRINT(STR("IDEInfoScreen.device.hasFlush"));
|
||||
}
|
||||
} else {
|
||||
@ -103,28 +105,28 @@ void IDEInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_PRINTLN();
|
||||
|
||||
// Filesystem information
|
||||
if (!fs)
|
||||
if (!provider)
|
||||
continue;
|
||||
|
||||
if (fs->type == file::ISO9660) {
|
||||
if (provider->type == fs::ISO9660) {
|
||||
_PRINT(STRH(header.iso9660));
|
||||
|
||||
_PRINT(
|
||||
STR("IDEInfoScreen.iso9660.info"),
|
||||
fs->volumeLabel,
|
||||
fs->capacity / 0x100000
|
||||
provider->volumeLabel,
|
||||
provider->capacity / 0x100000
|
||||
);
|
||||
} else {
|
||||
_PRINT(STRH(header.fat));
|
||||
|
||||
_PRINT(
|
||||
STR("IDEInfoScreen.fat.info"),
|
||||
_FAT_TYPES[fs->type],
|
||||
fs->volumeLabel,
|
||||
fs->serialNumber >> 16,
|
||||
fs->serialNumber & 0xffff,
|
||||
fs->capacity / 0x100000,
|
||||
fs->getFreeSpace() / 0x100000
|
||||
_FAT_TYPES[provider->type],
|
||||
provider->volumeLabel,
|
||||
provider->serialNumber >> 16,
|
||||
provider->serialNumber & 0xffff,
|
||||
provider->capacity / 0x100000,
|
||||
provider->getFreeSpace() / 0x100000
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,12 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/misc.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/defs.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/idedefs.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/rom.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
@ -44,21 +43,9 @@ static const char *const _AUTOBOOT_PATHS[][2]{
|
||||
{ "hdd:/noboot.txt", "hdd:/psx.exe" }
|
||||
};
|
||||
|
||||
bool App::_ideInitWorker(void) {
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
auto &dev = ide::devices[i];
|
||||
|
||||
if (dev.flags & ide::DEVICE_READY)
|
||||
continue;
|
||||
|
||||
_workerStatus.update(
|
||||
i, util::countOf(ide::devices), WSTR("App.ideInitWorker.init")
|
||||
);
|
||||
|
||||
auto error = dev.enumerate();
|
||||
|
||||
LOG_APP("drive %d: %s", i, ide::getErrorString(error));
|
||||
}
|
||||
bool App::_startupWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.startupWorker.ideInit"));
|
||||
_fileIO.initIDE();
|
||||
|
||||
_fileInitWorker();
|
||||
|
||||
@ -80,7 +67,7 @@ bool App::_ideInitWorker(void) {
|
||||
}
|
||||
|
||||
for (auto path : _AUTOBOOT_PATHS) {
|
||||
file::FileInfo info;
|
||||
fs::FileInfo info;
|
||||
|
||||
if (_fileIO.vfs.getFileInfo(info, path[0]))
|
||||
continue;
|
||||
@ -99,16 +86,16 @@ bool App::_ideInitWorker(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool App::_fileInitWorker(void) {
|
||||
_workerStatus.update(0, 3, WSTR("App.fileInitWorker.unmount"));
|
||||
_fileIO.closeResourceFile();
|
||||
_fileIO.closeIDE();
|
||||
_fileIO.unmountIDE();
|
||||
|
||||
_workerStatus.update(1, 3, WSTR("App.fileInitWorker.mount"));
|
||||
_fileIO.initIDE();
|
||||
_fileIO.mountIDE();
|
||||
|
||||
_workerStatus.update(2, 3, WSTR("App.fileInitWorker.loadResources"));
|
||||
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
|
||||
@ -155,7 +142,7 @@ bool App::_executableWorker(void) {
|
||||
} else {
|
||||
__builtin_memset(header.magic, 0, sizeof(header.magic));
|
||||
|
||||
auto _file = _fileIO.vfs.openFile(path, file::READ);
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
device = -(path[3] - '0' + 1); // ide0: or ide1: -> -1 or -2
|
||||
|
||||
if (_file) {
|
||||
@ -231,11 +218,11 @@ bool App::_executableWorker(void) {
|
||||
} else {
|
||||
// Pass the list of LBAs taken up by the executable to the launcher
|
||||
// through the command line.
|
||||
file::FileFragmentTable fragments;
|
||||
fs::FileFragmentTable fragments;
|
||||
|
||||
_fileIO.vfs.getFileFragments(fragments, path);
|
||||
|
||||
auto fragment = fragments.as<const file::FileFragment>();
|
||||
auto fragment = fragments.as<const fs::FileFragment>();
|
||||
auto count = fragments.getNumFragments();
|
||||
|
||||
for (size_t i = count; i; i--, fragment++) {
|
||||
@ -261,7 +248,7 @@ bool App::_executableWorker(void) {
|
||||
// main() before starting the new executable.
|
||||
_unloadCartData();
|
||||
_fileIO.closeResourceFile();
|
||||
_fileIO.closeIDE();
|
||||
_fileIO.unmountIDE();
|
||||
|
||||
LOG_APP("jumping to launcher");
|
||||
uninstallExceptionHandler();
|
||||
@ -281,20 +268,23 @@ bool App::_executableWorker(void) {
|
||||
bool App::_atapiEjectWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.atapiEjectWorker.eject"));
|
||||
|
||||
for (auto &dev : ide::devices) {
|
||||
if (!(dev.flags & ide::DEVICE_ATAPI))
|
||||
for (auto dev : _fileIO.ideDevices) {
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
auto error = ide::DISC_CHANGED;
|
||||
// If the device does not support ejecting (i.e. is not ATAPI), move
|
||||
// onto the next drive.
|
||||
auto error = storage::DISC_CHANGED;
|
||||
|
||||
while (error == ide::DISC_CHANGED)
|
||||
error =
|
||||
ide::devices[0].startStopUnit(ide::START_STOP_MODE_OPEN_TRAY);
|
||||
while (error == storage::DISC_CHANGED)
|
||||
error = dev->eject();
|
||||
if (error == storage::UNSUPPORTED_OP)
|
||||
continue;
|
||||
|
||||
if (error) {
|
||||
_messageScreen.setMessage(
|
||||
MESSAGE_ERROR, WSTR("App.atapiEjectWorker.ejectError"),
|
||||
ide::getErrorString(error)
|
||||
storage::getErrorString(error)
|
||||
);
|
||||
_workerStatus.setNextScreen(_messageScreen);
|
||||
return false;
|
||||
@ -315,7 +305,7 @@ bool App::_rebootWorker(void) {
|
||||
|
||||
_unloadCartData();
|
||||
_fileIO.closeResourceFile();
|
||||
_fileIO.closeIDE();
|
||||
_fileIO.unmountIDE();
|
||||
_workerStatus.setStatus(WORKER_REBOOT);
|
||||
|
||||
// Fall back to a soft reboot if the watchdog fails to reset the system.
|
||||
|
@ -16,13 +16,13 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/file/misc.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/fs/misc.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/log.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/defs.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
#include "main/app/modals.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
@ -121,45 +121,63 @@ void ConfirmScreen::update(ui::Context &ctx) {
|
||||
|
||||
/* File picker screen */
|
||||
|
||||
#ifdef ENABLE_PCDRV
|
||||
struct SpecialEntry {
|
||||
struct DeviceType {
|
||||
public:
|
||||
util::Hash name;
|
||||
const char *prefix;
|
||||
const char *format;
|
||||
util::Hash error;
|
||||
};
|
||||
|
||||
static const SpecialEntry _SPECIAL_ENTRIES[]{
|
||||
static const DeviceType _DEVICE_TYPES[]{
|
||||
{
|
||||
.name = "FilePickerScreen.host"_h,
|
||||
.prefix = "host:"
|
||||
// storage::NONE
|
||||
.format = CH_HOST_ICON " %s",
|
||||
.error = "FilePickerScreen.hostError"_h
|
||||
}, {
|
||||
// storage::ATA
|
||||
.format = CH_HDD_ICON " %s: %s",
|
||||
.error = "FilePickerScreen.ataError"_h
|
||||
}, {
|
||||
// storage::ATAPI
|
||||
.format = CH_CDROM_ICON " %s: %s",
|
||||
.error = "FilePickerScreen.atapiError"_h
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
void FilePickerScreen::_addDevice(
|
||||
storage::Device *dev, fs::Provider *provider, const char *prefix
|
||||
) {
|
||||
// Note that devices are added (and thus displayed in the list) even if
|
||||
// their filesystem is unrecognized and no file provider is available.
|
||||
if (!dev && !provider)
|
||||
return;
|
||||
if (_listLength >= int(MAX_FILE_PICKER_DEVICES))
|
||||
return;
|
||||
|
||||
auto &entry = _entries[_listLength++];
|
||||
|
||||
entry.dev = dev;
|
||||
entry.provider = provider;
|
||||
entry.prefix = prefix;
|
||||
}
|
||||
|
||||
const char *FilePickerScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||
#ifdef ENABLE_PCDRV
|
||||
int offset = util::countOf(_SPECIAL_ENTRIES);
|
||||
static char name[fs::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap
|
||||
|
||||
if (index < offset)
|
||||
return STRH(_SPECIAL_ENTRIES[index].name);
|
||||
else
|
||||
index -= offset;
|
||||
#endif
|
||||
|
||||
static char name[file::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap
|
||||
|
||||
int drive = _drives[index];
|
||||
auto &dev = ide::devices[drive];
|
||||
auto fs = APP->_fileIO.ide[drive];
|
||||
|
||||
auto format = (dev.flags & ide::DEVICE_ATAPI)
|
||||
? (CH_CDROM_ICON " %s: %s")
|
||||
: (CH_HDD_ICON " %s: %s");
|
||||
auto label = fs
|
||||
? fs->volumeLabel
|
||||
auto &entry = _entries[index];
|
||||
auto label = entry.provider
|
||||
? entry.provider->volumeLabel
|
||||
: STR("FilePickerScreen.noFS");
|
||||
|
||||
snprintf(name, sizeof(name), format, dev.model, label);
|
||||
if (entry.dev)
|
||||
snprintf(
|
||||
name, sizeof(name), _DEVICE_TYPES[entry.dev->type].format,
|
||||
entry.dev->model, label
|
||||
);
|
||||
else
|
||||
snprintf(
|
||||
name, sizeof(name), _DEVICE_TYPES[storage::NONE].format, label
|
||||
);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -178,8 +196,10 @@ void FilePickerScreen::setMessage(
|
||||
void FilePickerScreen::reloadAndShow(ui::Context &ctx) {
|
||||
// Check if any drive has reported a disc change and reload all filesystems
|
||||
// if necessary.
|
||||
for (auto &dev : ide::devices) {
|
||||
if (!dev.poll())
|
||||
for (auto dev : APP->_fileIO.ideDevices) {
|
||||
if (!dev)
|
||||
continue;
|
||||
if (!dev->poll())
|
||||
continue;
|
||||
|
||||
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
|
||||
@ -198,15 +218,16 @@ void FilePickerScreen::show(ui::Context &ctx, bool goBack) {
|
||||
|
||||
_listLength = 0;
|
||||
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
if (ide::devices[i].flags & ide::DEVICE_READY)
|
||||
_drives[_listLength++] = i;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_PCDRV
|
||||
_listLength += util::countOf(_SPECIAL_ENTRIES);
|
||||
_addDevice(nullptr, APP->_fileIO.host, "host:");
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < util::countOf(APP->_fileIO.ideDevices); i++)
|
||||
_addDevice(
|
||||
APP->_fileIO.ideDevices[i], APP->_fileIO.ideProviders[i],
|
||||
IDE_MOUNT_POINTS[i]
|
||||
);
|
||||
|
||||
ListScreen::show(ctx, goBack);
|
||||
}
|
||||
|
||||
@ -226,27 +247,11 @@ void FilePickerScreen::update(ui::Context &ctx) {
|
||||
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||
ctx.show(*previousScreen, true, true);
|
||||
} else {
|
||||
int index = _activeItem;
|
||||
#ifdef ENABLE_PCDRV
|
||||
int offset = util::countOf(_SPECIAL_ENTRIES);
|
||||
auto &entry = _entries[_activeItem];
|
||||
auto type = entry.dev ? entry.dev->type : storage::NONE;
|
||||
|
||||
if (index < offset) {
|
||||
APP->_fileBrowserScreen.loadDirectory(
|
||||
ctx, _SPECIAL_ENTRIES[index].prefix
|
||||
);
|
||||
ctx.show(APP->_fileBrowserScreen, false, true);
|
||||
return;
|
||||
} else {
|
||||
index -= offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
int drive = _drives[index];
|
||||
auto &dev = ide::devices[drive];
|
||||
|
||||
int count = APP->_fileBrowserScreen.loadDirectory(
|
||||
ctx, IDE_MOUNT_POINTS[drive]
|
||||
);
|
||||
int count =
|
||||
APP->_fileBrowserScreen.loadDirectory(ctx, entry.prefix);
|
||||
|
||||
if (count > 0) {
|
||||
ctx.show(APP->_fileBrowserScreen, false, true);
|
||||
@ -255,10 +260,8 @@ void FilePickerScreen::update(ui::Context &ctx) {
|
||||
|
||||
if (!count)
|
||||
error = "FilePickerScreen.noFilesError"_h;
|
||||
else if (dev.flags & ide::DEVICE_ATAPI)
|
||||
error = "FilePickerScreen.atapiError"_h;
|
||||
else
|
||||
error = "FilePickerScreen.ideError"_h;
|
||||
error = _DEVICE_TYPES[type].error;
|
||||
|
||||
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
|
||||
APP->_messageScreen.setMessage(MESSAGE_ERROR, STRH(error));
|
||||
@ -277,7 +280,7 @@ void FileBrowserScreen::_setPathToParent(void) {
|
||||
__builtin_memcpy(selectedPath, _currentPath, length);
|
||||
selectedPath[length] = 0;
|
||||
} else {
|
||||
ptr = __builtin_strchr(_currentPath, file::VFS_PREFIX_SEPARATOR);
|
||||
ptr = __builtin_strchr(_currentPath, fs::VFS_PREFIX_SEPARATOR);
|
||||
*(++ptr) = 0;
|
||||
}
|
||||
}
|
||||
@ -305,7 +308,7 @@ void FileBrowserScreen::_unloadDirectory(void) {
|
||||
}
|
||||
|
||||
const char *FileBrowserScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||
static char name[file::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap
|
||||
static char name[fs::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap
|
||||
|
||||
if (!_isRoot)
|
||||
index--;
|
||||
@ -316,12 +319,12 @@ const char *FileBrowserScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||
format = CH_PARENT_DIR_ICON " %s";
|
||||
path = STR("FileBrowserScreen.parentDir");
|
||||
} else if (index < _numDirectories) {
|
||||
auto entries = _directories.as<file::FileInfo>();
|
||||
auto entries = _directories.as<fs::FileInfo>();
|
||||
|
||||
format = CH_DIR_ICON " %s";
|
||||
path = entries[index].name;
|
||||
} else {
|
||||
auto entries = _files.as<file::FileInfo>();
|
||||
auto entries = _files.as<fs::FileInfo>();
|
||||
|
||||
format = CH_FILE_ICON " %s";
|
||||
path = entries[index - _numDirectories].name;
|
||||
@ -343,10 +346,10 @@ int FileBrowserScreen::loadDirectory(
|
||||
if (!directory)
|
||||
return -1;
|
||||
|
||||
file::FileInfo info;
|
||||
fs::FileInfo info;
|
||||
|
||||
while (directory->getEntry(info)) {
|
||||
if (info.attributes & file::DIRECTORY)
|
||||
if (info.attributes & fs::DIRECTORY)
|
||||
_numDirectories++;
|
||||
else
|
||||
_numFiles++;
|
||||
@ -364,13 +367,13 @@ int FileBrowserScreen::loadDirectory(
|
||||
|
||||
LOG_APP("files=%d, dirs=%d", _numFiles, _numDirectories);
|
||||
|
||||
file::FileInfo *files = nullptr;
|
||||
file::FileInfo *directories = nullptr;
|
||||
fs::FileInfo *files = nullptr;
|
||||
fs::FileInfo *directories = nullptr;
|
||||
|
||||
if (_numFiles)
|
||||
files = _files.allocate<file::FileInfo>(_numFiles);
|
||||
files = _files.allocate<fs::FileInfo>(_numFiles);
|
||||
if (_numDirectories)
|
||||
directories = _directories.allocate<file::FileInfo>(_numDirectories);
|
||||
directories = _directories.allocate<fs::FileInfo>(_numDirectories);
|
||||
|
||||
// Iterate over all entries again to populate the newly allocated arrays.
|
||||
directory = APP->_fileIO.vfs.openDirectory(path);
|
||||
@ -379,10 +382,10 @@ int FileBrowserScreen::loadDirectory(
|
||||
return -1;
|
||||
|
||||
while (directory->getEntry(info)) {
|
||||
auto ptr = (info.attributes & file::DIRECTORY)
|
||||
auto ptr = (info.attributes & fs::DIRECTORY)
|
||||
? (directories++) : (files++);
|
||||
|
||||
__builtin_memcpy(ptr, &info, sizeof(file::FileInfo));
|
||||
__builtin_memcpy(ptr, &info, sizeof(fs::FileInfo));
|
||||
}
|
||||
|
||||
directory->close();
|
||||
@ -415,7 +418,7 @@ void FileBrowserScreen::update(ui::Context &ctx) {
|
||||
index--;
|
||||
|
||||
if (index < _numDirectories) {
|
||||
auto entries = _directories.as<file::FileInfo>();
|
||||
auto entries = _directories.as<fs::FileInfo>();
|
||||
|
||||
if (index < 0)
|
||||
_setPathToParent();
|
||||
@ -433,7 +436,7 @@ void FileBrowserScreen::update(ui::Context &ctx) {
|
||||
ctx.show(APP->_messageScreen, false, true);
|
||||
}
|
||||
} else {
|
||||
auto entries = _files.as<file::FileInfo>();
|
||||
auto entries = _files.as<fs::FileInfo>();
|
||||
|
||||
_setPathToChild(entries[index - _numDirectories].name);
|
||||
APP->_filePickerScreen._callback(ctx);
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/storage/device.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
@ -71,6 +71,15 @@ public:
|
||||
|
||||
/* File picker screen */
|
||||
|
||||
static constexpr size_t MAX_FILE_PICKER_DEVICES = 4;
|
||||
|
||||
struct FilePickerEntry {
|
||||
public:
|
||||
storage::Device *dev;
|
||||
fs::Provider *provider;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
class FilePickerScreen : public ui::ListScreen {
|
||||
friend class FileBrowserScreen;
|
||||
|
||||
@ -78,7 +87,11 @@ private:
|
||||
char _promptText[512];
|
||||
void (*_callback)(ui::Context &ctx);
|
||||
|
||||
int _drives[util::countOf(ide::devices)];
|
||||
FilePickerEntry _entries[MAX_FILE_PICKER_DEVICES];
|
||||
|
||||
void _addDevice(
|
||||
storage::Device *dev, fs::Provider *provider, const char *prefix
|
||||
);
|
||||
|
||||
protected:
|
||||
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||
@ -97,7 +110,7 @@ public:
|
||||
|
||||
class FileBrowserScreen : public ui::ListScreen {
|
||||
private:
|
||||
char _currentPath[file::MAX_PATH_LENGTH];
|
||||
char _currentPath[fs::MAX_PATH_LENGTH];
|
||||
bool _isRoot;
|
||||
|
||||
int _numFiles, _numDirectories;
|
||||
@ -111,7 +124,7 @@ protected:
|
||||
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||
|
||||
public:
|
||||
char selectedPath[file::MAX_PATH_LENGTH];
|
||||
char selectedPath[fs::MAX_PATH_LENGTH];
|
||||
|
||||
int loadDirectory(
|
||||
ui::Context &ctx, const char *path, bool updateCurrent = true
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file/file.hpp"
|
||||
#include "common/fs/file.hpp"
|
||||
#include "common/util/hash.hpp"
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/defs.hpp"
|
||||
@ -116,7 +116,7 @@ bool App::_romDumpWorker(void) {
|
||||
|
||||
// Store all dumps in a subdirectory named "dumpNNNN" within the main data
|
||||
// folder.
|
||||
char dirPath[file::MAX_PATH_LENGTH], filePath[file::MAX_PATH_LENGTH];
|
||||
char dirPath[fs::MAX_PATH_LENGTH], filePath[fs::MAX_PATH_LENGTH];
|
||||
|
||||
if (!_createDataDirectory())
|
||||
goto _initError;
|
||||
@ -146,7 +146,7 @@ bool App::_romDumpWorker(void) {
|
||||
snprintf(filePath, sizeof(filePath), entry.path, dirPath);
|
||||
|
||||
auto _file = _fileIO.vfs.openFile(
|
||||
filePath, file::WRITE | file::ALLOW_CREATE
|
||||
filePath, fs::WRITE | fs::ALLOW_CREATE
|
||||
);
|
||||
|
||||
if (!_file)
|
||||
@ -201,7 +201,7 @@ bool App::_romRestoreWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.romRestoreWorker.init"));
|
||||
|
||||
const char *path = _fileBrowserScreen.selectedPath;
|
||||
auto _file = _fileIO.vfs.openFile(path, file::READ);
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
|
||||
if (!_file) {
|
||||
_messageScreen.setMessage(
|
||||
|
@ -53,12 +53,10 @@ int main(int argc, const char **argv) {
|
||||
auto uiCtx = new ui::Context(*gpuCtx);
|
||||
auto app = new App(*uiCtx);
|
||||
|
||||
io::resetIDEDevices();
|
||||
io::setIOBoardLights(0);
|
||||
|
||||
gpu::enableDisplay(true);
|
||||
spu::setMasterVolume(spu::MAX_VOLUME / 2);
|
||||
io::setMiscOutput(io::MISC_OUT_SPU_ENABLE, true);
|
||||
io::setIOBoardLights(0);
|
||||
|
||||
app->run(args.resourcePtr, args.resourceLength);
|
||||
|
||||
|
170
src/vendor/diskio.h
vendored
170
src/vendor/diskio.h
vendored
@ -1,81 +1,89 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
#if 0
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*
|
||||
* This library has been modified to remove unnecessary global state, change the
|
||||
* data type used to identify physical drives and 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.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_status (PDRV_t pdrv);
|
||||
DRESULT disk_read (PDRV_t pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (PDRV_t pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (PDRV_t pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
#if 0
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
518
src/vendor/ff.c
vendored
518
src/vendor/ff.c
vendored
@ -1,8 +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.
|
||||
* This library has been modified to remove unnecessary global state, change the
|
||||
* data type used to identify physical drives and 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.
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------/
|
||||
@ -253,12 +255,12 @@
|
||||
|
||||
|
||||
/* Definitions of logical drive - physical location conversion */
|
||||
#if FF_MULTI_PARTITION
|
||||
#if 0
|
||||
#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
|
||||
#define LD2PT(vol) VolToPart[vol].pt /* Get partition number (0:auto search, 1..:forced partition number) */
|
||||
#else
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Auto partition search */
|
||||
#define LD2PD(vol) (PDRV_t)(vol) /* Each logical drive is associated with the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Auto partition search */
|
||||
#endif
|
||||
|
||||
|
||||
@ -465,16 +467,8 @@ typedef struct {
|
||||
/* File/Volume controls */
|
||||
/*--------------------------------*/
|
||||
|
||||
#if FF_VOLUMES < 1 || FF_VOLUMES > 10
|
||||
#error Wrong FF_VOLUMES setting
|
||||
#endif
|
||||
static FATFS *FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */
|
||||
static WORD Fsid; /* Filesystem mount ID */
|
||||
|
||||
#if FF_FS_RPATH != 0
|
||||
static BYTE CurrVol; /* Current drive set by f_chdrive() */
|
||||
#endif
|
||||
|
||||
#if FF_FS_LOCK
|
||||
static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
|
||||
#if FF_FS_REENTRANT
|
||||
@ -483,7 +477,7 @@ static volatile BYTE SysLockVolume; /* Volume id who is locking Files[] */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#if 0
|
||||
#ifdef FF_VOLUME_STRS
|
||||
static const char *const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */
|
||||
#endif
|
||||
@ -909,18 +903,18 @@ static int lock_volume ( /* 1:Ok, 0:timeout */
|
||||
|
||||
|
||||
#if FF_FS_LOCK
|
||||
rv = ff_mutex_take(fs->ldrv); /* Lock the volume */
|
||||
if (rv && syslock) { /* System lock reqiered? */
|
||||
rv = ff_mutex_take(FF_VOLUMES); /* Lock the system */
|
||||
rv = ff_mutex_take(fs->ldrv + 1); /* Lock the volume */
|
||||
if (rv && syslock) { /* System lock reqiered? */
|
||||
rv = ff_mutex_take(0); /* Lock the system */
|
||||
if (rv) {
|
||||
SysLockVolume = fs->ldrv;
|
||||
SysLock = 2; /* System lock succeeded */
|
||||
SysLock = 2; /* System lock succeeded */
|
||||
} else {
|
||||
ff_mutex_give(fs->ldrv); /* Failed system lock */
|
||||
ff_mutex_give(fs->ldrv + 1); /* Failed system lock */
|
||||
}
|
||||
}
|
||||
#else
|
||||
rv = syslock ? ff_mutex_take(fs->ldrv) : ff_mutex_take(fs->ldrv); /* Lock the volume (this is to prevent compiler warning) */
|
||||
rv = syslock ? ff_mutex_take(fs->ldrv + 1) : ff_mutex_take(fs->ldrv + 1); /* Lock the volume (this is to prevent compiler warning) */
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
@ -935,10 +929,10 @@ static void unlock_volume (
|
||||
#if FF_FS_LOCK
|
||||
if (SysLock == 2 && SysLockVolume == fs->ldrv) { /* Unlock system if it has been locked by this task */
|
||||
SysLock = 1;
|
||||
ff_mutex_give(FF_VOLUMES);
|
||||
ff_mutex_give(0);
|
||||
}
|
||||
#endif
|
||||
ff_mutex_give(fs->ldrv); /* Unlock the volume */
|
||||
ff_mutex_give(fs->ldrv + 1); /* Unlock the volume */
|
||||
}
|
||||
}
|
||||
|
||||
@ -1922,7 +1916,7 @@ static int cmp_lfn ( /* 1:matched, 0:not matched */
|
||||
}
|
||||
|
||||
|
||||
#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
|
||||
#if FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_EXFAT
|
||||
/*-----------------------------------------------------*/
|
||||
/* FAT-LFN: Pick a part of file name from an LFN entry */
|
||||
/*-----------------------------------------------------*/
|
||||
@ -2200,7 +2194,7 @@ static void init_alloc_info (
|
||||
|
||||
|
||||
|
||||
#if !FF_FS_READONLY || FF_FS_RPATH != 0
|
||||
#if !FF_FS_READONLY
|
||||
/*------------------------------------------------*/
|
||||
/* exFAT: Load the object's directory entry block */
|
||||
/*------------------------------------------------*/
|
||||
@ -2304,7 +2298,7 @@ static void create_xdir (
|
||||
|
||||
|
||||
|
||||
#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
|
||||
#if FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_EXFAT
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read an object from the directory */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@ -2382,7 +2376,7 @@ static FRESULT dir_read (
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
|
||||
#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL */
|
||||
|
||||
|
||||
|
||||
@ -2618,7 +2612,7 @@ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
|
||||
|
||||
|
||||
|
||||
#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
|
||||
#if FF_FS_MINIMIZE <= 1
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get file information from directory entry */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@ -2756,7 +2750,7 @@ static void get_fileinfo (
|
||||
fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */
|
||||
}
|
||||
|
||||
#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
|
||||
#endif /* FF_FS_MINIMIZE <= 1 */
|
||||
|
||||
|
||||
|
||||
@ -2888,7 +2882,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
||||
}
|
||||
*path = p; /* Return pointer to the next segment */
|
||||
|
||||
#if FF_FS_RPATH != 0
|
||||
#if 0
|
||||
if ((di == 1 && lfn[di - 1] == '.') ||
|
||||
(di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */
|
||||
lfn[di] = 0;
|
||||
@ -2995,7 +2989,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
||||
p = *path; sfn = dp->fn;
|
||||
memset(sfn, ' ', 11);
|
||||
si = i = 0; ni = 8;
|
||||
#if FF_FS_RPATH != 0
|
||||
#if 0
|
||||
if (p[si] == '.') { /* Is this a dot entry? */
|
||||
for (;;) {
|
||||
c = (BYTE)p[si++];
|
||||
@ -3067,7 +3061,7 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
|
||||
FATFS *fs = dp->obj.fs;
|
||||
|
||||
|
||||
#if FF_FS_RPATH != 0
|
||||
#if 0
|
||||
if (!IsSeparator(*path) && (FF_STR_VOLUME_ID != 2 || !IsTerminator(*path))) { /* Without heading separator */
|
||||
dp->obj.sclust = fs->cdir; /* Start at the current directory */
|
||||
} else
|
||||
@ -3078,7 +3072,7 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
|
||||
}
|
||||
#if FF_FS_EXFAT
|
||||
dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */
|
||||
#if FF_FS_RPATH != 0
|
||||
#if 0
|
||||
if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */
|
||||
DIR dj;
|
||||
|
||||
@ -3105,11 +3099,14 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
|
||||
ns = dp->fn[NSFLAG];
|
||||
if (res != FR_OK) { /* Failed to find the object */
|
||||
if (res == FR_NO_FILE) { /* Object is not found */
|
||||
if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */
|
||||
#if 0
|
||||
if (ns & NS_DOT) { /* If dot entry is not exist, stay there */
|
||||
if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */
|
||||
dp->fn[NSFLAG] = NS_NONAME;
|
||||
res = FR_OK;
|
||||
} else { /* Could not find the object */
|
||||
} else
|
||||
#endif
|
||||
{ /* Could not find the object */
|
||||
if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */
|
||||
}
|
||||
}
|
||||
@ -3140,85 +3137,6 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get logical drive number from path name */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
|
||||
const TCHAR** path /* Pointer to pointer to the path name */
|
||||
)
|
||||
{
|
||||
const TCHAR *tp;
|
||||
const TCHAR *tt;
|
||||
TCHAR tc;
|
||||
int i;
|
||||
int vol = -1;
|
||||
#if FF_STR_VOLUME_ID /* Find string volume ID */
|
||||
const char *sp;
|
||||
char c;
|
||||
#endif
|
||||
|
||||
tt = tp = *path;
|
||||
if (!tp) return vol; /* Invalid path name? */
|
||||
do { /* Find a colon in the path */
|
||||
tc = *tt++;
|
||||
} while (!IsTerminator(tc) && tc != ':');
|
||||
|
||||
if (tc == ':') { /* DOS/Windows style volume ID? */
|
||||
i = FF_VOLUMES;
|
||||
if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */
|
||||
i = (int)*tp - '0'; /* Get the LD number */
|
||||
}
|
||||
#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
|
||||
else {
|
||||
i = 0;
|
||||
do {
|
||||
sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
|
||||
do { /* Compare the volume ID with path name */
|
||||
c = *sp++; tc = *tp++;
|
||||
if (IsLower(c)) c -= 0x20;
|
||||
if (IsLower(tc)) tc -= 0x20;
|
||||
} while (c && (TCHAR)c == tc);
|
||||
} while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
|
||||
}
|
||||
#endif
|
||||
if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
|
||||
vol = i; /* Drive number */
|
||||
*path = tt; /* Snip the drive prefix off */
|
||||
}
|
||||
return vol;
|
||||
}
|
||||
#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
|
||||
if (*tp == '/') { /* Is there a volume ID? */
|
||||
while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */
|
||||
i = 0;
|
||||
do {
|
||||
tt = tp; sp = VolumeStr[i]; /* Path name and this string volume ID */
|
||||
do { /* Compare the volume ID with path name */
|
||||
c = *sp++; tc = *(++tt);
|
||||
if (IsLower(c)) c -= 0x20;
|
||||
if (IsLower(tc)) tc -= 0x20;
|
||||
} while (c && (TCHAR)c == tc);
|
||||
} while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
|
||||
if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
|
||||
vol = i; /* Drive number */
|
||||
*path = tt; /* Snip the drive prefix off */
|
||||
}
|
||||
return vol;
|
||||
}
|
||||
#endif
|
||||
/* No drive prefix is found */
|
||||
#if FF_FS_RPATH != 0
|
||||
vol = CurrVol; /* Default drive is current drive */
|
||||
#else
|
||||
vol = 0; /* Default drive is 0 */
|
||||
#endif
|
||||
return vol; /* Return the default drive */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* GPT support functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@ -3392,13 +3310,10 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
||||
const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
|
||||
FATFS** rfs, /* Pointer to pointer to the found filesystem object */
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
BYTE mode /* Desiered access mode to check write protection */
|
||||
)
|
||||
{
|
||||
int vol;
|
||||
FATFS *fs;
|
||||
DSTATUS stat;
|
||||
LBA_t bsect;
|
||||
DWORD tsect, sysect, fasize, nclst, szbfat;
|
||||
@ -3406,20 +3321,14 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
||||
UINT fmt;
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
*rfs = 0;
|
||||
vol = get_ldnumber(path);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
|
||||
/* Check if the filesystem object is valid or not */
|
||||
fs = FatFs[vol]; /* Get pointer to the filesystem object */
|
||||
if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
|
||||
#if FF_FS_REENTRANT
|
||||
if (!lock_volume(fs, 1)) return FR_TIMEOUT; /* Lock the volume, and system if needed */
|
||||
#endif
|
||||
*rfs = fs; /* Return pointer to the filesystem object */
|
||||
|
||||
mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
|
||||
#if 0
|
||||
if (fs->fs_type != 0) { /* If the volume has been mounted */
|
||||
stat = disk_status(fs->pdrv);
|
||||
if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
|
||||
@ -3429,12 +3338,13 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
||||
return FR_OK; /* The filesystem object is already valid */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The filesystem object is not valid. */
|
||||
/* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
|
||||
|
||||
fs->fs_type = 0; /* Invalidate the filesystem object */
|
||||
stat = disk_initialize(fs->pdrv); /* Initialize the volume hosting physical drive */
|
||||
stat = disk_status(fs->pdrv); /* Initialize the volume hosting physical drive */
|
||||
if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
|
||||
return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
|
||||
}
|
||||
@ -3447,7 +3357,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
||||
#endif
|
||||
|
||||
/* Find an FAT volume on the hosting drive */
|
||||
fmt = find_volume(fs, LD2PT(vol));
|
||||
fmt = find_volume(fs, LD2PT(fs->ldrv));
|
||||
if (fmt == 4) return FR_DISK_ERR; /* An error occurred in the disk I/O layer */
|
||||
if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */
|
||||
bsect = fs->winsect; /* Volume offset in the hosting physical drive */
|
||||
@ -3603,9 +3513,6 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
||||
fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */
|
||||
#endif
|
||||
#endif
|
||||
#if FF_FS_RPATH != 0
|
||||
fs->cdir = 0; /* Initialize current directory */
|
||||
#endif
|
||||
#if FF_FS_LOCK /* Clear file lock semaphores */
|
||||
clear_share(fs);
|
||||
#endif
|
||||
@ -3664,66 +3571,67 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_mount (
|
||||
FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/
|
||||
const TCHAR* path, /* Logical drive number to be mounted/unmounted */
|
||||
FATFS* fs, /* Pointer to the filesystem object to be registered */
|
||||
PDRV_t pdrv, /* Physical drive number */
|
||||
BYTE ldrv, /* Logical drive number */
|
||||
BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
|
||||
)
|
||||
{
|
||||
FATFS *cfs;
|
||||
int vol;
|
||||
FRESULT res;
|
||||
const TCHAR *rp = path;
|
||||
|
||||
if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
|
||||
|
||||
/* Get volume ID (logical drive number) */
|
||||
vol = get_ldnumber(&rp);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
cfs = FatFs[vol]; /* Pointer to the filesystem object of the volume */
|
||||
|
||||
if (cfs) { /* Unregister current filesystem object if regsitered */
|
||||
FatFs[vol] = 0;
|
||||
/* Register new filesystem object */
|
||||
fs->pdrv = LD2PD(pdrv); /* Volume hosting physical drive */
|
||||
#if FF_FS_REENTRANT /* Create a volume mutex */
|
||||
fs->ldrv = ldrv; /* Owner volume ID */
|
||||
if (!ff_mutex_create(ldrv + 1)) return FR_INT_ERR;
|
||||
#if FF_FS_LOCK
|
||||
clear_share(cfs);
|
||||
#endif
|
||||
#if FF_FS_REENTRANT /* Discard mutex of the current volume */
|
||||
ff_mutex_delete(vol);
|
||||
#endif
|
||||
cfs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */
|
||||
}
|
||||
|
||||
if (fs) { /* Register new filesystem object */
|
||||
fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */
|
||||
#if FF_FS_REENTRANT /* Create a volume mutex */
|
||||
fs->ldrv = (BYTE)vol; /* Owner volume ID */
|
||||
if (!ff_mutex_create(vol)) return FR_INT_ERR;
|
||||
#if FF_FS_LOCK
|
||||
if (SysLock == 0) { /* Create a system mutex if needed */
|
||||
if (!ff_mutex_create(FF_VOLUMES)) {
|
||||
ff_mutex_delete(vol);
|
||||
return FR_INT_ERR;
|
||||
}
|
||||
SysLock = 1; /* System mutex is ready */
|
||||
if (SysLock == 0) { /* Create a system mutex if needed */
|
||||
if (!ff_mutex_create(0)) {
|
||||
ff_mutex_delete(ldrv + 1);
|
||||
return FR_INT_ERR;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
fs->fs_type = 0; /* Invalidate the new filesystem object */
|
||||
FatFs[vol] = fs; /* Register new fs object */
|
||||
SysLock = 1; /* System mutex is ready */
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
fs->fs_type = 0; /* Invalidate the new filesystem object */
|
||||
|
||||
if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted in subsequent file functions */
|
||||
|
||||
res = mount_volume(&path, &fs, 0); /* Force mounted the volume */
|
||||
res = mount_volume(fs, 0); /* Force mounted the volume */
|
||||
LEAVE_FF(fs, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FRESULT f_unmount (
|
||||
FATFS* fs /* Pointer to the filesystem object */
|
||||
)
|
||||
{
|
||||
if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
|
||||
|
||||
#if FF_FS_LOCK
|
||||
clear_share(fs);
|
||||
#endif
|
||||
#if FF_FS_REENTRANT /* Discard mutex of the current volume */
|
||||
ff_mutex_delete(fs->ldrv + 1);
|
||||
#endif
|
||||
fs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Open or Create a File */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_open (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
FIL* fp, /* Pointer to the blank file object */
|
||||
const TCHAR* path, /* Pointer to the file name */
|
||||
BYTE mode /* Access mode and open mode flags */
|
||||
@ -3731,7 +3639,6 @@ FRESULT f_open (
|
||||
{
|
||||
FRESULT res;
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
#if !FF_FS_READONLY
|
||||
DWORD cl, bcs, clst, tm;
|
||||
LBA_t sc;
|
||||
@ -3744,7 +3651,7 @@ FRESULT f_open (
|
||||
|
||||
/* Get logical drive number */
|
||||
mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
|
||||
res = mount_volume(&path, &fs, mode);
|
||||
res = mount_volume(fs, mode);
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@ -4253,187 +4160,6 @@ FRESULT f_close (
|
||||
|
||||
|
||||
|
||||
#if FF_FS_RPATH >= 1
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Change Current Directory or Current Drive, Get Current Directory */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_chdrive (
|
||||
const TCHAR* path /* Drive number to set */
|
||||
)
|
||||
{
|
||||
int vol;
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
vol = get_ldnumber(&path);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
CurrVol = (BYTE)vol; /* Set it as current volume */
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FRESULT f_chdir (
|
||||
const TCHAR* path /* Pointer to the directory path */
|
||||
)
|
||||
{
|
||||
#if FF_STR_VOLUME_ID == 2
|
||||
UINT i;
|
||||
#endif
|
||||
FRESULT res;
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &fs, 0);
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
res = follow_path(&dj, path); /* Follow the path */
|
||||
if (res == FR_OK) { /* Follow completed */
|
||||
if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */
|
||||
fs->cdir = dj.obj.sclust;
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
fs->cdc_scl = dj.obj.c_scl;
|
||||
fs->cdc_size = dj.obj.c_size;
|
||||
fs->cdc_ofs = dj.obj.c_ofs;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */
|
||||
fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */
|
||||
fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
|
||||
fs->cdc_ofs = dj.blk_ofs;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */
|
||||
}
|
||||
} else {
|
||||
res = FR_NO_PATH; /* Reached but a file */
|
||||
}
|
||||
}
|
||||
}
|
||||
FREE_NAMBUF();
|
||||
if (res == FR_NO_FILE) res = FR_NO_PATH;
|
||||
#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed if in Unix style volume ID */
|
||||
if (res == FR_OK) {
|
||||
for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */
|
||||
CurrVol = (BYTE)i;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
LEAVE_FF(fs, res);
|
||||
}
|
||||
|
||||
|
||||
#if FF_FS_RPATH >= 2
|
||||
FRESULT f_getcwd (
|
||||
TCHAR* buff, /* Pointer to the directory path */
|
||||
UINT len /* Size of buff in unit of TCHAR */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
UINT i, n;
|
||||
DWORD ccl;
|
||||
TCHAR *tp = buff;
|
||||
#if FF_VOLUMES >= 2
|
||||
UINT vl;
|
||||
#if FF_STR_VOLUME_ID
|
||||
const char *vp;
|
||||
#endif
|
||||
#endif
|
||||
FILINFO fno;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
/* Get logical drive */
|
||||
buff[0] = 0; /* Set null string to get current volume */
|
||||
res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
|
||||
/* Follow parent directories and create the path */
|
||||
i = len; /* Bottom of buffer (directory stack base) */
|
||||
if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */
|
||||
dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */
|
||||
while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */
|
||||
res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */
|
||||
if (res != FR_OK) break;
|
||||
res = move_window(fs, dj.sect);
|
||||
if (res != FR_OK) break;
|
||||
dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */
|
||||
res = dir_sdi(&dj, 0);
|
||||
if (res != FR_OK) break;
|
||||
do { /* Find the entry links to the child directory */
|
||||
res = DIR_READ_FILE(&dj);
|
||||
if (res != FR_OK) break;
|
||||
if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */
|
||||
res = dir_next(&dj, 0);
|
||||
} while (res == FR_OK);
|
||||
if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
|
||||
if (res != FR_OK) break;
|
||||
get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */
|
||||
for (n = 0; fno.fname[n]; n++) ; /* Name length */
|
||||
if (i < n + 1) { /* Insufficient space to store the path name? */
|
||||
res = FR_NOT_ENOUGH_CORE; break;
|
||||
}
|
||||
while (n) buff[--i] = fno.fname[--n]; /* Stack the name */
|
||||
buff[--i] = '/';
|
||||
}
|
||||
}
|
||||
if (res == FR_OK) {
|
||||
if (i == len) buff[--i] = '/'; /* Is it the root-directory? */
|
||||
#if FF_VOLUMES >= 2 /* Put drive prefix */
|
||||
vl = 0;
|
||||
#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
|
||||
for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
|
||||
if (i >= n + 2) {
|
||||
if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
|
||||
for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
|
||||
if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
|
||||
vl++;
|
||||
}
|
||||
#else /* Numeric volume ID */
|
||||
if (i >= 3) {
|
||||
*tp++ = (TCHAR)'0' + CurrVol;
|
||||
*tp++ = (TCHAR)':';
|
||||
vl = 2;
|
||||
}
|
||||
#endif
|
||||
if (vl == 0) res = FR_NOT_ENOUGH_CORE;
|
||||
#endif
|
||||
/* Add current directory path */
|
||||
if (res == FR_OK) {
|
||||
do { /* Copy stacked path string */
|
||||
*tp++ = buff[i++];
|
||||
} while (i < len);
|
||||
}
|
||||
}
|
||||
FREE_NAMBUF();
|
||||
}
|
||||
|
||||
*tp = 0;
|
||||
LEAVE_FF(fs, res);
|
||||
}
|
||||
|
||||
#endif /* FF_FS_RPATH >= 2 */
|
||||
#endif /* FF_FS_RPATH >= 1 */
|
||||
|
||||
|
||||
|
||||
#if FF_FS_MINIMIZE <= 2
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Seek File Read/Write Pointer */
|
||||
@ -4604,19 +4330,19 @@ FRESULT f_lseek (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_opendir (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
DIR* dp, /* Pointer to directory object to create */
|
||||
const TCHAR* path /* Pointer to the directory path */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
if (!dp) return FR_INVALID_OBJECT;
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &fs, 0);
|
||||
res = mount_volume(fs, 0);
|
||||
if (res == FR_OK) {
|
||||
dp->obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@ -4788,6 +4514,7 @@ FRESULT f_findfirst (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_stat (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path, /* Pointer to the file path */
|
||||
FILINFO* fno /* Pointer to file information to return */
|
||||
)
|
||||
@ -4798,9 +4525,10 @@ FRESULT f_stat (
|
||||
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &dj.obj.fs, 0);
|
||||
res = mount_volume(fs, 0);
|
||||
if (res == FR_OK) {
|
||||
INIT_NAMBUF(dj.obj.fs);
|
||||
INIT_NAMBUF(fs);
|
||||
dj.obj.fs = fs;
|
||||
res = follow_path(&dj, path); /* Follow the file path */
|
||||
if (res == FR_OK) { /* Follow completed */
|
||||
if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */
|
||||
@ -4812,7 +4540,7 @@ FRESULT f_stat (
|
||||
FREE_NAMBUF();
|
||||
}
|
||||
|
||||
LEAVE_FF(dj.obj.fs, res);
|
||||
LEAVE_FF(fs, res);
|
||||
}
|
||||
|
||||
|
||||
@ -4823,13 +4551,11 @@ FRESULT f_stat (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_getfree (
|
||||
const TCHAR* path, /* Logical drive number */
|
||||
DWORD* nclst, /* Pointer to a variable to return number of free clusters */
|
||||
FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
DWORD* nclst /* Pointer to a variable to return number of free clusters */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DWORD nfree, clst, stat;
|
||||
LBA_t sect;
|
||||
UINT i;
|
||||
@ -4837,9 +4563,8 @@ FRESULT f_getfree (
|
||||
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &fs, 0);
|
||||
res = mount_volume(fs, 0);
|
||||
if (res == FR_OK) {
|
||||
*fatfs = fs; /* Return ptr to the fs object */
|
||||
/* If free_clst is valid, return it without full FAT scan */
|
||||
if (fs->free_clst <= fs->n_fatent - 2) {
|
||||
*nclst = fs->free_clst;
|
||||
@ -4969,11 +4694,11 @@ FRESULT f_truncate (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_unlink (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path /* Pointer to the file or directory path */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj, sdj;
|
||||
DWORD dclst = 0;
|
||||
#if FF_FS_EXFAT
|
||||
@ -4983,14 +4708,11 @@ FRESULT f_unlink (
|
||||
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &fs, FA_WRITE);
|
||||
res = mount_volume(fs, FA_WRITE);
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
res = follow_path(&dj, path); /* Follow the file path */
|
||||
if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {
|
||||
res = FR_INVALID_NAME; /* Cannot remove dot entry */
|
||||
}
|
||||
#if FF_FS_LOCK
|
||||
if (res == FR_OK) res = chk_share(&dj, 2); /* Check if it is an open object */
|
||||
#endif
|
||||
@ -5014,7 +4736,7 @@ FRESULT f_unlink (
|
||||
dclst = ld_clust(fs, dj.dir);
|
||||
}
|
||||
if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */
|
||||
#if FF_FS_RPATH != 0
|
||||
#if 0
|
||||
if (dclst == fs->cdir) { /* Is it the current directory? */
|
||||
res = FR_DENIED;
|
||||
} else
|
||||
@ -5063,26 +4785,23 @@ FRESULT f_unlink (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_mkdir (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path /* Pointer to the directory path */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj;
|
||||
FFOBJID sobj;
|
||||
DWORD dcl, pcl, tm;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */
|
||||
res = mount_volume(fs, FA_WRITE); /* Get logical drive */
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
res = follow_path(&dj, path); /* Follow the file path */
|
||||
if (res == FR_OK) res = FR_EXIST; /* Name collision? */
|
||||
if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */
|
||||
res = FR_INVALID_NAME;
|
||||
}
|
||||
if (res == FR_NO_FILE) { /* It is clear to create a new directory */
|
||||
sobj.fs = fs; /* New object id to create a new chain */
|
||||
dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */
|
||||
@ -5147,20 +4866,19 @@ FRESULT f_mkdir (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_rename (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path_old, /* Pointer to the object name to be renamed */
|
||||
const TCHAR* path_new /* Pointer to the new name */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR djo, djn;
|
||||
BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
|
||||
LBA_t sect;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
get_ldnumber(&path_new); /* Snip the drive number of new name off */
|
||||
res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */
|
||||
res = mount_volume(fs, FA_WRITE); /* Get logical drive of the old object */
|
||||
if (res == FR_OK) {
|
||||
djo.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@ -5257,18 +4975,18 @@ FRESULT f_rename (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_chmod (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path, /* Pointer to the file path */
|
||||
BYTE attr, /* Attribute bits */
|
||||
BYTE mask /* Attribute mask to change */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */
|
||||
res = mount_volume(fs, FA_WRITE); /* Get logical drive */
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@ -5304,17 +5022,17 @@ FRESULT f_chmod (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_utime (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* path, /* Pointer to the file/directory name */
|
||||
const FILINFO* fno /* Pointer to the timestamp to be set */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj;
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */
|
||||
res = mount_volume(fs, FA_WRITE); /* Get logical drive */
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@ -5351,19 +5069,18 @@ FRESULT f_utime (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_getlabel (
|
||||
const TCHAR* path, /* Logical drive number */
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
TCHAR* label, /* Buffer to store the volume label */
|
||||
DWORD* vsn /* Variable to store the volume serial number */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj;
|
||||
UINT si, di;
|
||||
WCHAR wc;
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&path, &fs, 0);
|
||||
res = mount_volume(fs, 0);
|
||||
|
||||
/* Get volume label */
|
||||
if (res == FR_OK && label) {
|
||||
@ -5452,11 +5169,11 @@ FRESULT f_getlabel (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_setlabel (
|
||||
FATFS* fs, /* Pointer to the filesystem object */
|
||||
const TCHAR* label /* Volume label to set with heading logical drive number */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
DIR dj;
|
||||
BYTE dirvn[22];
|
||||
UINT di;
|
||||
@ -5467,9 +5184,9 @@ FRESULT f_setlabel (
|
||||
#endif
|
||||
|
||||
/* Get logical drive */
|
||||
res = mount_volume(&label, &fs, FA_WRITE);
|
||||
res = mount_volume(fs, FA_WRITE);
|
||||
if (res != FR_OK) LEAVE_FF(fs, res);
|
||||
#if FF_STR_VOLUME_ID == 2
|
||||
#if 0
|
||||
for ( ; *label == '/'; label++) ; /* Snip the separators off */
|
||||
#endif
|
||||
|
||||
@ -5749,7 +5466,7 @@ FRESULT f_forward (
|
||||
/* Create partitions on the physical drive in format of MBR or GPT */
|
||||
|
||||
static FRESULT create_partition (
|
||||
BYTE drv, /* Physical drive number */
|
||||
PDRV_t drv, /* Physical drive number */
|
||||
const LBA_t plst[], /* Partition list */
|
||||
BYTE sys, /* System ID for each partition (for only MBR) */
|
||||
BYTE *buf /* Working buffer for a sector */
|
||||
@ -5892,7 +5609,8 @@ static FRESULT create_partition (
|
||||
|
||||
|
||||
FRESULT f_mkfs (
|
||||
const TCHAR* path, /* Logical drive number */
|
||||
PDRV_t pdrv, /* Physical drive number */
|
||||
BYTE ipart, /* Partition number (0: create new, 1..4: format existing) */
|
||||
const MKFS_PARM* opt, /* Format options */
|
||||
void* work, /* Pointer to working buffer (null: use len bytes of heap memory) */
|
||||
UINT len /* Size of working buffer [byte] */
|
||||
@ -5901,7 +5619,7 @@ FRESULT f_mkfs (
|
||||
static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */
|
||||
static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
|
||||
static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
|
||||
BYTE fsopt, fsty, sys, pdrv, ipart;
|
||||
BYTE fsopt, fsty, sys;
|
||||
BYTE *buf;
|
||||
BYTE *pte;
|
||||
WORD ss; /* Sector size */
|
||||
@ -5910,23 +5628,9 @@ FRESULT f_mkfs (
|
||||
LBA_t sect, lba[2];
|
||||
DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */
|
||||
UINT n_fat, n_root, i; /* Index, Number of FATs and Number of roor dir entries */
|
||||
int vol;
|
||||
DSTATUS ds;
|
||||
FRESULT res;
|
||||
|
||||
|
||||
/* Check mounted drive and clear work area */
|
||||
vol = get_ldnumber(&path); /* Get target logical drive */
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs object if mounted */
|
||||
pdrv = LD2PD(vol); /* Hosting physical drive */
|
||||
ipart = LD2PT(vol); /* Hosting partition (0:create as new, 1..:existing partition) */
|
||||
|
||||
/* Initialize the hosting physical drive */
|
||||
ds = disk_initialize(pdrv);
|
||||
if (ds & STA_NOINIT) return FR_NOT_READY;
|
||||
if (ds & STA_PROTECT) return FR_WRITE_PROTECTED;
|
||||
|
||||
/* Get physical drive parameters (sz_drv, sz_blk and ss) */
|
||||
if (!opt) opt = &defopt; /* Use default parameter if it is not given */
|
||||
sz_blk = opt->align;
|
||||
@ -6396,21 +6100,15 @@ FRESULT f_mkfs (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_fdisk (
|
||||
BYTE pdrv, /* Physical drive number */
|
||||
PDRV_t pdrv, /* Physical drive number */
|
||||
const LBA_t ptbl[], /* Pointer to the size table for each partitions */
|
||||
void* work /* Pointer to the working buffer (null: use heap memory) */
|
||||
)
|
||||
{
|
||||
BYTE *buf = (BYTE*)work;
|
||||
DSTATUS stat;
|
||||
FRESULT res;
|
||||
|
||||
|
||||
/* Initialize the physical drive */
|
||||
stat = disk_initialize(pdrv);
|
||||
if (stat & STA_NOINIT) return FR_NOT_READY;
|
||||
if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
|
||||
|
||||
#if FF_USE_LFN == 3
|
||||
if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
|
||||
#endif
|
||||
|
59
src/vendor/ff.h
vendored
59
src/vendor/ff.h
vendored
@ -1,8 +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.
|
||||
* This library has been modified to remove unnecessary global state, change the
|
||||
* data type used to identify physical drives and 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.
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------/
|
||||
@ -114,9 +116,11 @@ typedef char TCHAR;
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
typedef void* PDRV_t;
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
PDRV_t pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
@ -134,7 +138,7 @@ extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Volume hosting physical drive */
|
||||
PDRV_t 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) */
|
||||
@ -154,14 +158,6 @@ typedef struct {
|
||||
#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 */
|
||||
@ -304,35 +300,33 @@ typedef enum {
|
||||
/* FatFs Module Application Interface */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_open (FATFS* fs, 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_opendir (FATFS* fs, 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_mkdir (FATFS* fs, const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (FATFS* fs, const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (FATFS* fs, const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (FATFS* fs, const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (FATFS* fs, const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (FATFS* fs, const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_getfree (FATFS* fs, DWORD* nclst); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (FATFS* fs, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (FATFS* fs, 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_mount (FATFS* fs, PDRV_t pdrv, BYTE ldrv, BYTE opt); /* Mount a logical drive */
|
||||
FRESULT f_unmount (FATFS* fs); /* Unmount a logical drive */
|
||||
FRESULT f_mkfs (PDRV_t pdrv, BYTE ipart, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (PDRV_t 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 */
|
||||
@ -348,8 +342,7 @@ FRESULT f_getlbas (FIL* fp, LBA_t* tbl, UINT ofs, UINT* len); /* Get list of LB
|
||||
#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)
|
||||
#define f_rmdir(fs, path) f_unlink(fs, path)
|
||||
|
||||
|
||||
|
||||
|
11
src/vendor/vendorconfig.h
vendored
11
src/vendor/vendorconfig.h
vendored
@ -35,10 +35,7 @@
|
||||
#define FF_LFN_UNICODE 0
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
#define FF_FS_RPATH 1
|
||||
|
||||
#define FF_VOLUMES 2
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_MULTI_PARTITION 0
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
@ -55,6 +52,14 @@
|
||||
#define FF_FS_LOCK 0
|
||||
#define FF_FS_REENTRANT 1
|
||||
|
||||
// The following options have been removed entirely from the vendored copy of
|
||||
// FatFs.
|
||||
#if 0
|
||||
#define FF_FS_RPATH 0
|
||||
#define FF_VOLUMES 2
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#endif
|
||||
|
||||
/* miniz configuration */
|
||||
|
||||
#define MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
|
Loading…
x
Reference in New Issue
Block a user