mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Rewrite IDE driver completely, fix VFS and flash driver
This commit is contained in:
parent
db5eaabe57
commit
ef46aad5b0
@ -106,6 +106,7 @@ addExecutable(
|
|||||||
src/common/io.cpp
|
src/common/io.cpp
|
||||||
src/common/pad.cpp
|
src/common/pad.cpp
|
||||||
src/common/rom.cpp
|
src/common/rom.cpp
|
||||||
|
src/common/romdrivers.cpp
|
||||||
src/common/spu.cpp
|
src/common/spu.cpp
|
||||||
src/common/util.cpp
|
src/common/util.cpp
|
||||||
src/libc/crt0.c
|
src/libc/crt0.c
|
||||||
|
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
"App": {
|
"App": {
|
||||||
"ideInitWorker": {
|
"ideInitWorker": {
|
||||||
"initDrives": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
|
"init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives."
|
||||||
"initFileIO": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
|
},
|
||||||
|
"fileInitWorker": {
|
||||||
|
"unmount": "Unmounting all mounted filesystems...\nDo not turn off the 573 or unplug drives.",
|
||||||
|
"mount": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
|
||||||
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.",
|
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.",
|
||||||
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
|
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
|
||||||
},
|
},
|
||||||
@ -113,7 +116,7 @@
|
|||||||
|
|
||||||
"AudioTestScreen": {
|
"AudioTestScreen": {
|
||||||
"title": "{RIGHT_ARROW} Audio output test",
|
"title": "{RIGHT_ARROW} Audio output test",
|
||||||
"prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by default and will be disabled again once this screen is closed.",
|
"prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by this tool by default.",
|
||||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||||
|
|
||||||
"playLeft": "Play sound on left channel",
|
"playLeft": "Play sound on left channel",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "common/ide.hpp"
|
#include "common/ide.hpp"
|
||||||
|
#include "common/idedefs.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "ps1/registers573.h"
|
#include "ps1/registers573.h"
|
||||||
@ -22,13 +23,6 @@
|
|||||||
|
|
||||||
namespace ide {
|
namespace ide {
|
||||||
|
|
||||||
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
|
||||||
static constexpr int _REQUEST_SENSE_TIMEOUT = 500000;
|
|
||||||
static constexpr int _DETECT_DRIVE_TIMEOUT = 500000;
|
|
||||||
|
|
||||||
static constexpr int _DMA_TIMEOUT = 10000;
|
|
||||||
static constexpr int _SRST_DELAY = 5000;
|
|
||||||
|
|
||||||
static const char *const _SENSE_KEY_NAMES[]{
|
static const char *const _SENSE_KEY_NAMES[]{
|
||||||
"NO_SENSE",
|
"NO_SENSE",
|
||||||
"RECOVERED_ERROR",
|
"RECOVERED_ERROR",
|
||||||
@ -39,25 +33,25 @@ static const char *const _SENSE_KEY_NAMES[]{
|
|||||||
"UNIT_ATTENTION",
|
"UNIT_ATTENTION",
|
||||||
"DATA_PROTECT",
|
"DATA_PROTECT",
|
||||||
"BLANK_CHECK",
|
"BLANK_CHECK",
|
||||||
nullptr,
|
"UNKNOWN_9",
|
||||||
nullptr,
|
"UNKNOWN_A",
|
||||||
"ABORTED_COMMAND",
|
"ABORTED_COMMAND",
|
||||||
nullptr,
|
"UNKNOWN_C",
|
||||||
nullptr,
|
"UNKNOWN_D",
|
||||||
"MISCOMPARE",
|
"MISCOMPARE",
|
||||||
nullptr
|
"UNKNOWN_F"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const DEVICE_ERROR_NAMES[]{
|
const char *const DEVICE_ERROR_NAMES[]{
|
||||||
"NO_ERROR",
|
"NO_ERROR",
|
||||||
"UNSUPPORTED_OP",
|
"UNSUPPORTED_OP",
|
||||||
"NO_DRIVE",
|
"NO_DRIVE",
|
||||||
|
"NOT_YET_READY",
|
||||||
"STATUS_TIMEOUT",
|
"STATUS_TIMEOUT",
|
||||||
|
"CHECKSUM_MISMATCH",
|
||||||
"DRIVE_ERROR",
|
"DRIVE_ERROR",
|
||||||
"DISC_ERROR",
|
"DISC_ERROR",
|
||||||
"DISC_CHANGED",
|
"DISC_CHANGED"
|
||||||
"INCOMPLETE_DATA",
|
|
||||||
"CHECKSUM_MISMATCH"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
@ -92,6 +86,50 @@ static void _copyString(char *output, const uint16_t *input, size_t length) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static DeviceError _senseDataToError(const SenseData &data) {
|
||||||
|
auto key = data.senseKey & 15;
|
||||||
|
auto asc = data.getPackedASC();
|
||||||
|
auto lba = data.getErrorLBA();
|
||||||
|
|
||||||
|
LOG("%s", _SENSE_KEY_NAMES[key]);
|
||||||
|
LOG("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey);
|
||||||
|
LOG("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier);
|
||||||
|
|
||||||
|
if (lba) {
|
||||||
|
LOG("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)
|
||||||
|
? NO_ERROR
|
||||||
|
: DISC_CHANGED;
|
||||||
|
|
||||||
|
case SENSE_KEY_ABORTED_COMMAND:
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DRIVE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IdentifyBlock::validateChecksum(void) const {
|
bool IdentifyBlock::validateChecksum(void) const {
|
||||||
if ((checksum & 0xff) != 0xa5)
|
if ((checksum & 0xff) != 0xa5)
|
||||||
return true;
|
return true;
|
||||||
@ -122,29 +160,129 @@ int IdentifyBlock::getHighestPIOMode(void) const {
|
|||||||
|
|
||||||
/* Device class */
|
/* Device class */
|
||||||
|
|
||||||
|
static constexpr int _DMA_TIMEOUT = 10000;
|
||||||
|
|
||||||
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
|
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
|
||||||
|
|
||||||
DeviceError Device::_waitForStatus(
|
Device::Device(uint32_t flags)
|
||||||
uint8_t mask, uint8_t value, int timeout, bool ignoreErrors
|
: flags(flags), capacity(0), lastStatusReg(0), lastErrorReg(0) {
|
||||||
) {
|
util::clear(lastSenseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::_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 Device::_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 Device::_readDMA(void *data, size_t length) const {
|
||||||
|
length += 3;
|
||||||
|
length /= 4;
|
||||||
|
|
||||||
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
|
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||||
|
DMA_BCR (DMA_PIO) = length;
|
||||||
|
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 Device::_writeDMA(const void *data, size_t length) const {
|
||||||
|
length += 3;
|
||||||
|
length /= 4;
|
||||||
|
|
||||||
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
|
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||||
|
DMA_BCR (DMA_PIO) = length;
|
||||||
|
DMA_CHCR(DMA_PIO) = 0
|
||||||
|
| DMA_CHCR_WRITE
|
||||||
|
| DMA_CHCR_MODE_BURST
|
||||||
|
| DMA_CHCR_ENABLE
|
||||||
|
| DMA_CHCR_TRIGGER;
|
||||||
|
|
||||||
|
return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status and error polling */
|
||||||
|
|
||||||
|
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||||
|
static constexpr int _DRQ_TIMEOUT = 30000000;
|
||||||
|
static constexpr int _DETECT_TIMEOUT = 500000;
|
||||||
|
static constexpr int _SRST_DELAY = 5000;
|
||||||
|
|
||||||
|
// 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 Device::_waitForIdle(bool drdy, int timeout, bool ignoreError) {
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
timeout = _COMMAND_TIMEOUT;
|
timeout = _COMMAND_TIMEOUT;
|
||||||
|
|
||||||
for (; timeout > 0; timeout -= 10) {
|
for (; timeout > 0; timeout -= 10) {
|
||||||
uint8_t status = _read(CS0_STATUS);
|
auto status = _read(CS0_STATUS);
|
||||||
|
|
||||||
if (!ignoreErrors && (status & CS0_STATUS_ERR)) {
|
// Only check for errors *after* BSY is cleared.
|
||||||
LOG(
|
if (!(status & CS0_STATUS_BSY)) {
|
||||||
"drive %d error, stat=0x%02x, err=0x%02x",
|
if ((status & CS0_STATUS_ERR) && !ignoreError) {
|
||||||
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS),
|
_handleError();
|
||||||
_read(CS0_ERROR)
|
return DRIVE_ERROR;
|
||||||
);
|
}
|
||||||
|
|
||||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
if ((status & CS0_STATUS_DRDY) || !drdy)
|
||||||
return DRIVE_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((status & mask) == value)
|
delayMicroseconds(10);
|
||||||
|
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||||
|
io::clearWatchdog();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("timeout, ignore=%d", ignoreError);
|
||||||
|
_handleTimeout();
|
||||||
|
return STATUS_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError Device::_waitForDRQ(int timeout, bool ignoreError) {
|
||||||
|
if (!timeout)
|
||||||
|
timeout = _DRQ_TIMEOUT;
|
||||||
|
|
||||||
|
for (; timeout > 0; timeout -= 10) {
|
||||||
|
auto status = _read(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;
|
return NO_ERROR;
|
||||||
|
|
||||||
delayMicroseconds(10);
|
delayMicroseconds(10);
|
||||||
@ -153,66 +291,48 @@ DeviceError Device::_waitForStatus(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(
|
LOG("timeout, ignore=%d", ignoreError);
|
||||||
"drive %d timeout, stat=0x%02x, err=0x%02x",
|
_handleTimeout();
|
||||||
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), _read(CS0_ERROR)
|
|
||||||
);
|
|
||||||
|
|
||||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
|
||||||
return STATUS_TIMEOUT;
|
return STATUS_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_select(
|
void Device::_handleError(void) {
|
||||||
uint8_t devSelFlags, int timeout, bool ignoreErrors
|
lastStatusReg = _read(CS0_STATUS);
|
||||||
) {
|
lastErrorReg = _read(CS0_ERROR);
|
||||||
if (flags & DEVICE_SECONDARY)
|
|
||||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_SECONDARY);
|
|
||||||
else
|
|
||||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_PRIMARY);
|
|
||||||
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0, timeout, ignoreErrors);
|
LOG(
|
||||||
|
"drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1,
|
||||||
|
lastStatusReg, lastErrorReg
|
||||||
|
);
|
||||||
|
|
||||||
|
// Issuing a device reset command to an ATAPI drive would result in the
|
||||||
|
// error's sense data being lost.
|
||||||
|
if (!(flags & DEVICE_ATAPI))
|
||||||
|
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_setLBA(uint64_t lba, size_t count, int timeout) {
|
void Device::_handleTimeout(void) {
|
||||||
if (flags & DEVICE_HAS_LBA48) {
|
lastStatusReg = _read(CS0_STATUS);
|
||||||
//assert(lba < (1ULL << 48));
|
lastErrorReg = _read(CS0_ERROR);
|
||||||
//assert(count <= (1 << 16));
|
|
||||||
|
|
||||||
auto error = _select(CS0_DEVICE_SEL_LBA, timeout);
|
LOG(
|
||||||
|
"drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1,
|
||||||
|
lastStatusReg, lastErrorReg
|
||||||
|
);
|
||||||
|
|
||||||
if (error)
|
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||||
return error;
|
|
||||||
|
|
||||||
_write(CS0_COUNT, (count >> 8) & 0xff);
|
|
||||||
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
|
||||||
} else {
|
|
||||||
//assert(lba < (1ULL << 28));
|
|
||||||
//assert(count <= (1 << 8));
|
|
||||||
|
|
||||||
auto error = _select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15), timeout);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
_write(CS0_COUNT, (count >> 0) & 0xff);
|
|
||||||
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_detectDrive(void) {
|
DeviceError Device::_resetDrive(void) {
|
||||||
// Issue a software reset, which affects both devices on the bus.
|
// Issue a software reset, which affects both devices on the bus.
|
||||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST);
|
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST);
|
||||||
delayMicroseconds(_SRST_DELAY);
|
delayMicroseconds(_SRST_DELAY);
|
||||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
||||||
delayMicroseconds(_SRST_DELAY);
|
delayMicroseconds(_SRST_DELAY);
|
||||||
|
|
||||||
if (_select(0, _DETECT_DRIVE_TIMEOUT, true)) {
|
_select(0);
|
||||||
|
|
||||||
|
if (_waitForIdle(false, _DETECT_TIMEOUT, true)) {
|
||||||
LOG("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1);
|
LOG("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1);
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
}
|
}
|
||||||
@ -225,129 +345,62 @@ DeviceError Device::_detectDrive(void) {
|
|||||||
// the written value. This should not fail even if the drive is busy.
|
// the written value. This should not fail even if the drive is busy.
|
||||||
uint8_t pattern = 0x55;
|
uint8_t pattern = 0x55;
|
||||||
|
|
||||||
for (int timeout = _DETECT_DRIVE_TIMEOUT; timeout > 0; timeout -= 100) {
|
for (int timeout = _DETECT_TIMEOUT; timeout > 0; timeout -= 10) {
|
||||||
_write(CS0_COUNT, pattern);
|
_write(CS0_COUNT, pattern);
|
||||||
|
|
||||||
// Note that ATA drives will also assert DRDY when ready, but ATAPI
|
if (_read(CS0_COUNT) == pattern) {
|
||||||
// drives will not.
|
_write(CS0_COUNT, 0);
|
||||||
if (_read(CS0_COUNT) == pattern)
|
return NO_ERROR;
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
}
|
||||||
|
|
||||||
pattern >>= 1;
|
pattern ^= 0xff;
|
||||||
if (!(pattern & 1))
|
|
||||||
pattern |= 1 << 7;
|
|
||||||
|
|
||||||
delayMicroseconds(100);
|
delayMicroseconds(10);
|
||||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||||
io::clearWatchdog();
|
io::clearWatchdog();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("drive %d not found", (flags / DEVICE_SECONDARY) & 1);
|
LOG("drive %d not responding", (flags / DEVICE_SECONDARY) & 1);
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_readPIO(
|
/* ATA-specific function */
|
||||||
void *data, size_t length, int timeout, bool ignoreErrors
|
|
||||||
) {
|
|
||||||
util::assertAligned<uint16_t>(data);
|
|
||||||
|
|
||||||
auto error = _waitForStatus(
|
DeviceError Device::_ataSetLBA(uint64_t lba, size_t count, int timeout) {
|
||||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
if (flags & DEVICE_HAS_LBA48) {
|
||||||
);
|
//assert(lba < (1ULL << 48));
|
||||||
|
//assert(count <= (1 << 16));
|
||||||
|
_select(CS0_DEVICE_SEL_LBA);
|
||||||
|
|
||||||
if (error)
|
auto error = _waitForIdle(true);
|
||||||
return error;
|
|
||||||
|
|
||||||
auto ptr = reinterpret_cast<uint16_t *>(data);
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
for (; length; length -= 2)
|
_write(CS0_COUNT, (count >> 8) & 0xff);
|
||||||
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
|
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
||||||
|
} else {
|
||||||
|
//assert(lba < (1ULL << 28));
|
||||||
|
//assert(count <= (1 << 8));
|
||||||
|
_select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15));
|
||||||
|
|
||||||
return NO_ERROR;
|
auto error = _waitForIdle(true);
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::_writePIO(
|
if (error)
|
||||||
const void *data, size_t length, int timeout, bool ignoreErrors
|
return error;
|
||||||
) {
|
|
||||||
util::assertAligned<uint16_t>(data);
|
|
||||||
|
|
||||||
auto error = _waitForStatus(
|
|
||||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
auto ptr = reinterpret_cast<const uint16_t *>(data);
|
|
||||||
|
|
||||||
for (; length; length -= 2)
|
|
||||||
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::_readDMA(
|
|
||||||
void *data, size_t length, int timeout, bool ignoreErrors
|
|
||||||
) {
|
|
||||||
length /= 4;
|
|
||||||
|
|
||||||
util::assertAligned<uint32_t>(data);
|
|
||||||
|
|
||||||
auto error = _waitForStatus(
|
|
||||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
|
||||||
DMA_BCR (DMA_PIO) = length;
|
|
||||||
DMA_CHCR(DMA_PIO) = 0
|
|
||||||
| DMA_CHCR_READ
|
|
||||||
| DMA_CHCR_MODE_BURST
|
|
||||||
| DMA_CHCR_ENABLE
|
|
||||||
| DMA_CHCR_TRIGGER;
|
|
||||||
|
|
||||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) {
|
|
||||||
LOG("DMA transfer timeout");
|
|
||||||
return INCOMPLETE_DATA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_write(CS0_COUNT, (count >> 0) & 0xff);
|
||||||
|
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_writeDMA(
|
DeviceError Device::_ataTransfer(
|
||||||
const void *data, size_t length, int timeout, bool ignoreErrors
|
|
||||||
) {
|
|
||||||
length /= 4;
|
|
||||||
|
|
||||||
util::assertAligned<uint32_t>(data);
|
|
||||||
|
|
||||||
auto error = _waitForStatus(
|
|
||||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
|
||||||
DMA_BCR (DMA_PIO) = length;
|
|
||||||
DMA_CHCR(DMA_PIO) = 0
|
|
||||||
| DMA_CHCR_WRITE
|
|
||||||
| DMA_CHCR_MODE_BURST
|
|
||||||
| DMA_CHCR_ENABLE
|
|
||||||
| DMA_CHCR_TRIGGER;
|
|
||||||
|
|
||||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) {
|
|
||||||
LOG("DMA transfer timeout");
|
|
||||||
return INCOMPLETE_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::_ideReadWrite(
|
|
||||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||||
) {
|
) {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
@ -364,7 +417,7 @@ DeviceError Device::_ideReadWrite(
|
|||||||
while (count) {
|
while (count) {
|
||||||
size_t chunkLength = util::min(count, maxLength);
|
size_t chunkLength = util::min(count, maxLength);
|
||||||
|
|
||||||
auto error = _setLBA(lba, chunkLength);
|
auto error = _ataSetLBA(lba, chunkLength);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -373,41 +426,125 @@ DeviceError Device::_ideReadWrite(
|
|||||||
|
|
||||||
// Data must be transferred one sector at a time as the drive may
|
// Data must be transferred one sector at a time as the drive may
|
||||||
// deassert DRQ between sectors.
|
// deassert DRQ between sectors.
|
||||||
for (size_t i = chunkLength; i; i--) {
|
for (size_t i = chunkLength; i; i--, ptr += ATA_SECTOR_SIZE) {
|
||||||
if (write)
|
auto error = _waitForDRQ();
|
||||||
error = _writePIO(
|
|
||||||
reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE
|
|
||||||
);
|
|
||||||
else
|
|
||||||
error = _readPIO(
|
|
||||||
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
ptr += ATA_SECTOR_SIZE;
|
if (write)
|
||||||
|
_writePIO(reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE);
|
||||||
|
else
|
||||||
|
_readPIO(reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = _waitForStatus(
|
|
||||||
CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
lba += chunkLength;
|
lba += chunkLength;
|
||||||
count -= chunkLength;
|
count -= chunkLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO_ERROR;
|
return _waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATAPI-specific functions */
|
||||||
|
|
||||||
|
static constexpr int _ATAPI_READY_TIMEOUT = 30000000;
|
||||||
|
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 Device::_atapiRequestSense(void) {
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
|
packet.setRequestSense();
|
||||||
|
_select(0);
|
||||||
|
|
||||||
|
auto error = _waitForIdle(false, _REQ_SENSE_TIMEOUT, true);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
_setCylinder(sizeof(SenseData));
|
||||||
|
_write(CS0_COMMAND, ATA_PACKET);
|
||||||
|
|
||||||
|
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||||
|
}
|
||||||
|
if (!error) {
|
||||||
|
_writePIO(&packet, getPacketSize());
|
||||||
|
|
||||||
|
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||||
|
}
|
||||||
|
if (!error) {
|
||||||
|
_readPIO(&lastSenseData, sizeof(SenseData));
|
||||||
|
LOG("sense data ok");
|
||||||
|
} else {
|
||||||
|
// If the request sense command fails, fall back to reading the sense
|
||||||
|
// key from the error register.
|
||||||
|
util::clear(lastSenseData);
|
||||||
|
|
||||||
|
lastSenseData.senseKey = lastErrorReg >> 4;
|
||||||
|
LOG("%s", DEVICE_ERROR_NAMES[error]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _senseDataToError(lastSenseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) {
|
||||||
|
if (!(flags & DEVICE_READY))
|
||||||
|
return NO_DRIVE;
|
||||||
|
if (!(flags & DEVICE_ATAPI))
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
|
LOG("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 -= 10000) {
|
||||||
|
_select(0);
|
||||||
|
|
||||||
|
auto error = _waitForIdle();
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
_setCylinder(dataLength);
|
||||||
|
_write(CS0_COMMAND, ATA_PACKET);
|
||||||
|
|
||||||
|
error = _waitForDRQ();
|
||||||
|
}
|
||||||
|
if (!error) {
|
||||||
|
_writePIO(&packet, getPacketSize());
|
||||||
|
|
||||||
|
error = dataLength
|
||||||
|
? _waitForDRQ()
|
||||||
|
: _waitForIdle();
|
||||||
|
}
|
||||||
|
if (!error)
|
||||||
|
return NO_ERROR;
|
||||||
|
|
||||||
|
// If an error occurred, fetch sense data to determine whether to resend
|
||||||
|
// the command.
|
||||||
|
LOG("%s, cmd=0x%02x", DEVICE_ERROR_NAMES[error], packet.command);
|
||||||
|
|
||||||
|
error = _atapiRequestSense();
|
||||||
|
|
||||||
|
if (error && (error != NOT_YET_READY)) {
|
||||||
|
LOG("%s (from sense)", DEVICE_ERROR_NAMES[error]);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
delayMicroseconds(10000);
|
||||||
|
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||||
|
io::clearWatchdog();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("retry timeout, cmd=0x%02x", packet.command);
|
||||||
|
return STATUS_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
||||||
Packet packet;
|
Packet packet;
|
||||||
|
|
||||||
packet.setRead(lba, count);
|
packet.setRead(lba, count);
|
||||||
auto error = atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
|
||||||
|
auto error = _atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -415,19 +552,28 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
|||||||
// Data must be transferred one sector at a time as the drive may deassert
|
// Data must be transferred one sector at a time as the drive may deassert
|
||||||
// DRQ between sectors.
|
// DRQ between sectors.
|
||||||
for (; count; count--) {
|
for (; count; count--) {
|
||||||
if (_readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE))
|
auto error = _waitForDRQ();
|
||||||
return atapiPoll();
|
|
||||||
|
|
||||||
ptr += ATAPI_SECTOR_SIZE;
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
size_t chunkLength = _getCylinder();
|
||||||
|
|
||||||
|
_readPIO(reinterpret_cast<void *>(ptr), chunkLength);
|
||||||
|
ptr += chunkLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
return _waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Public API */
|
||||||
|
|
||||||
|
static constexpr uint16_t _ATAPI_SIGNATURE = 0xeb14;
|
||||||
|
|
||||||
DeviceError Device::enumerate(void) {
|
DeviceError Device::enumerate(void) {
|
||||||
flags &= DEVICE_PRIMARY | DEVICE_SECONDARY;
|
flags &= DEVICE_PRIMARY | DEVICE_SECONDARY;
|
||||||
|
|
||||||
auto error = _detectDrive();
|
auto error = _resetDrive();
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -440,17 +586,25 @@ DeviceError Device::enumerate(void) {
|
|||||||
// to prevent blocking for too long.
|
// to prevent blocking for too long.
|
||||||
IdentifyBlock block;
|
IdentifyBlock block;
|
||||||
|
|
||||||
//_select();
|
if (_getCylinder() == _ATAPI_SIGNATURE) {
|
||||||
|
|
||||||
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
|
|
||||||
flags |= DEVICE_ATAPI;
|
flags |= DEVICE_ATAPI;
|
||||||
|
|
||||||
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
||||||
|
} else {
|
||||||
|
_write(CS0_COMMAND, ATA_IDENTIFY);
|
||||||
|
}
|
||||||
|
|
||||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
if (_waitForDRQ(_DETECT_TIMEOUT))
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
if (!block.validateChecksum())
|
|
||||||
return CHECKSUM_MISMATCH;
|
_readPIO(&block, sizeof(IdentifyBlock));
|
||||||
|
|
||||||
|
if (!block.validateChecksum())
|
||||||
|
return CHECKSUM_MISMATCH;
|
||||||
|
|
||||||
|
// Parse the identification block.
|
||||||
|
if (flags & DEVICE_ATAPI) {
|
||||||
|
LOG("ATAPI drive found");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||||
@ -463,12 +617,7 @@ DeviceError Device::enumerate(void) {
|
|||||||
)
|
)
|
||||||
flags |= DEVICE_HAS_PACKET16;
|
flags |= DEVICE_HAS_PACKET16;
|
||||||
} else {
|
} else {
|
||||||
_write(CS0_COMMAND, ATA_IDENTIFY);
|
LOG("ATA drive found");
|
||||||
|
|
||||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
|
||||||
return NO_DRIVE;
|
|
||||||
if (!block.validateChecksum())
|
|
||||||
return CHECKSUM_MISMATCH;
|
|
||||||
|
|
||||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||||
flags |= DEVICE_HAS_LBA48;
|
flags |= DEVICE_HAS_LBA48;
|
||||||
@ -481,116 +630,42 @@ DeviceError Device::enumerate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_FULL_IDE_DRIVER
|
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||||
_copyString(model, block.model, sizeof(block.model));
|
_copyString(model, block.model, sizeof(block.model));
|
||||||
_copyString(revision, block.revision, sizeof(block.revision));
|
_copyString(revision, block.revision, sizeof(block.revision));
|
||||||
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
||||||
|
|
||||||
LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Find out the fastest PIO transfer mode supported and enable it.
|
// Find out the fastest PIO transfer mode supported and enable it.
|
||||||
int mode = block.getHighestPIOMode();
|
int mode = block.getHighestPIOMode();
|
||||||
|
|
||||||
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
||||||
_write(CS0_COUNT, (1 << 3) | mode);
|
_write(CS0_COUNT, TRANSFER_MODE_PIO | mode);
|
||||||
_write(CS0_COMMAND, ATA_SET_FEATURES);
|
_write(CS0_COMMAND, ATA_SET_FEATURES);
|
||||||
|
|
||||||
error = _waitForStatus(CS0_STATUS_BSY, 0);
|
error = _waitForIdle();
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
LOG("done, stat=0x%02x, mode=PIO%d", _read(CS0_STATUS), mode);
|
LOG("drive %d ready, mode=PIO%d", (flags / DEVICE_SECONDARY) & 1, mode);
|
||||||
flags |= DEVICE_READY;
|
flags |= DEVICE_READY;
|
||||||
return NO_ERROR;
|
|
||||||
|
// Make sure any pending ATAPI sense data is cleared.
|
||||||
|
return poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::atapiPacket(const Packet &packet, size_t transferLength) {
|
DeviceError Device::poll(void) {
|
||||||
if (!(flags & DEVICE_READY))
|
if (!(flags & DEVICE_READY))
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
if (!(flags & DEVICE_ATAPI))
|
|
||||||
return UNSUPPORTED_OP;
|
|
||||||
|
|
||||||
auto error = _select(0);
|
if (flags & DEVICE_ATAPI) {
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
if (error)
|
packet.setTestUnitReady();
|
||||||
return atapiPoll();
|
return _atapiPacket(packet);
|
||||||
|
|
||||||
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
|
||||||
_write(CS0_COMMAND, ATA_PACKET);
|
|
||||||
|
|
||||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return atapiPoll();
|
|
||||||
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::atapiPoll(void) {
|
|
||||||
if (!(flags & DEVICE_READY))
|
|
||||||
return NO_DRIVE;
|
|
||||||
if (!(flags & DEVICE_ATAPI))
|
|
||||||
return UNSUPPORTED_OP;
|
|
||||||
|
|
||||||
Packet packet;
|
|
||||||
SenseData data;
|
|
||||||
|
|
||||||
packet.setRequestSense();
|
|
||||||
|
|
||||||
// If an error occurs, the error flag in the status register will be set but
|
|
||||||
// the drive will still accept a request sense command.
|
|
||||||
auto error = _select(0, _REQUEST_SENSE_TIMEOUT, true);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
_write(CS0_CYLINDER_L, (sizeof(data) >> 0) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (sizeof(data) >> 8) & 0xff);
|
|
||||||
_write(CS0_COMMAND, ATA_PACKET);
|
|
||||||
|
|
||||||
error = _writePIO(
|
|
||||||
&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12, _REQUEST_SENSE_TIMEOUT
|
|
||||||
);
|
|
||||||
|
|
||||||
//if (!error)
|
|
||||||
//error = _waitForStatus(CS0_STATUS_BSY, 0, _REQUEST_SENSE_TIMEOUT);
|
|
||||||
if (!error)
|
|
||||||
error = _readPIO(&data, sizeof(data), _REQUEST_SENSE_TIMEOUT);
|
|
||||||
|
|
||||||
int senseKey;
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
// If the request sense command fails, fall back to reading the sense
|
|
||||||
// key from the IDE error register.
|
|
||||||
senseKey = (_read(CS0_ERROR) >> 4) & 15;
|
|
||||||
LOG("request sense failed");
|
|
||||||
} else {
|
} else {
|
||||||
senseKey = data.senseKey & 15;
|
_select(CS0_DEVICE_SEL_LBA);
|
||||||
LOG(
|
return _waitForIdle(true);
|
||||||
"key=0x%02x, asc=0x%02x, ascq=0x%02x", data.senseKey, data.asc,
|
|
||||||
data.ascQualifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG("sense key: %s (%d)", _SENSE_KEY_NAMES[senseKey], senseKey);
|
|
||||||
|
|
||||||
switch (senseKey) {
|
|
||||||
case SENSE_KEY_NO_SENSE:
|
|
||||||
case SENSE_KEY_RECOVERED_ERROR:
|
|
||||||
return NO_ERROR;
|
|
||||||
|
|
||||||
case SENSE_KEY_NOT_READY:
|
|
||||||
case SENSE_KEY_MEDIUM_ERROR:
|
|
||||||
case SENSE_KEY_DATA_PROTECT:
|
|
||||||
return DISC_ERROR;
|
|
||||||
|
|
||||||
case SENSE_KEY_UNIT_ATTENTION:
|
|
||||||
return DISC_CHANGED;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return DRIVE_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,7 +680,7 @@ DeviceError Device::readData(void *data, uint64_t lba, size_t count) {
|
|||||||
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
return _ideReadWrite(
|
return _ataTransfer(
|
||||||
reinterpret_cast<uintptr_t>(data), lba, count, false
|
reinterpret_cast<uintptr_t>(data), lba, count, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -618,51 +693,60 @@ DeviceError Device::writeData(const void *data, uint64_t lba, size_t count) {
|
|||||||
if (flags & (DEVICE_READ_ONLY | DEVICE_ATAPI))
|
if (flags & (DEVICE_READ_ONLY | DEVICE_ATAPI))
|
||||||
return UNSUPPORTED_OP;
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
return _ideReadWrite(reinterpret_cast<uintptr_t>(data), lba, count, true);
|
return _ataTransfer(reinterpret_cast<uintptr_t>(data), lba, count, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::goIdle(bool standby) {
|
DeviceError Device::goIdle(bool standby) {
|
||||||
if (!(flags & DEVICE_READY))
|
if (!(flags & DEVICE_READY))
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
|
if (flags & DEVICE_ATAPI)
|
||||||
|
return startStopUnit(START_STOP_MODE_STOP_DISC);
|
||||||
|
|
||||||
if (flags & DEVICE_ATAPI) {
|
_select(CS0_DEVICE_SEL_LBA);
|
||||||
Packet packet;
|
|
||||||
|
|
||||||
packet.setStartStopUnit(START_STOP_MODE_STOP_DISC);
|
auto error = _waitForIdle(true);
|
||||||
return atapiPacket(packet);
|
|
||||||
} else {
|
|
||||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (standby)
|
_write(CS0_COMMAND, standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
|
||||||
_write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE);
|
return _waitForIdle();
|
||||||
else
|
}
|
||||||
_write(CS0_COMMAND, ATA_IDLE_IMMEDIATE);
|
|
||||||
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
DeviceError Device::startStopUnit(ATAPIStartStopMode mode) {
|
||||||
}
|
if (!(flags & DEVICE_READY))
|
||||||
|
return NO_DRIVE;
|
||||||
|
if (!(flags & DEVICE_ATAPI))
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
|
packet.setStartStopUnit(mode);
|
||||||
|
return _atapiPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::flushCache(void) {
|
DeviceError Device::flushCache(void) {
|
||||||
if (!(flags & DEVICE_READY))
|
if (!(flags & DEVICE_READY))
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
if (!(flags & DEVICE_HAS_FLUSH))
|
if (!(flags & DEVICE_HAS_FLUSH))
|
||||||
|
#if 0
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
#else
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
//return UNSUPPORTED_OP;
|
#endif
|
||||||
|
|
||||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
_select(CS0_DEVICE_SEL_LBA);
|
||||||
|
|
||||||
|
auto error = _waitForIdle(true);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (flags & DEVICE_HAS_LBA48)
|
_write(
|
||||||
_write(CS0_COMMAND, ATA_FLUSH_CACHE_EXT);
|
CS0_COMMAND,
|
||||||
else
|
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
|
||||||
_write(CS0_COMMAND, ATA_FLUSH_CACHE);
|
);
|
||||||
|
return _waitForIdle();
|
||||||
return _waitForStatus(CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,169 +3,15 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "common/idedefs.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "ps1/registers573.h"
|
#include "ps1/registers573.h"
|
||||||
|
|
||||||
namespace ide {
|
namespace ide {
|
||||||
|
|
||||||
/* Register definitions */
|
static constexpr size_t ATA_SECTOR_SIZE = 512;
|
||||||
|
|
||||||
enum CS0Register {
|
|
||||||
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 CS1Register {
|
|
||||||
CS1_ALT_STATUS = 6,
|
|
||||||
CS1_DEVICE_CTRL = 6
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CS0StatusFlag : uint8_t {
|
|
||||||
CS0_STATUS_ERR = 1 << 0, // Error
|
|
||||||
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 CS0DeviceSelectFlag : uint8_t {
|
|
||||||
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
|
|
||||||
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
|
|
||||||
CS0_DEVICE_SEL_LBA = 1 << 6
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CS1DeviceControlFlag : 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)
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ATA protocol definitions */
|
|
||||||
|
|
||||||
static constexpr size_t ATA_SECTOR_SIZE = 512;
|
|
||||||
|
|
||||||
enum ATACommand : uint8_t {
|
|
||||||
ATA_NOP = 0x00,
|
|
||||||
ATA_DEVICE_RESET = 0x08,
|
|
||||||
ATA_READ_SECTORS = 0x20,
|
|
||||||
ATA_READ_SECTORS_EXT = 0x24,
|
|
||||||
ATA_READ_DMA_EXT = 0x25,
|
|
||||||
ATA_READ_DMA_QUEUED_EXT = 0x26,
|
|
||||||
ATA_WRITE_SECTORS = 0x30,
|
|
||||||
ATA_WRITE_SECTORS_EXT = 0x34,
|
|
||||||
ATA_WRITE_DMA_EXT = 0x35,
|
|
||||||
ATA_WRITE_DMA_QUEUED_EXT = 0x36,
|
|
||||||
ATA_SEEK = 0x70,
|
|
||||||
ATA_EXECUTE_DIAGNOSTIC = 0x90,
|
|
||||||
ATA_PACKET = 0xa0,
|
|
||||||
ATA_IDENTIFY_PACKET = 0xa1,
|
|
||||||
ATA_SERVICE = 0xa2,
|
|
||||||
ATA_DEVICE_CONFIG = 0xb1,
|
|
||||||
ATA_ERASE_SECTORS = 0xc0,
|
|
||||||
ATA_READ_DMA_QUEUED = 0xc7,
|
|
||||||
ATA_READ_DMA = 0xc8,
|
|
||||||
ATA_WRITE_DMA = 0xca,
|
|
||||||
ATA_WRITE_DMA_QUEUED = 0xcc,
|
|
||||||
ATA_STANDBY_IMMEDIATE = 0xe0,
|
|
||||||
ATA_IDLE_IMMEDIATE = 0xe1,
|
|
||||||
ATA_STANDBY = 0xe2,
|
|
||||||
ATA_IDLE = 0xe3,
|
|
||||||
ATA_CHECK_POWER_MODE = 0xe5,
|
|
||||||
ATA_SLEEP = 0xe6,
|
|
||||||
ATA_FLUSH_CACHE = 0xe7,
|
|
||||||
ATA_FLUSH_CACHE_EXT = 0xea,
|
|
||||||
ATA_IDENTIFY = 0xec,
|
|
||||||
ATA_SET_FEATURES = 0xef
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ATAFeature : uint8_t {
|
|
||||||
FEATURE_8BIT_DATA = 0x01,
|
|
||||||
FEATURE_WRITE_CACHE = 0x02,
|
|
||||||
FEATURE_TRANSFER_MODE = 0x03,
|
|
||||||
FEATURE_APM = 0x05,
|
|
||||||
FEATURE_AAM = 0x42,
|
|
||||||
FEATURE_RELEASE_IRQ = 0x5d,
|
|
||||||
FEATURE_SERVICE_IRQ = 0x5e,
|
|
||||||
FEATURE_DISABLE = 0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ATAPI protocol definitions */
|
|
||||||
|
|
||||||
static constexpr size_t ATAPI_SECTOR_SIZE = 2048;
|
static constexpr size_t ATAPI_SECTOR_SIZE = 2048;
|
||||||
|
|
||||||
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 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 ATAPIStartStopMode : uint8_t {
|
|
||||||
START_STOP_MODE_STOP_DISC = 0,
|
|
||||||
START_STOP_MODE_START_DISC = 1,
|
|
||||||
START_STOP_MODE_OPEN_TRAY = 2,
|
|
||||||
START_STOP_MODE_CLOSE_TRAY = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Identification blocks */
|
/* Identification blocks */
|
||||||
|
|
||||||
enum IdentifyDeviceFlag : uint16_t {
|
enum IdentifyDeviceFlag : uint16_t {
|
||||||
@ -275,6 +121,9 @@ public:
|
|||||||
| (info[2] << 8)
|
| (info[2] << 8)
|
||||||
| (info[3] << 0);
|
| (info[3] << 0);
|
||||||
}
|
}
|
||||||
|
inline uint16_t getPackedASC(void) const {
|
||||||
|
return asc | (ascQualifier << 8);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Packet {
|
class Packet {
|
||||||
@ -283,12 +132,34 @@ public:
|
|||||||
uint8_t param[11];
|
uint8_t param[11];
|
||||||
uint8_t _reserved[4];
|
uint8_t _reserved[4];
|
||||||
|
|
||||||
|
inline void setTestUnitReady(void) {
|
||||||
|
util::clear(*this);
|
||||||
|
|
||||||
|
//command = ATAPI_TEST_UNIT_READY;
|
||||||
|
}
|
||||||
|
inline void setRequestSense(uint8_t additionalLength = 0) {
|
||||||
|
util::clear(*this);
|
||||||
|
|
||||||
|
command = ATAPI_REQUEST_SENSE;
|
||||||
|
param[3] = sizeof(SenseData) + additionalLength;
|
||||||
|
}
|
||||||
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
||||||
util::clear(*this);
|
util::clear(*this);
|
||||||
|
|
||||||
command = ATAPI_START_STOP_UNIT;
|
command = ATAPI_START_STOP_UNIT;
|
||||||
param[3] = mode;
|
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) {
|
inline void setRead(uint32_t lba, size_t count) {
|
||||||
util::clear(*this);
|
util::clear(*this);
|
||||||
|
|
||||||
@ -309,23 +180,6 @@ public:
|
|||||||
param[1] = (value >> 8) & 0xff;
|
param[1] = (value >> 8) & 0xff;
|
||||||
param[2] = (value >> 0) & 0xff;
|
param[2] = (value >> 0) & 0xff;
|
||||||
}
|
}
|
||||||
inline void setRequestSense(uint8_t additionalLength = 0) {
|
|
||||||
util::clear(*this);
|
|
||||||
|
|
||||||
command = ATAPI_REQUEST_SENSE;
|
|
||||||
param[3] = sizeof(SenseData) + additionalLength;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Device class */
|
/* Device class */
|
||||||
@ -334,12 +188,12 @@ enum DeviceError {
|
|||||||
NO_ERROR = 0,
|
NO_ERROR = 0,
|
||||||
UNSUPPORTED_OP = 1,
|
UNSUPPORTED_OP = 1,
|
||||||
NO_DRIVE = 2,
|
NO_DRIVE = 2,
|
||||||
STATUS_TIMEOUT = 3,
|
NOT_YET_READY = 3,
|
||||||
DRIVE_ERROR = 4,
|
STATUS_TIMEOUT = 4,
|
||||||
DISC_ERROR = 5,
|
CHECKSUM_MISMATCH = 5,
|
||||||
DISC_CHANGED = 6,
|
DRIVE_ERROR = 6,
|
||||||
INCOMPLETE_DATA = 7,
|
DISC_ERROR = 7,
|
||||||
CHECKSUM_MISMATCH = 8
|
DISC_CHANGED = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DeviceFlag {
|
enum DeviceFlag {
|
||||||
@ -357,46 +211,53 @@ enum DeviceFlag {
|
|||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
private:
|
private:
|
||||||
inline uint8_t _read(CS0Register reg) {
|
inline uint8_t _read(CS0Register reg) const {
|
||||||
return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff);
|
return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff);
|
||||||
}
|
}
|
||||||
inline void _write(CS0Register reg, uint8_t value) {
|
inline void _write(CS0Register reg, uint8_t value) const {
|
||||||
SYS573_IDE_CS0_BASE[reg] = value;
|
SYS573_IDE_CS0_BASE[reg] = value;
|
||||||
}
|
}
|
||||||
inline uint8_t _read(CS1Register reg) {
|
inline uint8_t _read(CS1Register reg) const {
|
||||||
return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff);
|
return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff);
|
||||||
}
|
}
|
||||||
inline void _write(CS1Register reg, uint8_t value) {
|
inline void _write(CS1Register reg, uint8_t value) const {
|
||||||
SYS573_IDE_CS1_BASE[reg] = value;
|
SYS573_IDE_CS1_BASE[reg] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError _waitForStatus(
|
inline void _select(uint8_t selFlags) const {
|
||||||
uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false
|
if (flags & DEVICE_SECONDARY)
|
||||||
);
|
_write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||||
DeviceError _select(
|
else
|
||||||
uint8_t devSelFlags, int timeout = 0, bool ignoreErrors = false
|
_write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||||
);
|
}
|
||||||
DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0);
|
inline void _setCylinder(uint16_t value) const {
|
||||||
DeviceError _detectDrive(void);
|
_write(CS0_CYLINDER_L, (value >> 0) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (value >> 8) & 0xff);
|
||||||
|
}
|
||||||
|
inline uint16_t _getCylinder(void) const {
|
||||||
|
return _read(CS0_CYLINDER_L) | (_read(CS0_CYLINDER_H) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
DeviceError _readPIO(
|
void _readPIO(void *data, size_t length) const;
|
||||||
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
void _writePIO(const void *data, size_t length) const;
|
||||||
);
|
bool _readDMA(void *data, size_t length) const;
|
||||||
DeviceError _writePIO(
|
bool _writeDMA(const void *data, size_t length) const;
|
||||||
const void *data, size_t length, int timeout = 0,
|
|
||||||
bool ignoreErrors = false
|
|
||||||
);
|
|
||||||
DeviceError _readDMA(
|
|
||||||
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
|
||||||
);
|
|
||||||
DeviceError _writeDMA(
|
|
||||||
const void *data, size_t length, int timeout = 0,
|
|
||||||
bool ignoreErrors = false
|
|
||||||
);
|
|
||||||
|
|
||||||
DeviceError _ideReadWrite(
|
DeviceError _waitForIdle(
|
||||||
|
bool drdy = false, int timeout = 0, bool ignoreError = false
|
||||||
|
);
|
||||||
|
DeviceError _waitForDRQ(int timeout = 0, bool ignoreError = false);
|
||||||
|
void _handleError(void);
|
||||||
|
void _handleTimeout(void);
|
||||||
|
DeviceError _resetDrive(void);
|
||||||
|
|
||||||
|
DeviceError _ataSetLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||||
|
DeviceError _ataTransfer(
|
||||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DeviceError _atapiRequestSense(void);
|
||||||
|
DeviceError _atapiPacket(const Packet &packet, size_t dataLength = 0);
|
||||||
DeviceError _atapiRead(uintptr_t ptr, uint32_t lba, size_t count);
|
DeviceError _atapiRead(uintptr_t ptr, uint32_t lba, size_t count);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -407,13 +268,16 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
uint64_t capacity;
|
uint64_t capacity;
|
||||||
|
|
||||||
inline Device(uint32_t flags)
|
uint8_t lastStatusReg, lastErrorReg;
|
||||||
: flags(flags), capacity(0) {}
|
SenseData lastSenseData;
|
||||||
|
|
||||||
inline size_t getSectorSize(void) const {
|
inline size_t getSectorSize(void) const {
|
||||||
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
inline bool isPointerAligned(const void *ptr) {
|
inline size_t getPacketSize(void) const {
|
||||||
|
return (flags & DEVICE_HAS_PACKET16) ? 16 : 12;
|
||||||
|
}
|
||||||
|
inline bool isPointerAligned(const void *ptr) const {
|
||||||
// DMA transfers require 4-byte alignment, while PIO transfers require
|
// DMA transfers require 4-byte alignment, while PIO transfers require
|
||||||
// 2-byte alignment.
|
// 2-byte alignment.
|
||||||
#if 0
|
#if 0
|
||||||
@ -423,13 +287,14 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Device(uint32_t flags);
|
||||||
DeviceError enumerate(void);
|
DeviceError enumerate(void);
|
||||||
DeviceError atapiPacket(const Packet &packet, size_t transferLength = 0);
|
DeviceError poll(void);
|
||||||
DeviceError atapiPoll(void);
|
|
||||||
|
|
||||||
DeviceError readData(void *data, uint64_t lba, size_t count);
|
DeviceError readData(void *data, uint64_t lba, size_t count);
|
||||||
DeviceError writeData(const void *data, uint64_t lba, size_t count);
|
DeviceError writeData(const void *data, uint64_t lba, size_t count);
|
||||||
DeviceError goIdle(bool standby = false);
|
DeviceError goIdle(bool standby = false);
|
||||||
|
DeviceError startStopUnit(ATAPIStartStopMode mode);
|
||||||
DeviceError flushCache(void);
|
DeviceError flushCache(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
238
src/common/idedefs.hpp
Normal file
238
src/common/idedefs.hpp
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace ide {
|
||||||
|
|
||||||
|
/* Register definitions */
|
||||||
|
|
||||||
|
enum CS0Register {
|
||||||
|
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 CS1Register {
|
||||||
|
CS1_ALT_STATUS = 6,
|
||||||
|
CS1_DEVICE_CTRL = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CS0StatusFlag : 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 CS0DeviceSelectFlag : uint8_t {
|
||||||
|
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
|
||||||
|
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
|
||||||
|
CS0_DEVICE_SEL_LBA = 1 << 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CS1DeviceControlFlag : 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)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ATA command definitions */
|
||||||
|
|
||||||
|
enum ATACommand : uint8_t {
|
||||||
|
ATA_NOP = 0x00,
|
||||||
|
ATA_DEVICE_RESET = 0x08,
|
||||||
|
ATA_READ_SECTORS = 0x20,
|
||||||
|
ATA_READ_SECTORS_EXT = 0x24,
|
||||||
|
ATA_READ_DMA_EXT = 0x25,
|
||||||
|
ATA_READ_DMA_QUEUED_EXT = 0x26,
|
||||||
|
ATA_WRITE_SECTORS = 0x30,
|
||||||
|
ATA_WRITE_SECTORS_EXT = 0x34,
|
||||||
|
ATA_WRITE_DMA_EXT = 0x35,
|
||||||
|
ATA_WRITE_DMA_QUEUED_EXT = 0x36,
|
||||||
|
ATA_SEEK = 0x70,
|
||||||
|
ATA_EXECUTE_DIAGNOSTIC = 0x90,
|
||||||
|
ATA_PACKET = 0xa0,
|
||||||
|
ATA_IDENTIFY_PACKET = 0xa1,
|
||||||
|
ATA_SERVICE = 0xa2,
|
||||||
|
ATA_DEVICE_CONFIG = 0xb1,
|
||||||
|
ATA_ERASE_SECTORS = 0xc0,
|
||||||
|
ATA_READ_DMA_QUEUED = 0xc7,
|
||||||
|
ATA_READ_DMA = 0xc8,
|
||||||
|
ATA_WRITE_DMA = 0xca,
|
||||||
|
ATA_WRITE_DMA_QUEUED = 0xcc,
|
||||||
|
ATA_STANDBY_IMMEDIATE = 0xe0,
|
||||||
|
ATA_IDLE_IMMEDIATE = 0xe1,
|
||||||
|
ATA_STANDBY = 0xe2,
|
||||||
|
ATA_IDLE = 0xe3,
|
||||||
|
ATA_CHECK_POWER_MODE = 0xe5,
|
||||||
|
ATA_SLEEP = 0xe6,
|
||||||
|
ATA_FLUSH_CACHE = 0xe7,
|
||||||
|
ATA_FLUSH_CACHE_EXT = 0xea,
|
||||||
|
ATA_IDENTIFY = 0xec,
|
||||||
|
ATA_SET_FEATURES = 0xef
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ATAFeature : uint8_t {
|
||||||
|
FEATURE_8BIT_DATA = 0x01,
|
||||||
|
FEATURE_WRITE_CACHE = 0x02,
|
||||||
|
FEATURE_TRANSFER_MODE = 0x03,
|
||||||
|
FEATURE_APM = 0x05,
|
||||||
|
FEATURE_AAM = 0x42,
|
||||||
|
FEATURE_RELEASE_IRQ = 0x5d,
|
||||||
|
FEATURE_SERVICE_IRQ = 0x5e,
|
||||||
|
FEATURE_DISABLE = 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ATATransferModeFlag : uint8_t {
|
||||||
|
TRANSFER_MODE_PIO_DEFAULT = 0 << 3,
|
||||||
|
TRANSFER_MODE_PIO = 1 << 3,
|
||||||
|
TRANSFER_MODE_DMA = 1 << 5,
|
||||||
|
TRANSFER_MODE_UDMA = 1 << 6
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ATAPI 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_DISC = 0,
|
||||||
|
START_STOP_MODE_START_DISC = 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 = 0x00 | (0x00 << 8), // "NO ADDITIONAL SENSE INFORMATION"
|
||||||
|
ASC_PLAY_IN_PROGRESS = 0x00 | (0x11 << 8), // "PLAY OPERATION IN PROGRESS"
|
||||||
|
ASC_PLAY_PAUSED = 0x00 | (0x12 << 8), // "PLAY OPERATION PAUSED"
|
||||||
|
ASC_PLAY_COMPLETED = 0x00 | (0x13 << 8), // "PLAY OPERATION SUCCESSFULLY COMPLETED"
|
||||||
|
ASC_PLAY_ERROR = 0x00 | (0x14 << 8), // "PLAY OPERATION STOPPED DUE TO ERROR"
|
||||||
|
ASC_NO_AUDIO_STATUS = 0x00 | (0x15 << 8), // "NO CURRENT AUDIO STATUS TO RETURN"
|
||||||
|
ASC_MECHANICAL_ERROR = 0x01 | (0x00 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||||
|
ASC_NO_SEEK_COMPLETE = 0x02 | (0x00 << 8), // "NO SEEK COMPLETE"
|
||||||
|
ASC_NOT_READY = 0x04 | (0x00 << 8), // "LOGICAL DRIVE NOT READY - CAUSE NOT REPORTABLE"
|
||||||
|
ASC_NOT_READY_IN_PROGRESS = 0x04 | (0x01 << 8), // "LOGICAL DRIVE NOT READY - IN PROGRESS OF BECOMING READY"
|
||||||
|
ASC_NOT_READY_INIT_REQ = 0x04 | (0x02 << 8), // "LOGICAL DRIVE NOT READY - INITIALIZING COMMAND REQUIRED"
|
||||||
|
ASC_NOT_READY_MANUAL_REQ = 0x04 | (0x03 << 8), // "LOGICAL DRIVE NOT READY - MANUAL INTERVENTION REQUIRED"
|
||||||
|
ASC_LOAD_EJECT_FAILED = 0x05 | (0x01 << 8), // "MEDIA LOAD - EJECT FAILED"
|
||||||
|
ASC_NO_REFERENCE_POSITION = 0x06 | (0x00 << 8), // "NO REFERENCE POSITION FOUND"
|
||||||
|
ASC_TRACK_FOLLOW_ERROR = 0x09 | (0x00 << 8), // "TRACK FOLLOWING ERROR"
|
||||||
|
ASC_TRACK_SERVO_FAILURE = 0x09 | (0x01 << 8), // "TRACKING SERVO FAILURE"
|
||||||
|
ASC_FOCUS_SERVO_FAILURE = 0x09 | (0x02 << 8), // "FOCUS SERVO FAILURE"
|
||||||
|
ASC_SPINDLE_SERVO_FAILURE = 0x09 | (0x03 << 8), // "SPINDLE SERVO FAILURE"
|
||||||
|
ASC_UNRECOVERED_READ_ERROR = 0x11 | (0x00 << 8), // "UNRECOVERED READ ERROR"
|
||||||
|
ASC_CIRC_UNRECOVERED_ERROR = 0x11 | (0x06 << 8), // "CIRC UNRECOVERED ERROR"
|
||||||
|
ASC_POSITIONING_ERROR = 0x15 | (0x00 << 8), // "RANDOM POSITIONING ERROR"
|
||||||
|
ASC_MECHANICAL_ERROR_2 = 0x15 | (0x01 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||||
|
ASC_POSITIONING_ERROR_2 = 0x15 | (0x02 << 8), // "POSITIONING ERROR DETECTED BY READ OF MEDIUM"
|
||||||
|
ASC_REC_DATA_NO_ECC = 0x17 | (0x00 << 8), // "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"
|
||||||
|
ASC_REC_DATA_RETRIES = 0x17 | (0x01 << 8), // "RECOVERED DATA WITH RETRIES"
|
||||||
|
ASC_REC_DATA_POS_OFFSET = 0x17 | (0x02 << 8), // "RECOVERED DATA WITH POSITIVE HEAD OFFSET"
|
||||||
|
ASC_REC_DATA_NEG_OFFSET = 0x17 | (0x03 << 8), // "RECOVERED DATA WITH NEGATIVE HEAD OFFSET"
|
||||||
|
ASC_REC_DATA_RETRIES_CIRC = 0x17 | (0x04 << 8), // "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"
|
||||||
|
ASC_REC_DATA_PREV_SECTOR = 0x17 | (0x05 << 8), // "RECOVERED DATA USING PREVIOUS SECTOR ID"
|
||||||
|
ASC_REC_DATA_ECC = 0x18 | (0x00 << 8), // "RECOVERED DATA WITH ERROR CORRECTION APPLIED"
|
||||||
|
ASC_REC_DATA_ECC_RETRIES = 0x18 | (0x01 << 8), // "RECOVERED DATA WITH ERROR CORRECTION & RETRIES APPLIED"
|
||||||
|
ASC_REC_DATA_REALLOCATED = 0x18 | (0x02 << 8), // "RECOVERED DATA - THE DATA WAS AUTO-REALLOCATED"
|
||||||
|
ASC_REC_DATA_CIRC = 0x18 | (0x03 << 8), // "RECOVERED DATA WITH CIRC"
|
||||||
|
ASC_REC_DATA_L_EC = 0x18 | (0x04 << 8), // "RECOVERED DATA WITH L-EC"
|
||||||
|
ASC_PARAM_LENGTH_ERROR = 0x1a | (0x00 << 8), // "PARAMETER LIST LENGTH ERROR"
|
||||||
|
ASC_INVALID_COMMAND = 0x20 | (0x00 << 8), // "INVALID COMMAND OPERATION CODE"
|
||||||
|
ASC_LBA_OUT_OF_RANGE = 0x21 | (0x00 << 8), // "LOGICAL BLOCK ADDRESS OUT OF RANGE"
|
||||||
|
ASC_INVALID_PACKET_FIELD = 0x24 | (0x00 << 8), // "INVALID FIELD IN COMMAND PACKET"
|
||||||
|
ASC_INVALID_PARAM_FIELD = 0x26 | (0x00 << 8), // "INVALID FIELD IN PARAMETER LIST"
|
||||||
|
ASC_PARAM_NOT_SUPPORTED = 0x26 | (0x01 << 8), // "PARAMETER NOT SUPPORTED"
|
||||||
|
ASC_PARAM_VALUE_INVALID = 0x26 | (0x02 << 8), // "PARAMETER VALUE INVALID"
|
||||||
|
ASC_NOT_READY_TO_READY = 0x28 | (0x00 << 8), // "NOT READY TO READY TRANSITION, MEDIUM MAY HAVE CHANGED"
|
||||||
|
ASC_RESET_OCCURRED = 0x29 | (0x00 << 8), // "POWER ON, RESET OR BUS DEVICE RESET OCCURRED"
|
||||||
|
ASC_PARAMS_CHANGED = 0x2a | (0x00 << 8), // "PARAMETERS CHANGED"
|
||||||
|
ASC_MODE_PARAMS_CHANGED = 0x2a | (0x01 << 8), // "MODE PARAMETERS CHANGED"
|
||||||
|
ASC_INCOMPATIBLE_MEDIUM = 0x30 | (0x00 << 8), // "INCOMPATIBLE MEDIUM INSTALLED"
|
||||||
|
ASC_UNKNOWN_FORMAT = 0x30 | (0x01 << 8), // "CANNOT READ MEDIUM - UNKNOWN FORMAT"
|
||||||
|
ASC_INCOMPATIBLE_FORMAT = 0x30 | (0x02 << 8), // "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"
|
||||||
|
ASC_SAVING_NOT_SUPPORTED = 0x39 | (0x00 << 8), // "SAVING PARAMETERS NOT SUPPORTED"
|
||||||
|
ASC_MEDIUM_NOT_PRESENT = 0x3a | (0x00 << 8), // "MEDIUM NOT PRESENT"
|
||||||
|
ASC_CONDITIONS_CHANGED = 0x3f | (0x00 << 8), // "ATAPI CD-ROM DRIVE OPERATING CONDITIONS HAVE CHANGED"
|
||||||
|
ASC_MICROCODE_CHANGED = 0x3f | (0x01 << 8), // "MICROCODE HAS BEEN CHANGED"
|
||||||
|
ASC_INTERNAL_DRIVE_FAILURE = 0x44 | (0x00 << 8), // "INTERNAL ATAPI CD-ROM DRIVE FAILURE"
|
||||||
|
ASC_OVERLAP_ATTEMPTED = 0x4e | (0x00 << 8), // "OVERLAPPED COMMANDS ATTEMPTED"
|
||||||
|
ASC_LOAD_EJECT_FAILED_2 = 0x53 | (0x00 << 8), // "MEDIA LOAD OR EJECT FAILED"
|
||||||
|
ASC_REMOVAL_PREVENTED = 0x53 | (0x02 << 8), // "MEDIUM REMOVAL PREVENTED"
|
||||||
|
ASC_UNABLE_TO_RECOVER_TOC = 0x57 | (0x00 << 8), // "UNABLE TO RECOVER TABLE OF CONTENTS"
|
||||||
|
ASC_OPERATOR_REQUEST = 0x5a | (0x00 << 8), // "OPERATOR REQUEST OR STATE CHANGE INPUT (UNSPECIFIED)"
|
||||||
|
ASC_REMOVAL_REQUEST = 0x5a | (0x01 << 8), // "OPERATOR MEDIUM REMOVAL REQUEST"
|
||||||
|
ASC_END_OF_USER_AREA = 0x63 | (0x00 << 8), // "END OF USER AREA ENCOUNTERED ON THIS TRACK"
|
||||||
|
ASC_ILLEGAL_TRACK_MODE = 0x64 | (0x00 << 8), // "ILLEGAL MODE FOR THIS TRACK"
|
||||||
|
ASC_PLAY_ABORTED = 0xb9 | (0x00 << 8), // "PLAY OPERATION ABORTED"
|
||||||
|
ASC_LOSS_OF_STREAMING = 0xbf | (0x00 << 8) // "LOSS OF STREAMING"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/rom.hpp"
|
#include "common/rom.hpp"
|
||||||
|
#include "common/romdrivers.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "ps1/registers.h"
|
#include "ps1/registers.h"
|
||||||
#include "ps1/registers573.h"
|
#include "ps1/registers573.h"
|
||||||
@ -165,29 +166,6 @@ uint32_t FlashRegion::zipCRC32(
|
|||||||
|
|
||||||
/* Flash-specific functions */
|
/* Flash-specific functions */
|
||||||
|
|
||||||
enum JEDECCommand : uint16_t {
|
|
||||||
_JEDEC_RESET = 0xf0f0,
|
|
||||||
_JEDEC_HANDSHAKE1 = 0xaaaa,
|
|
||||||
_JEDEC_HANDSHAKE2 = 0x5555,
|
|
||||||
_JEDEC_GET_ID = 0x9090,
|
|
||||||
_JEDEC_WRITE_BYTE = 0xa0a0,
|
|
||||||
_JEDEC_ERASE_HANDSHAKE = 0x8080,
|
|
||||||
_JEDEC_ERASE_CHIP = 0x1010,
|
|
||||||
_JEDEC_ERASE_SECTOR = 0x3030
|
|
||||||
};
|
|
||||||
|
|
||||||
enum IntelCommand : uint16_t {
|
|
||||||
_INTEL_RESET = 0xffff,
|
|
||||||
_INTEL_GET_ID = 0x9090,
|
|
||||||
_INTEL_WRITE_BYTE = 0x4040,
|
|
||||||
_INTEL_ERASE_SECTOR1 = 0x2020,
|
|
||||||
_INTEL_ERASE_SECTOR2 = 0xd0d0,
|
|
||||||
_INTEL_GET_STATUS = 0x7070,
|
|
||||||
_INTEL_CLEAR_STATUS = 0x5050,
|
|
||||||
_INTEL_SUSPEND = 0xb0b0,
|
|
||||||
_INTEL_RESUME = 0xd0d0
|
|
||||||
};
|
|
||||||
|
|
||||||
enum FlashIdentifier : uint16_t {
|
enum FlashIdentifier : uint16_t {
|
||||||
_ID_AM29F016 = 0x01 | (0xad << 8),
|
_ID_AM29F016 = 0x01 | (0xad << 8),
|
||||||
_ID_AM29F040 = 0x01 | (0xa4 << 8),
|
_ID_AM29F040 = 0x01 | (0xa4 << 8),
|
||||||
@ -234,16 +212,16 @@ uint32_t FlashRegion::getJEDECID(void) const {
|
|||||||
|
|
||||||
auto _ptr = reinterpret_cast<volatile uint16_t *>(ptr);
|
auto _ptr = reinterpret_cast<volatile uint16_t *>(ptr);
|
||||||
|
|
||||||
_ptr[0x000] = _JEDEC_RESET;
|
_ptr[0x000] = JEDEC_RESET;
|
||||||
_ptr[0x000] = _INTEL_RESET;
|
_ptr[0x000] = INTEL_RESET;
|
||||||
_ptr[0x555] = _JEDEC_HANDSHAKE1;
|
_ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
_ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
_ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
_ptr[0x555] = _JEDEC_GET_ID; // Same as _INTEL_GET_ID
|
_ptr[0x555] = JEDEC_GET_ID; // Same as INTEL_GET_ID
|
||||||
|
|
||||||
uint32_t id = _ptr[0] | (_ptr[1] << 16);
|
uint32_t id = _ptr[0] | (_ptr[1] << 16);
|
||||||
|
|
||||||
_ptr[0x000] = _JEDEC_RESET;
|
_ptr[0x000] = JEDEC_RESET;
|
||||||
_ptr[0x000] = _INTEL_RESET;
|
_ptr[0x000] = INTEL_RESET;
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -301,356 +279,6 @@ const FlashRegion pcmcia[2]{
|
|||||||
{ SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 }
|
{ SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Data common to all chip drivers */
|
|
||||||
|
|
||||||
static constexpr int _FLASH_WRITE_TIMEOUT = 100000;
|
|
||||||
static constexpr int _FLASH_ERASE_TIMEOUT = 20000000;
|
|
||||||
|
|
||||||
const char *const DRIVER_ERROR_NAMES[]{
|
|
||||||
"NO_ERROR",
|
|
||||||
"UNSUPPORTED_OP",
|
|
||||||
"CHIP_TIMEOUT",
|
|
||||||
"CHIP_ERROR",
|
|
||||||
"VERIFY_MISMATCH",
|
|
||||||
"WRITE_PROTECTED"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const ChipSize _DUMMY_CHIP_SIZE{
|
|
||||||
.chipLength = 0,
|
|
||||||
.eraseSectorLength = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
|
|
||||||
// KB sectors and an 8-bit bus.
|
|
||||||
static const ChipSize _STANDARD_CHIP_SIZE{
|
|
||||||
.chipLength = 2 * 0x200000,
|
|
||||||
.eraseSectorLength = 2 * 0x10000
|
|
||||||
};
|
|
||||||
|
|
||||||
static const ChipSize _ALT_CHIP_SIZE{
|
|
||||||
.chipLength = 2 * 0x80000,
|
|
||||||
.eraseSectorLength = 2 * 0x10000
|
|
||||||
};
|
|
||||||
|
|
||||||
const ChipSize &Driver::getChipSize(void) const {
|
|
||||||
return _DUMMY_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RTC RAM driver */
|
|
||||||
|
|
||||||
static const ChipSize _RTC_CHIP_SIZE{
|
|
||||||
.chipLength = 0x1ff8,
|
|
||||||
.eraseSectorLength = 0x1ff8
|
|
||||||
};
|
|
||||||
|
|
||||||
void RTCDriver::write(uint32_t offset, uint16_t value) {
|
|
||||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
|
||||||
ptr[0] = value & 0xff;
|
|
||||||
ptr[1] = value >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTCDriver::eraseSector(uint32_t offset) {
|
|
||||||
auto ptr = reinterpret_cast<void *>(_region.ptr);
|
|
||||||
|
|
||||||
__builtin_memset(ptr, 0, _region.regionLength * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTCDriver::eraseChip(uint32_t offset) {
|
|
||||||
eraseSector(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
|
|
||||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
|
||||||
|
|
||||||
uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8);
|
|
||||||
|
|
||||||
if (value != actualValue) {
|
|
||||||
LOG(
|
|
||||||
"mismatch @ 0x%08x, exp=0x%02x, got=0x%04x", offset, value,
|
|
||||||
actualValue
|
|
||||||
);
|
|
||||||
return VERIFY_MISMATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError RTCDriver::flushErase(uint32_t offset) {
|
|
||||||
return flushWrite(offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChipSize &RTCDriver::getChipSize(void) const {
|
|
||||||
return _RTC_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */
|
|
||||||
|
|
||||||
enum JEDECStatusFlag : uint16_t {
|
|
||||||
_JEDEC_STATUS_ERASE_TOGGLE = 1 << 2,
|
|
||||||
_JEDEC_STATUS_ERASE_START = 1 << 3,
|
|
||||||
_JEDEC_STATUS_ERROR = 1 << 5,
|
|
||||||
_JEDEC_STATUS_TOGGLE = 1 << 6,
|
|
||||||
_JEDEC_STATUS_POLL_BIT = 1 << 7
|
|
||||||
};
|
|
||||||
|
|
||||||
DriverError AM29F016Driver::_flush(
|
|
||||||
uint32_t offset, uint16_t value, int timeout
|
|
||||||
) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
|
||||||
|
|
||||||
int shift = (offset & 1) * 8;
|
|
||||||
uint8_t byte = (value >> shift) & 0xff;
|
|
||||||
|
|
||||||
uint8_t status, diff;
|
|
||||||
|
|
||||||
for (; timeout > 0; timeout--) {
|
|
||||||
status = (*ptr >> shift) & 0xff;
|
|
||||||
diff = status ^ byte;
|
|
||||||
|
|
||||||
if (!(diff & _JEDEC_STATUS_POLL_BIT))
|
|
||||||
return NO_ERROR;
|
|
||||||
if (status & _JEDEC_STATUS_ERROR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the error flag was set, make sure an error actually occurred.
|
|
||||||
status = (*ptr >> shift) & 0xff;
|
|
||||||
diff = status ^ byte;
|
|
||||||
|
|
||||||
if (!(diff & _JEDEC_STATUS_POLL_BIT))
|
|
||||||
return NO_ERROR;
|
|
||||||
|
|
||||||
*ptr = _JEDEC_RESET;
|
|
||||||
|
|
||||||
if (status & _JEDEC_STATUS_ERROR) {
|
|
||||||
LOG("error @ 0x%08x, stat=0x%02x", offset, status);
|
|
||||||
return CHIP_ERROR;
|
|
||||||
} else {
|
|
||||||
LOG("timeout @ 0x%08x, stat=0x%02x", offset, status);
|
|
||||||
return CHIP_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AM29F016Driver::write(uint32_t offset, uint16_t value) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
|
||||||
|
|
||||||
ptr[0x000] = _JEDEC_RESET;
|
|
||||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x555] = _JEDEC_WRITE_BYTE;
|
|
||||||
ptr[offset] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AM29F016Driver::eraseSector(uint32_t offset) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
|
||||||
|
|
||||||
ptr[0x000] = _JEDEC_RESET;
|
|
||||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
|
|
||||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[offset] = _JEDEC_ERASE_SECTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AM29F016Driver::eraseChip(uint32_t offset) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
|
|
||||||
ptr[0x000] = _JEDEC_RESET;
|
|
||||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
|
|
||||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x555] = _JEDEC_ERASE_CHIP;
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError AM29F016Driver::flushWrite(
|
|
||||||
uint32_t offset, uint16_t value
|
|
||||||
) {
|
|
||||||
auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError AM29F016Driver::flushErase(uint32_t offset) {
|
|
||||||
auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChipSize &AM29F016Driver::getChipSize(void) const {
|
|
||||||
return _STANDARD_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* AMD AM29F040 (Fujitsu MBM29F040A) driver */
|
|
||||||
|
|
||||||
// Konami's drivers handle this chip pretty much identically to the MBM29F016A,
|
|
||||||
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa.
|
|
||||||
|
|
||||||
void AM29F040Driver::write(uint32_t offset, uint16_t value) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
|
||||||
|
|
||||||
ptr[0x0000] = _JEDEC_RESET;
|
|
||||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x5555] = _JEDEC_WRITE_BYTE;
|
|
||||||
ptr[offset] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AM29F040Driver::eraseSector(uint32_t offset) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
|
||||||
|
|
||||||
ptr[0x0000] = _JEDEC_RESET;
|
|
||||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
|
|
||||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[offset] = _JEDEC_ERASE_SECTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AM29F040Driver::eraseChip(uint32_t offset) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
|
||||||
|
|
||||||
ptr[0x0005] = _JEDEC_RESET;
|
|
||||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
|
|
||||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
|
||||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
|
||||||
ptr[0x5555] = _JEDEC_ERASE_CHIP;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChipSize &AM29F040Driver::getChipSize(void) const {
|
|
||||||
return _ALT_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Intel 28F016S5 (Sharp LH28F016S) driver */
|
|
||||||
|
|
||||||
enum IntelStatusFlag : uint16_t {
|
|
||||||
_INTEL_STATUS_DPS = 1 << 1,
|
|
||||||
_INTEL_STATUS_BWSS = 1 << 2,
|
|
||||||
_INTEL_STATUS_VPPS = 1 << 3,
|
|
||||||
_INTEL_STATUS_BWSLBS = 1 << 4,
|
|
||||||
_INTEL_STATUS_ECLBS = 1 << 5,
|
|
||||||
_INTEL_STATUS_ESS = 1 << 6,
|
|
||||||
_INTEL_STATUS_WSMS = 1 << 7
|
|
||||||
};
|
|
||||||
|
|
||||||
DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
|
||||||
|
|
||||||
int shift = (offset & 1) * 8;
|
|
||||||
uint8_t status = 0;
|
|
||||||
|
|
||||||
// Not required as all write/erase commands already put the chip into status
|
|
||||||
// reading mode.
|
|
||||||
//*ptr = _INTEL_GET_STATUS;
|
|
||||||
|
|
||||||
for (; timeout > 0; timeout--) {
|
|
||||||
status = (*ptr >> shift) & 0xff;
|
|
||||||
|
|
||||||
if (!(status & _INTEL_STATUS_WSMS))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
*ptr = _INTEL_RESET;
|
|
||||||
|
|
||||||
// The datasheet suggests only checking the error flags after WSMS = 1.
|
|
||||||
if (status & (_INTEL_STATUS_DPS | _INTEL_STATUS_VPPS)) {
|
|
||||||
*ptr = _INTEL_CLEAR_STATUS;
|
|
||||||
|
|
||||||
LOG("locked @ 0x%08x, stat=0x%02x", offset, status);
|
|
||||||
return WRITE_PROTECTED;
|
|
||||||
}
|
|
||||||
if (status & (_INTEL_STATUS_BWSLBS | _INTEL_STATUS_ECLBS)) {
|
|
||||||
*ptr = _INTEL_CLEAR_STATUS;
|
|
||||||
|
|
||||||
LOG("error @ 0x%08x, stat=0x%02x", offset, status);
|
|
||||||
return CHIP_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ptr = _INTEL_RESET;
|
|
||||||
|
|
||||||
LOG("timeout @ 0x%08x, stat=0x%02x", offset, status);
|
|
||||||
return CHIP_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
|
||||||
|
|
||||||
*ptr = _INTEL_RESET;
|
|
||||||
*ptr = _INTEL_CLEAR_STATUS;
|
|
||||||
*ptr = _INTEL_WRITE_BYTE;
|
|
||||||
*ptr = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Intel28F016S5Driver::eraseSector(uint32_t offset) {
|
|
||||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
|
||||||
|
|
||||||
*ptr = _INTEL_RESET;
|
|
||||||
*ptr = _INTEL_ERASE_SECTOR1;
|
|
||||||
*ptr = _INTEL_ERASE_SECTOR2;
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError Intel28F016S5Driver::flushWrite(
|
|
||||||
uint32_t offset, uint16_t value
|
|
||||||
) {
|
|
||||||
auto error = _flush(offset, _FLASH_WRITE_TIMEOUT);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
return _flush(offset + 1, _FLASH_WRITE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError Intel28F016S5Driver::flushErase(uint32_t offset) {
|
|
||||||
auto error = _flush(offset, _FLASH_ERASE_TIMEOUT);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
return _flush(offset + 1, _FLASH_ERASE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChipSize &Intel28F016S5Driver::getChipSize(void) const {
|
|
||||||
return _STANDARD_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Intel 28F640J5 driver */
|
|
||||||
|
|
||||||
static const ChipSize _28F640J5_CHIP_SIZE{
|
|
||||||
.chipLength = 0x800000,
|
|
||||||
.eraseSectorLength = 0x20000
|
|
||||||
};
|
|
||||||
|
|
||||||
DriverError Intel28F640J5Driver::flushWrite(
|
|
||||||
uint32_t offset, uint16_t value
|
|
||||||
) {
|
|
||||||
return _flush(offset, _FLASH_WRITE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverError Intel28F640J5Driver::flushErase(uint32_t offset) {
|
|
||||||
return _flush(offset, _FLASH_ERASE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChipSize &Intel28F640J5Driver::getChipSize(void) const {
|
|
||||||
return _28F640J5_CHIP_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BIOS ROM headers */
|
/* BIOS ROM headers */
|
||||||
|
|
||||||
static const ShellInfo _SHELL_VERSIONS[]{
|
static const ShellInfo _SHELL_VERSIONS[]{
|
||||||
|
@ -76,116 +76,6 @@ extern const BIOSRegion bios;
|
|||||||
extern const RTCRegion rtc;
|
extern const RTCRegion rtc;
|
||||||
extern const FlashRegion flash, pcmcia[2];
|
extern const FlashRegion flash, pcmcia[2];
|
||||||
|
|
||||||
/* Chip drivers */
|
|
||||||
|
|
||||||
enum DriverError {
|
|
||||||
NO_ERROR = 0,
|
|
||||||
UNSUPPORTED_OP = 1,
|
|
||||||
CHIP_TIMEOUT = 2,
|
|
||||||
CHIP_ERROR = 3,
|
|
||||||
VERIFY_MISMATCH = 4,
|
|
||||||
WRITE_PROTECTED = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ChipSize {
|
|
||||||
public:
|
|
||||||
size_t chipLength, eraseSectorLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Driver {
|
|
||||||
protected:
|
|
||||||
const Region &_region;
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline Driver(const Region ®ion)
|
|
||||||
: _region(region) {}
|
|
||||||
|
|
||||||
// Note that all offsets must be multiples of 2, as writes are done in
|
|
||||||
// halfwords.
|
|
||||||
virtual ~Driver(void) {}
|
|
||||||
virtual void write(uint32_t offset, uint16_t value) {}
|
|
||||||
virtual void eraseSector(uint32_t offset) {}
|
|
||||||
virtual void eraseChip(uint32_t offset) {}
|
|
||||||
virtual DriverError flushWrite(uint32_t offset, uint16_t value) {
|
|
||||||
return UNSUPPORTED_OP;
|
|
||||||
}
|
|
||||||
virtual DriverError flushErase(uint32_t offset) {
|
|
||||||
return UNSUPPORTED_OP;
|
|
||||||
}
|
|
||||||
virtual const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RTCDriver : public Driver {
|
|
||||||
public:
|
|
||||||
inline RTCDriver(const RTCRegion ®ion)
|
|
||||||
: Driver(region) {}
|
|
||||||
|
|
||||||
void write(uint32_t offset, uint16_t value);
|
|
||||||
void eraseSector(uint32_t offset);
|
|
||||||
void eraseChip(uint32_t offset);
|
|
||||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
|
||||||
DriverError flushErase(uint32_t offset);
|
|
||||||
const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AM29F016Driver : public Driver {
|
|
||||||
protected:
|
|
||||||
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline AM29F016Driver(const FlashRegion ®ion)
|
|
||||||
: Driver(region) {}
|
|
||||||
|
|
||||||
virtual void write(uint32_t offset, uint16_t value);
|
|
||||||
virtual void eraseSector(uint32_t offset);
|
|
||||||
virtual void eraseChip(uint32_t offset);
|
|
||||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
|
||||||
DriverError flushErase(uint32_t offset);
|
|
||||||
const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AM29F040Driver : public AM29F016Driver {
|
|
||||||
public:
|
|
||||||
inline AM29F040Driver(const FlashRegion ®ion)
|
|
||||||
: AM29F016Driver(region) {}
|
|
||||||
|
|
||||||
void write(uint32_t offset, uint16_t value);
|
|
||||||
void eraseSector(uint32_t offset);
|
|
||||||
void eraseChip(uint32_t offset);
|
|
||||||
const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Intel28F016S5Driver : public Driver {
|
|
||||||
protected:
|
|
||||||
DriverError _flush(uint32_t offset, int timeout);
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline Intel28F016S5Driver(const FlashRegion ®ion)
|
|
||||||
: Driver(region) {}
|
|
||||||
|
|
||||||
void write(uint32_t offset, uint16_t value);
|
|
||||||
void eraseSector(uint32_t offset);
|
|
||||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
|
||||||
DriverError flushErase(uint32_t offset);
|
|
||||||
const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Intel28F640J5Driver : public Intel28F016S5Driver {
|
|
||||||
public:
|
|
||||||
inline Intel28F640J5Driver(const FlashRegion ®ion)
|
|
||||||
: Intel28F016S5Driver(region) {}
|
|
||||||
|
|
||||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
|
||||||
DriverError flushErase(uint32_t offset);
|
|
||||||
const ChipSize &getChipSize(void) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const char *const DRIVER_ERROR_NAMES[];
|
|
||||||
|
|
||||||
static inline const char *getErrorString(DriverError error) {
|
|
||||||
return DRIVER_ERROR_NAMES[error];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BIOS ROM headers */
|
/* BIOS ROM headers */
|
||||||
|
|
||||||
struct [[gnu::packed]] SonyKernelHeader {
|
struct [[gnu::packed]] SonyKernelHeader {
|
||||||
|
336
src/common/romdrivers.cpp
Normal file
336
src/common/romdrivers.cpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "common/rom.hpp"
|
||||||
|
#include "common/romdrivers.hpp"
|
||||||
|
|
||||||
|
namespace rom {
|
||||||
|
|
||||||
|
/* Data common to all chip drivers */
|
||||||
|
|
||||||
|
static constexpr int _FLASH_WRITE_TIMEOUT = 10000000;
|
||||||
|
static constexpr int _FLASH_ERASE_TIMEOUT = 20000000;
|
||||||
|
|
||||||
|
const char *const DRIVER_ERROR_NAMES[]{
|
||||||
|
"NO_ERROR",
|
||||||
|
"UNSUPPORTED_OP",
|
||||||
|
"CHIP_TIMEOUT",
|
||||||
|
"CHIP_ERROR",
|
||||||
|
"VERIFY_MISMATCH",
|
||||||
|
"WRITE_PROTECTED"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ChipSize _DUMMY_CHIP_SIZE{
|
||||||
|
.chipLength = 0,
|
||||||
|
.eraseSectorLength = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
|
||||||
|
// KB sectors and an 8-bit bus.
|
||||||
|
static const ChipSize _STANDARD_CHIP_SIZE{
|
||||||
|
.chipLength = 2 * 0x200000,
|
||||||
|
.eraseSectorLength = 2 * 0x10000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ChipSize _ALT_CHIP_SIZE{
|
||||||
|
.chipLength = 2 * 0x80000,
|
||||||
|
.eraseSectorLength = 2 * 0x10000
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChipSize &Driver::getChipSize(void) const {
|
||||||
|
return _DUMMY_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RTC RAM driver */
|
||||||
|
|
||||||
|
static const ChipSize _RTC_CHIP_SIZE{
|
||||||
|
.chipLength = 0x1ff8,
|
||||||
|
.eraseSectorLength = 0x1ff8
|
||||||
|
};
|
||||||
|
|
||||||
|
void RTCDriver::write(uint32_t offset, uint16_t value) {
|
||||||
|
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||||
|
ptr[0] = value & 0xff;
|
||||||
|
ptr[1] = value >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTCDriver::eraseSector(uint32_t offset) {
|
||||||
|
auto ptr = reinterpret_cast<void *>(_region.ptr);
|
||||||
|
|
||||||
|
__builtin_memset(ptr, 0, _region.regionLength * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTCDriver::eraseChip(uint32_t offset) {
|
||||||
|
eraseSector(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
|
||||||
|
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||||
|
|
||||||
|
uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8);
|
||||||
|
|
||||||
|
if (value != actualValue) {
|
||||||
|
LOG("ptr=0x%06x, exp=0x%02x, got=0x%04x", offset, value, actualValue);
|
||||||
|
return VERIFY_MISMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError RTCDriver::flushErase(uint32_t offset) {
|
||||||
|
return flushWrite(offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChipSize &RTCDriver::getChipSize(void) const {
|
||||||
|
return _RTC_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */
|
||||||
|
|
||||||
|
DriverError AM29F016Driver::_flush(
|
||||||
|
uint32_t offset, uint16_t value, int timeout
|
||||||
|
) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||||
|
|
||||||
|
int shift = (offset & 1) * 8;
|
||||||
|
uint8_t byte = (value >> shift) & 0xff;
|
||||||
|
|
||||||
|
uint8_t status, diff;
|
||||||
|
|
||||||
|
for (; timeout > 0; timeout--) {
|
||||||
|
status = (*ptr >> shift) & 0xff;
|
||||||
|
diff = status ^ byte;
|
||||||
|
|
||||||
|
if (!(diff & JEDEC_STATUS_POLL_BIT))
|
||||||
|
return NO_ERROR;
|
||||||
|
if (status & JEDEC_STATUS_ERROR)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the error flag was set, make sure an error actually occurred.
|
||||||
|
status = (*ptr >> shift) & 0xff;
|
||||||
|
diff = status ^ byte;
|
||||||
|
|
||||||
|
if (!(diff & JEDEC_STATUS_POLL_BIT))
|
||||||
|
return NO_ERROR;
|
||||||
|
|
||||||
|
*ptr = JEDEC_RESET;
|
||||||
|
|
||||||
|
if (status & JEDEC_STATUS_ERROR) {
|
||||||
|
LOG("JEDEC error, ptr=0x%06x, st=0x%02x", offset, status);
|
||||||
|
return CHIP_ERROR;
|
||||||
|
} else {
|
||||||
|
LOG("JEDEC timeout, ptr=0x%06x, st=0x%02x", offset, status);
|
||||||
|
return CHIP_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM29F016Driver::write(uint32_t offset, uint16_t value) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||||
|
|
||||||
|
ptr[0x000] = JEDEC_RESET;
|
||||||
|
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x555] = JEDEC_WRITE_BYTE;
|
||||||
|
ptr[offset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM29F016Driver::eraseSector(uint32_t offset) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||||
|
|
||||||
|
ptr[0x000] = JEDEC_RESET;
|
||||||
|
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x555] = JEDEC_ERASE_HANDSHAKE;
|
||||||
|
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[offset] = JEDEC_ERASE_SECTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM29F016Driver::eraseChip(uint32_t offset) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
|
||||||
|
ptr[0x000] = JEDEC_RESET;
|
||||||
|
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x555] = JEDEC_ERASE_HANDSHAKE;
|
||||||
|
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x555] = JEDEC_ERASE_CHIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError AM29F016Driver::flushWrite(
|
||||||
|
uint32_t offset, uint16_t value
|
||||||
|
) {
|
||||||
|
auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError AM29F016Driver::flushErase(uint32_t offset) {
|
||||||
|
auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChipSize &AM29F016Driver::getChipSize(void) const {
|
||||||
|
return _STANDARD_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AMD AM29F040 (Fujitsu MBM29F040A) driver */
|
||||||
|
|
||||||
|
// Konami's drivers handle this chip pretty much identically to the MBM29F016A,
|
||||||
|
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa.
|
||||||
|
|
||||||
|
void AM29F040Driver::write(uint32_t offset, uint16_t value) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||||
|
|
||||||
|
ptr[0x0000] = JEDEC_RESET;
|
||||||
|
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x5555] = JEDEC_WRITE_BYTE;
|
||||||
|
ptr[offset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM29F040Driver::eraseSector(uint32_t offset) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||||
|
|
||||||
|
ptr[0x0000] = JEDEC_RESET;
|
||||||
|
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x5555] = JEDEC_ERASE_HANDSHAKE;
|
||||||
|
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[offset] = JEDEC_ERASE_SECTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM29F040Driver::eraseChip(uint32_t offset) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||||
|
|
||||||
|
ptr[0x0005] = JEDEC_RESET;
|
||||||
|
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x5555] = JEDEC_ERASE_HANDSHAKE;
|
||||||
|
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||||
|
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||||
|
ptr[0x5555] = JEDEC_ERASE_CHIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChipSize &AM29F040Driver::getChipSize(void) const {
|
||||||
|
return _ALT_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intel 28F016S5 (Sharp LH28F016S) driver */
|
||||||
|
|
||||||
|
DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||||
|
|
||||||
|
int shift = (offset & 1) * 8;
|
||||||
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
*ptr = INTEL_GET_STATUS;
|
||||||
|
|
||||||
|
for (; timeout > 0; timeout--) {
|
||||||
|
status = (*ptr >> shift) & 0xff;
|
||||||
|
|
||||||
|
if (!(status & INTEL_STATUS_WSMS))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*ptr = INTEL_RESET;
|
||||||
|
|
||||||
|
// The datasheet suggests only checking the error flags after WSMS = 1.
|
||||||
|
if (status & (INTEL_STATUS_DPS | INTEL_STATUS_VPPS)) {
|
||||||
|
*ptr = INTEL_CLEAR_STATUS;
|
||||||
|
|
||||||
|
LOG("Intel WP, ptr=0x%06x, st=0x%02x", offset, status);
|
||||||
|
return WRITE_PROTECTED;
|
||||||
|
}
|
||||||
|
if (status & (INTEL_STATUS_BWSLBS | INTEL_STATUS_ECLBS)) {
|
||||||
|
*ptr = INTEL_CLEAR_STATUS;
|
||||||
|
|
||||||
|
LOG("Intel error, ptr=0x%06x, st=0x%02x", offset, status);
|
||||||
|
return CHIP_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = INTEL_RESET;
|
||||||
|
|
||||||
|
LOG("Intel timeout, ptr=0x%06x, st=0x%02x", offset, status);
|
||||||
|
return CHIP_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||||
|
|
||||||
|
*ptr = INTEL_RESET;
|
||||||
|
*ptr = INTEL_CLEAR_STATUS;
|
||||||
|
*ptr = INTEL_WRITE_BYTE;
|
||||||
|
*ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Intel28F016S5Driver::eraseSector(uint32_t offset) {
|
||||||
|
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||||
|
|
||||||
|
*ptr = INTEL_RESET;
|
||||||
|
*ptr = INTEL_ERASE_SECTOR1;
|
||||||
|
*ptr = INTEL_ERASE_SECTOR2;
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError Intel28F016S5Driver::flushWrite(
|
||||||
|
uint32_t offset, uint16_t value
|
||||||
|
) {
|
||||||
|
auto error = _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return _flush(offset + 1, _FLASH_WRITE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError Intel28F016S5Driver::flushErase(uint32_t offset) {
|
||||||
|
auto error = _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return _flush(offset + 1, _FLASH_ERASE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChipSize &Intel28F016S5Driver::getChipSize(void) const {
|
||||||
|
return _STANDARD_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intel 28F640J5 driver */
|
||||||
|
|
||||||
|
static const ChipSize _28F640J5_CHIP_SIZE{
|
||||||
|
.chipLength = 0x800000,
|
||||||
|
.eraseSectorLength = 0x20000
|
||||||
|
};
|
||||||
|
|
||||||
|
DriverError Intel28F640J5Driver::flushWrite(
|
||||||
|
uint32_t offset, uint16_t value
|
||||||
|
) {
|
||||||
|
return _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverError Intel28F640J5Driver::flushErase(uint32_t offset) {
|
||||||
|
return _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChipSize &Intel28F640J5Driver::getChipSize(void) const {
|
||||||
|
return _28F640J5_CHIP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
161
src/common/romdrivers.hpp
Normal file
161
src/common/romdrivers.hpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "common/rom.hpp"
|
||||||
|
|
||||||
|
namespace rom {
|
||||||
|
|
||||||
|
/* Chip drivers */
|
||||||
|
|
||||||
|
enum JEDECCommand : uint16_t {
|
||||||
|
JEDEC_RESET = 0xf0f0,
|
||||||
|
JEDEC_HANDSHAKE1 = 0xaaaa,
|
||||||
|
JEDEC_HANDSHAKE2 = 0x5555,
|
||||||
|
JEDEC_GET_ID = 0x9090,
|
||||||
|
JEDEC_WRITE_BYTE = 0xa0a0,
|
||||||
|
JEDEC_ERASE_HANDSHAKE = 0x8080,
|
||||||
|
JEDEC_ERASE_CHIP = 0x1010,
|
||||||
|
JEDEC_ERASE_SECTOR = 0x3030
|
||||||
|
};
|
||||||
|
|
||||||
|
enum JEDECStatusFlag : uint16_t {
|
||||||
|
JEDEC_STATUS_ERASE_TOGGLE = 1 << 2,
|
||||||
|
JEDEC_STATUS_ERASE_START = 1 << 3,
|
||||||
|
JEDEC_STATUS_ERROR = 1 << 5,
|
||||||
|
JEDEC_STATUS_TOGGLE = 1 << 6,
|
||||||
|
JEDEC_STATUS_POLL_BIT = 1 << 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum IntelCommand : uint16_t {
|
||||||
|
INTEL_RESET = 0xffff,
|
||||||
|
INTEL_GET_ID = 0x9090,
|
||||||
|
INTEL_WRITE_BYTE = 0x4040,
|
||||||
|
INTEL_ERASE_SECTOR1 = 0x2020,
|
||||||
|
INTEL_ERASE_SECTOR2 = 0xd0d0,
|
||||||
|
INTEL_GET_STATUS = 0x7070,
|
||||||
|
INTEL_CLEAR_STATUS = 0x5050,
|
||||||
|
INTEL_SUSPEND = 0xb0b0,
|
||||||
|
INTEL_RESUME = 0xd0d0
|
||||||
|
};
|
||||||
|
|
||||||
|
enum IntelStatusFlag : uint16_t {
|
||||||
|
INTEL_STATUS_DPS = 1 << 1,
|
||||||
|
INTEL_STATUS_BWSS = 1 << 2,
|
||||||
|
INTEL_STATUS_VPPS = 1 << 3,
|
||||||
|
INTEL_STATUS_BWSLBS = 1 << 4,
|
||||||
|
INTEL_STATUS_ECLBS = 1 << 5,
|
||||||
|
INTEL_STATUS_ESS = 1 << 6,
|
||||||
|
INTEL_STATUS_WSMS = 1 << 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DriverError {
|
||||||
|
NO_ERROR = 0,
|
||||||
|
UNSUPPORTED_OP = 1,
|
||||||
|
CHIP_TIMEOUT = 2,
|
||||||
|
CHIP_ERROR = 3,
|
||||||
|
VERIFY_MISMATCH = 4,
|
||||||
|
WRITE_PROTECTED = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChipSize {
|
||||||
|
public:
|
||||||
|
size_t chipLength, eraseSectorLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Driver {
|
||||||
|
protected:
|
||||||
|
const Region &_region;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Driver(const Region ®ion)
|
||||||
|
: _region(region) {}
|
||||||
|
|
||||||
|
// Note that all offsets must be multiples of 2, as writes are done in
|
||||||
|
// halfwords.
|
||||||
|
virtual ~Driver(void) {}
|
||||||
|
virtual void write(uint32_t offset, uint16_t value) {}
|
||||||
|
virtual void eraseSector(uint32_t offset) {}
|
||||||
|
virtual void eraseChip(uint32_t offset) {}
|
||||||
|
virtual DriverError flushWrite(uint32_t offset, uint16_t value) {
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
}
|
||||||
|
virtual DriverError flushErase(uint32_t offset) {
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
}
|
||||||
|
virtual const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RTCDriver : public Driver {
|
||||||
|
public:
|
||||||
|
inline RTCDriver(const RTCRegion ®ion)
|
||||||
|
: Driver(region) {}
|
||||||
|
|
||||||
|
void write(uint32_t offset, uint16_t value);
|
||||||
|
void eraseSector(uint32_t offset);
|
||||||
|
void eraseChip(uint32_t offset);
|
||||||
|
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||||
|
DriverError flushErase(uint32_t offset);
|
||||||
|
const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AM29F016Driver : public Driver {
|
||||||
|
protected:
|
||||||
|
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline AM29F016Driver(const FlashRegion ®ion)
|
||||||
|
: Driver(region) {}
|
||||||
|
|
||||||
|
virtual void write(uint32_t offset, uint16_t value);
|
||||||
|
virtual void eraseSector(uint32_t offset);
|
||||||
|
virtual void eraseChip(uint32_t offset);
|
||||||
|
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||||
|
DriverError flushErase(uint32_t offset);
|
||||||
|
const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AM29F040Driver : public AM29F016Driver {
|
||||||
|
public:
|
||||||
|
inline AM29F040Driver(const FlashRegion ®ion)
|
||||||
|
: AM29F016Driver(region) {}
|
||||||
|
|
||||||
|
void write(uint32_t offset, uint16_t value);
|
||||||
|
void eraseSector(uint32_t offset);
|
||||||
|
void eraseChip(uint32_t offset);
|
||||||
|
const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Intel28F016S5Driver : public Driver {
|
||||||
|
protected:
|
||||||
|
DriverError _flush(uint32_t offset, int timeout);
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Intel28F016S5Driver(const FlashRegion ®ion)
|
||||||
|
: Driver(region) {}
|
||||||
|
|
||||||
|
void write(uint32_t offset, uint16_t value);
|
||||||
|
void eraseSector(uint32_t offset);
|
||||||
|
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||||
|
DriverError flushErase(uint32_t offset);
|
||||||
|
const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Intel28F640J5Driver : public Intel28F016S5Driver {
|
||||||
|
public:
|
||||||
|
inline Intel28F640J5Driver(const FlashRegion ®ion)
|
||||||
|
: Intel28F016S5Driver(region) {}
|
||||||
|
|
||||||
|
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||||
|
DriverError flushErase(uint32_t offset);
|
||||||
|
const ChipSize &getChipSize(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char *const DRIVER_ERROR_NAMES[];
|
||||||
|
|
||||||
|
static inline const char *getErrorString(DriverError error) {
|
||||||
|
return DRIVER_ERROR_NAMES[error];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
#include "common/filemisc.hpp"
|
#include "common/filemisc.hpp"
|
||||||
#include "common/filezip.hpp"
|
#include "common/filezip.hpp"
|
||||||
#include "common/gpu.hpp"
|
#include "common/gpu.hpp"
|
||||||
|
#include "common/ide.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/spu.hpp"
|
#include "common/spu.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
@ -65,7 +66,7 @@ WorkerStatusType WorkerStatus::setStatus(WorkerStatusType value) {
|
|||||||
/* Filesystem manager class */
|
/* Filesystem manager class */
|
||||||
|
|
||||||
FileIOManager::FileIOManager(void)
|
FileIOManager::FileIOManager(void)
|
||||||
: _resourceFile(nullptr) {
|
: _resourceFile(nullptr), resourcePtr(nullptr), resourceLength(0) {
|
||||||
__builtin_memset(ide, 0, sizeof(ide));
|
__builtin_memset(ide, 0, sizeof(ide));
|
||||||
|
|
||||||
vfs.mount("resource:", &resource);
|
vfs.mount("resource:", &resource);
|
||||||
@ -83,6 +84,9 @@ void FileIOManager::initIDE(void) {
|
|||||||
auto &dev = ide::devices[i];
|
auto &dev = ide::devices[i];
|
||||||
name[3] = i + '0';
|
name[3] = i + '0';
|
||||||
|
|
||||||
|
if (!(dev.flags & ide::DEVICE_READY))
|
||||||
|
continue;
|
||||||
|
|
||||||
// Note that calling vfs.mount() multiple times will *not* update any
|
// Note that calling vfs.mount() multiple times will *not* update any
|
||||||
// already mounted device, so if two hard drives or CD-ROMs are present
|
// already mounted device, so if two hard drives or CD-ROMs are present
|
||||||
// the hdd:/cdrom: prefix will be assigned to the first one.
|
// the hdd:/cdrom: prefix will be assigned to the first one.
|
||||||
@ -95,7 +99,7 @@ void FileIOManager::initIDE(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ide[i] = iso;
|
ide[i] = iso;
|
||||||
vfs.mount("cdrom:", iso, true);
|
vfs.mount("cdrom:", iso);
|
||||||
} else {
|
} else {
|
||||||
auto fat = new file::FATProvider();
|
auto fat = new file::FATProvider();
|
||||||
|
|
||||||
@ -105,7 +109,7 @@ void FileIOManager::initIDE(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ide[i] = fat;
|
ide[i] = fat;
|
||||||
vfs.mount("hdd:", fat, true);
|
vfs.mount("hdd:", fat);
|
||||||
}
|
}
|
||||||
|
|
||||||
vfs.mount(name, ide[i], true);
|
vfs.mount(name, ide[i], true);
|
||||||
@ -113,35 +117,44 @@ void FileIOManager::initIDE(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FileIOManager::closeIDE(void) {
|
void FileIOManager::closeIDE(void) {
|
||||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
char name[6]{ "ide#:" };
|
||||||
if (!ide[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ide[i]->close();
|
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||||
delete ide[i];
|
if (ide[i]) {
|
||||||
ide[i] = nullptr;
|
ide[i]->close();
|
||||||
|
delete ide[i];
|
||||||
|
ide[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
name[3] = i + '0';
|
||||||
|
vfs.unmount(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileIOManager::loadResourceFile(const char *path) {
|
bool FileIOManager::loadResourceFile(const char *path) {
|
||||||
closeResourceFile();
|
closeResourceFile();
|
||||||
|
|
||||||
_resourceFile = vfs.openFile(path, file::READ);
|
if (path)
|
||||||
|
_resourceFile = vfs.openFile(path, file::READ);
|
||||||
|
|
||||||
if (!_resourceFile)
|
// Fall back to the default in-memory resource archive in case of failure.
|
||||||
return false;
|
if (_resourceFile) {
|
||||||
|
if (resource.init(_resourceFile))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
resource.close();
|
resource.init(resourcePtr, resourceLength);
|
||||||
return resource.init(_resourceFile);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileIOManager::closeResourceFile(void) {
|
void FileIOManager::closeResourceFile(void) {
|
||||||
if (!_resourceFile)
|
resource.close();
|
||||||
return;
|
|
||||||
|
|
||||||
_resourceFile->close();
|
if (_resourceFile) {
|
||||||
delete _resourceFile;
|
_resourceFile->close();
|
||||||
_resourceFile = nullptr;
|
delete _resourceFile;
|
||||||
|
_resourceFile = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileIOManager::close(void) {
|
void FileIOManager::close(void) {
|
||||||
@ -320,9 +333,11 @@ void App::_interruptHandler(void) {
|
|||||||
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
|
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
|
||||||
LOG("(C) 2022-2024 spicyjpeg");
|
LOG("(C) 2022-2024 spicyjpeg");
|
||||||
|
|
||||||
_ctx.screenData = this;
|
_ctx.screenData = this;
|
||||||
|
_fileIO.resourcePtr = resourcePtr;
|
||||||
|
_fileIO.resourceLength = resourceLength;
|
||||||
|
|
||||||
_fileIO.resource.init(resourcePtr, resourceLength);
|
_fileIO.loadResourceFile(nullptr);
|
||||||
_loadResources();
|
_loadResources();
|
||||||
|
|
||||||
char dateString[24];
|
char dateString[24];
|
||||||
|
@ -54,6 +54,9 @@ private:
|
|||||||
file::File *_resourceFile;
|
file::File *_resourceFile;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
const void *resourcePtr;
|
||||||
|
size_t resourceLength;
|
||||||
|
|
||||||
file::Provider *ide[util::countOf(ide::devices)];
|
file::Provider *ide[util::countOf(ide::devices)];
|
||||||
file::ZIPProvider resource;
|
file::ZIPProvider resource;
|
||||||
#ifdef ENABLE_PCDRV
|
#ifdef ENABLE_PCDRV
|
||||||
@ -219,6 +222,7 @@ private:
|
|||||||
|
|
||||||
// miscworkers.cpp
|
// miscworkers.cpp
|
||||||
bool _ideInitWorker(void);
|
bool _ideInitWorker(void);
|
||||||
|
bool _fileInitWorker(void);
|
||||||
bool _executableWorker(void);
|
bool _executableWorker(void);
|
||||||
bool _atapiEjectWorker(void);
|
bool _atapiEjectWorker(void);
|
||||||
bool _rebootWorker(void);
|
bool _rebootWorker(void);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "common/defs.hpp"
|
#include "common/defs.hpp"
|
||||||
#include "common/file.hpp"
|
#include "common/file.hpp"
|
||||||
#include "common/ide.hpp"
|
#include "common/ide.hpp"
|
||||||
|
#include "common/idedefs.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "main/app/app.hpp"
|
#include "main/app/app.hpp"
|
||||||
@ -20,29 +21,37 @@ static const char *const _AUTOBOOT_PATHS[][2]{
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool App::_ideInitWorker(void) {
|
bool App::_ideInitWorker(void) {
|
||||||
_workerStatus.update(0, 4, WSTR("App.ideInitWorker.initDrives"));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||||
auto &dev = ide::devices[i];
|
auto &dev = ide::devices[i];
|
||||||
|
|
||||||
// Spin down all drives by default.
|
|
||||||
if (dev.flags & ide::DEVICE_READY)
|
if (dev.flags & ide::DEVICE_READY)
|
||||||
continue;
|
continue;
|
||||||
if (dev.enumerate())
|
|
||||||
dev.goIdle();
|
_workerStatus.update(
|
||||||
|
i, util::countOf(ide::devices), WSTR("App.ideInitWorker.init")
|
||||||
|
);
|
||||||
|
dev.enumerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
_workerStatus.update(1, 4, WSTR("App.ideInitWorker.initFileIO"));
|
return _fileInitWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool App::_fileInitWorker(void) {
|
||||||
|
_workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount"));
|
||||||
|
_fileIO.closeResourceFile();
|
||||||
|
_fileIO.close();
|
||||||
|
|
||||||
|
_workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount"));
|
||||||
_fileIO.initIDE();
|
_fileIO.initIDE();
|
||||||
|
|
||||||
_workerStatus.update(2, 4, WSTR("App.ideInitWorker.loadResources"));
|
_workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources"));
|
||||||
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
|
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
|
||||||
_loadResources();
|
_loadResources();
|
||||||
|
|
||||||
#ifdef ENABLE_AUTOBOOT
|
#ifdef ENABLE_AUTOBOOT
|
||||||
// Only try to autoboot if DIP switch 1 is on.
|
// Only try to autoboot if DIP switch 1 is on.
|
||||||
if (io::getDIPSwitch(0)) {
|
if (io::getDIPSwitch(0)) {
|
||||||
_workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot"));
|
_workerStatus.update(3, 4, WSTR("App.fileInitWorker.autoboot"));
|
||||||
|
|
||||||
for (auto path : _AUTOBOOT_PATHS) {
|
for (auto path : _AUTOBOOT_PATHS) {
|
||||||
file::FileInfo info;
|
file::FileInfo info;
|
||||||
@ -193,10 +202,8 @@ bool App::_atapiEjectWorker(void) {
|
|||||||
if (!(dev.flags & ide::DEVICE_ATAPI))
|
if (!(dev.flags & ide::DEVICE_ATAPI))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ide::Packet packet;
|
auto error =
|
||||||
|
ide::devices[0].startStopUnit(ide::START_STOP_MODE_OPEN_TRAY);
|
||||||
packet.setStartStopUnit(ide::START_STOP_MODE_OPEN_TRAY);
|
|
||||||
auto error = ide::devices[0].atapiPacket(packet);
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
_messageScreen.setMessage(
|
_messageScreen.setMessage(
|
||||||
|
@ -165,17 +165,15 @@ void FilePickerScreen::setMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FilePickerScreen::reloadAndShow(ui::Context &ctx) {
|
void FilePickerScreen::reloadAndShow(ui::Context &ctx) {
|
||||||
// Check if any disc has been changed and reload all filesystems if
|
// Check if any drive has reported a disc change and reload all filesystems
|
||||||
// necessary.
|
// if necessary.
|
||||||
for (auto &dev : ide::devices) {
|
for (auto &dev : ide::devices) {
|
||||||
if (!(dev.flags & ide::DEVICE_ATAPI))
|
if (!dev.poll())
|
||||||
continue;
|
|
||||||
if (!dev.atapiPoll())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
|
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
|
||||||
|
|
||||||
APP->_runWorker(&App::_ideInitWorker, *this, false, true);
|
APP->_runWorker(&App::_fileInitWorker, *this, false, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "common/defs.hpp"
|
#include "common/defs.hpp"
|
||||||
#include "common/file.hpp"
|
#include "common/file.hpp"
|
||||||
#include "common/rom.hpp"
|
#include "common/rom.hpp"
|
||||||
|
#include "common/romdrivers.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "main/app/app.hpp"
|
#include "main/app/app.hpp"
|
||||||
#include "main/app/romactions.hpp"
|
#include "main/app/romactions.hpp"
|
||||||
|
@ -235,8 +235,10 @@ void AudioTestScreen::update(ui::Context &ctx) {
|
|||||||
|
|
||||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||||
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||||
|
#if 0
|
||||||
io::setMiscOutput(io::MISC_AMP_ENABLE, false);
|
io::setMiscOutput(io::MISC_AMP_ENABLE, false);
|
||||||
io::setMiscOutput(io::MISC_CDDA_ENABLE, false);
|
io::setMiscOutput(io::MISC_CDDA_ENABLE, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
ctx.show(APP->_testMenuScreen, true, true);
|
ctx.show(APP->_testMenuScreen, true, true);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user