not64/main/ata.c
2024-11-20 16:43:57 -05:00

633 lines
No EOL
17 KiB
C

/**
* IDE EXI Driver for Gamecube & Wii
*
* Based loosely on code written by Dampro
* Re-written by emu_kidid
**/
#include <stdio.h>
#include <gccore.h> /*** Wrapper to include common libogc headers ***/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ogc/exi.h>
#include <ogc/machine/processor.h>
#include "ata.h"
#define IDE_EXI_V1 0
#define IDE_EXI_V2 1
#define IDE_EXI_V3 2
u16 buffer[256] ATTRIBUTE_ALIGN (32);
static int __ata_init[3] = {0,0,0};
static int _ideexi_version = IDE_EXI_V1;
// Drive information struct
typeDriveInfo ataDriveInfo;
// Returns 8 bits from the ATA Status register
static inline u8 ataReadStatusReg(int chn)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
// read ATA_REG_CMDSTATUS1 | 0x00 (dummy)
u16 dat = 0x1700;
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,2,EXI_WRITE);
EXI_ImmEx(chn,&dat,1,EXI_READ);
EXI_Deselect(chn);
EXI_Unlock(chn);
return *(u8*)&dat;
}
// Returns 8 bits from the ATA Error register
static inline u8 ataReadErrorReg(int chn)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
// read ATA_REG_ERROR | 0x00 (dummy)
u16 dat = 0x1100;
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,2,EXI_WRITE);
EXI_ImmEx(chn,&dat,1,EXI_READ);
EXI_Deselect(chn);
EXI_Unlock(chn);
return *(u8*)&dat;
}
// Writes 8 bits of data out to the specified ATA Register
static inline void ataWriteByte(int chn, u8 addr, u8 data)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
u32 dat = 0x80000000 | (addr << 24) | (data<<16);
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,3,EXI_WRITE);
EXI_Deselect(chn);
EXI_Unlock(chn);
}
// Writes 16 bits to the ATA Data register
static inline void ataWriteu16(int chn, u16 data)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
// write 16 bit to ATA_REG_DATA | data LSB | data MSB | 0x00 (dummy)
u32 dat = 0xD0000000 | (((data>>8) & 0xff)<<16) | ((data & 0xff)<<8);
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,4,EXI_WRITE);
EXI_Deselect(chn);
EXI_Unlock(chn);
}
// Returns 16 bits from the ATA Data register
static inline u16 ataReadu16(int chn)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
// read 16 bit from ATA_REG_DATA | 0x00 (dummy)
u16 dat = 0x5000;
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,2,EXI_WRITE);
EXI_ImmEx(chn,&dat,2,EXI_READ); // read LSB & MSB
EXI_Deselect(chn);
EXI_Unlock(chn);
return dat;
}
// Reads 512 bytes
static inline void ata_read_buffer(int chn, u32 *dst)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
u16 dwords = 128; // 128 * 4 = 512 bytes
// (31:29) 011b | (28:24) 10000b | (23:16) <num_words_LSB> | (15:8) <num_words_MSB> | (7:0) 00h (4 bytes)
u32 dat = 0x70000000 | ((dwords&0xff) << 16) | (((dwords>>8)&0xff) << 8);
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,4,EXI_WRITE);
if(_ideexi_version == IDE_EXI_V1) {
// IDE_EXI_V1, select / deselect for every 4 bytes
EXI_Deselect(chn);
EXI_Unlock(chn);
u32 i = 0;
u32 *ptr = dst;
for(i = 0; i < dwords; i++) {
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,ptr,4,EXI_READ);
ptr++;
EXI_Deselect(chn);
EXI_Unlock(chn);
}
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,4,EXI_READ);
EXI_Deselect(chn);
EXI_Unlock(chn);
}
else {
// IDE_EXI_V2, no need to select / deselect all the time
EXI_DmaEx(chn,dst,512,EXI_READ);
EXI_Deselect(chn);
EXI_Unlock(chn);
}
}
static inline void ata_write_buffer(int chn, u32 *src)
{
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
u16 dwords = 128; // 128 * 4 = 512 bytes
// (23:21) 111b | (20:16) 10000b | (15:8) <num_words_LSB> | (7:0) <num_words_MSB> (3 bytes)
u32 dat = 0xF0000000 | ((dwords&0xff) << 16) | (((dwords>>8)&0xff) << 8);
EXI_Lock(chn, dev, NULL);
EXI_Select(chn,dev,EXI_SPEED32MHZ);
EXI_ImmEx(chn,&dat,3,EXI_WRITE);
EXI_DmaEx(chn, src,512,EXI_WRITE);
dat = 0;
EXI_ImmEx(chn,&dat,1,EXI_WRITE); // Burn an extra cycle for the IDE-EXI to know to stop serving data
EXI_Deselect(chn);
EXI_Unlock(chn);
}
int _ideExiVersion(int chn) {
int dev = EXI_DEVICE_0;
if(chn == EXI_CHANNEL_2) {
chn = EXI_CHANNEL_0;
dev = EXI_DEVICE_2;
}
u32 cid = 0;
EXI_GetID(chn,dev,&cid);
if((cid&~0xff)==0x49444500) {
return (cid&0xff)-'1';
}
else {
return IDE_EXI_V1;
}
}
// Sends the IDENTIFY command to the HDD
// Returns 0 on success, -1 otherwise
u32 _ataDriveIdentify(int chn) {
u16 tmp,retries = 50;
u32 i = 0;
memset(&ataDriveInfo, 0, sizeof(typeDriveInfo));
// Get the ID to see if it's a V2+
_ideexi_version = _ideExiVersion(chn);
if(_ideexi_version == IDE_EXI_V1 && chn == EXI_CHANNEL_2) {
return -1;
}
// Select the device
ataWriteByte(chn, ATA_REG_DEVICE, 0);
// Wait for drive to be ready (BSY to clear) - 5 sec timeout
do {
tmp = ataReadStatusReg(chn);
usleep(100000); //sleep for 0.1 seconds
retries--;
}
while((tmp & ATA_SR_BSY) && retries);
if(!retries) {
return -1;
}
// Write the identify command
ataWriteByte(chn, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
// Wait for drive to request data transfer - 1 sec timeout
retries = 10;
do {
tmp = ataReadStatusReg(chn);
usleep(100000); //sleep for 0.1 seconds
retries--;
}
while((!(tmp & ATA_SR_DRQ)) && retries);
if(!retries) {
return -1;
}
usleep(2000);
u16 *ptr = (u16*)(&buffer[0]);
// Read Identify data from drive
for (i=0; i<256; i++) {
tmp = ataReadu16(chn); // get data
*ptr++ = bswap16(tmp); // swap
}
// Get the info out of the Identify data buffer
// From the command set, check if LBA48 is supported
u16 commandSet = *(u16*)(&buffer[ATA_IDENT_COMMANDSET]);
ataDriveInfo.lba48Support = (commandSet>>8) & ATA_IDENT_LBA48MASK;
if(ataDriveInfo.lba48Support) {
u16 lbaHi = *(u16*) (&buffer[ATA_IDENT_LBA48SECTORS+2]);
u16 lbaMid = *(u16*) (&buffer[ATA_IDENT_LBA48SECTORS+1]);
u16 lbaLo = *(u16*) (&buffer[ATA_IDENT_LBA48SECTORS]);
ataDriveInfo.sizeInSectors = (u64)(((u64)lbaHi << 32) | (lbaMid << 16) | lbaLo);
}
else {
ataDriveInfo.sizeInSectors = ((*(u16*) &buffer[ATA_IDENT_LBASECTORS+1])<<16) |
(*(u16*) &buffer[ATA_IDENT_LBASECTORS]);
}
i = 20;
// copy serial string
memcpy(&ataDriveInfo.serial[0], &buffer[ATA_IDENT_SERIAL],20);
// cut off the string (usually has trailing spaces)
while((ataDriveInfo.serial[i] == ' ' || !ataDriveInfo.serial[i]) && i >=0) {
ataDriveInfo.serial[i] = 0;
i--;
}
// copy model string
memcpy(&ataDriveInfo.model[0], &buffer[ATA_IDENT_MODEL],40);
// cut off the string (usually has trailing spaces)
i = 40;
while((ataDriveInfo.model[i] == ' ' || !ataDriveInfo.model[i]) && i >=0) {
ataDriveInfo.model[i] = 0;
i--;
}
// Return ok
return 0;
}
// Unlocks a ATA HDD with a password
// Returns 0 on success, -1 on failure.
int ataUnlock(int chn, int useMaster, char *password)
{
u32 i;
// Wait for drive to be ready (BSY to clear)
while(ataReadStatusReg(chn) & ATA_SR_BSY);
// Select the device
ataWriteByte(chn, ATA_REG_DEVICE, 0);
// Write the appropriate unlock command
ataWriteByte(chn, ATA_REG_COMMAND, ATA_CMD_UNLOCK);
while(!(ataReadStatusReg(chn) & ATA_SR_DRQ));
// Fill an unlock struct
unlockStruct unlock;
memset(&unlock, 0, sizeof(unlockStruct));
unlock.type = useMaster;
memcpy(unlock.password, password, strlen(password));
// write data to the drive
u16 *ptr = (u16*)&unlock;
for (i=0; i<256; i++) {
ataWriteu16(chn, ptr[i]);
}
// Wait for the write
while(ataReadStatusReg(chn) & ATA_SR_BSY);
return !(ataReadErrorReg(chn) & ATA_ER_ABRT);
}
// Reads sectors from the specified lba, for the specified slot
// Returns 0 on success, -1 on failure.
int _ataReadSector(int chn, u64 lba, u32 *Buffer)
{
u32 temp = 0;
// Wait for drive to be ready (BSY to clear)
while(ataReadStatusReg(chn) & ATA_SR_BSY);
// Select the device differently based on 28 or 48bit mode
if(ataDriveInfo.lba48Support) {
// Select the device (ATA_HEAD_USE_LBA is 0x40 for master, 0x50 for slave)
ataWriteByte(chn, ATA_REG_DEVICE, ATA_HEAD_USE_LBA);
}
else {
// Select the device (ATA_HEAD_USE_LBA is 0x40 for master, 0x50 for slave)
ataWriteByte(chn, ATA_REG_DEVICE, 0xE0 | (u8)((lba >> 24) & 0x0F));
}
// check if drive supports LBA 48-bit
if(ataDriveInfo.lba48Support) {
ataWriteByte(chn, ATA_REG_SECCOUNT, 0); // Sector count (Hi)
ataWriteByte(chn, ATA_REG_LBALO, (u8)((lba>>24)& 0xFF)); // LBA 4
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>32) & 0xFF)); // LBA 5
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>40) & 0xFF)); // LBA 6
ataWriteByte(chn, ATA_REG_SECCOUNT, 1); // Sector count (Lo)
ataWriteByte(chn, ATA_REG_LBALO, (u8)(lba & 0xFF)); // LBA 1
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>8) & 0xFF)); // LBA 2
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>16) & 0xFF)); // LBA 3
}
else {
ataWriteByte(chn, ATA_REG_SECCOUNT, 1); // Sector count
ataWriteByte(chn, ATA_REG_LBALO, (u8)(lba & 0xFF)); // LBA Lo
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>8) & 0xFF)); // LBA Mid
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>16) & 0xFF)); // LBA Hi
}
// Write the appropriate read command
ataWriteByte(chn, ATA_REG_COMMAND, ataDriveInfo.lba48Support ? ATA_CMD_READSECTEXT : ATA_CMD_READSECT);
// Wait for BSY to clear
while((temp = ataReadStatusReg(chn)) & ATA_SR_BSY);
// If the error bit was set, fail.
if(temp & ATA_SR_ERR) {
return 1;
}
// Wait for drive to request data transfer
while(!(ataReadStatusReg(chn) & ATA_SR_DRQ));
// read data from drive
ata_read_buffer(chn, Buffer);
temp = ataReadStatusReg(chn);
// If the error bit was set, fail.
if(temp & ATA_SR_ERR) {
return 1;
}
return temp & ATA_SR_ERR;
}
// Writes sectors to the specified lba, for the specified slot
// Returns 0 on success, -1 on failure.
int _ataWriteSector(int chn, u64 lba, u32 *Buffer)
{
u32 i, temp;
// Wait for drive to be ready (BSY to clear)
while(ataReadStatusReg(chn) & ATA_SR_BSY);
// Select the device differently based on 28 or 48bit mode
if(ataDriveInfo.lba48Support) {
// Select the device (ATA_HEAD_USE_LBA is 0x40 for master, 0x50 for slave)
ataWriteByte(chn, ATA_REG_DEVICE, ATA_HEAD_USE_LBA);
}
else {
// Select the device (ATA_HEAD_USE_LBA is 0x40 for master, 0x50 for slave)
ataWriteByte(chn, ATA_REG_DEVICE, 0xE0 | (u8)((lba >> 24) & 0x0F));
}
// check if drive supports LBA 48-bit
if(ataDriveInfo.lba48Support) {
ataWriteByte(chn, ATA_REG_SECCOUNT, 0); // Sector count (Hi)
ataWriteByte(chn, ATA_REG_LBALO, (u8)((lba>>24)& 0xFF)); // LBA 4
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>32) & 0xFF)); // LBA 4
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>40) & 0xFF)); // LBA 5
ataWriteByte(chn, ATA_REG_SECCOUNT, 1); // Sector count (Lo)
ataWriteByte(chn, ATA_REG_LBALO, (u8)(lba & 0xFF)); // LBA 1
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>8) & 0xFF)); // LBA 2
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>16) & 0xFF)); // LBA 3
}
else {
ataWriteByte(chn, ATA_REG_SECCOUNT, 1); // Sector count
ataWriteByte(chn, ATA_REG_LBALO, (u8)(lba & 0xFF)); // LBA Lo
ataWriteByte(chn, ATA_REG_LBAMID, (u8)((lba>>8) & 0xFF)); // LBA Mid
ataWriteByte(chn, ATA_REG_LBAHI, (u8)((lba>>16) & 0xFF)); // LBA Hi
}
// Write the appropriate write command
ataWriteByte(chn, ATA_REG_COMMAND, ataDriveInfo.lba48Support ? ATA_CMD_WRITESECTEXT : ATA_CMD_WRITESECT);
// Wait for BSY to clear
while((temp = ataReadStatusReg(chn)) & ATA_SR_BSY);
// If the error bit was set, fail.
if(temp & ATA_SR_ERR) {
return 1;
}
// Wait for drive to request data transfer
while(!(ataReadStatusReg(chn) & ATA_SR_DRQ));
// Write data to the drive
if(_ideexi_version < IDE_EXI_V3) {
u16 *ptr = (u16*)Buffer;
for (i=0; i<256; i++) {
ataWriteu16(chn, ptr[i]);
}
}
else {
ata_write_buffer(chn, Buffer);
}
// Wait for the write to finish
while(ataReadStatusReg(chn) & ATA_SR_BSY);
temp = ataReadStatusReg(chn);
// If the error bit was set, fail.
if(temp & ATA_SR_ERR) {
return 1;
}
return temp & ATA_SR_ERR;
}
// Wrapper to read a number of sectors
// 0 on Success, -1 on Error
int ataReadSectors(int chn, u64 sector, unsigned int numSectors, unsigned char *dest)
{
int ret = 0;
while(numSectors) {
if((ret=_ataReadSector(chn,sector,(u32*)dest))) {
return -1;
}
dest+=512;
sector++;
numSectors--;
}
return 0;
}
// Wrapper to write a number of sectors
// 0 on Success, -1 on Error
int ataWriteSectors(int chn, u64 sector,unsigned int numSectors, unsigned char *src)
{
int ret = 0;
while(numSectors) {
if((ret=_ataWriteSector(chn,sector,(u32*)src))) {
return -1;
}
src+=512;
sector++;
numSectors--;
}
return 0;
}
// Is an ATA device inserted?
bool ataIsInserted(int chn) {
if(__ata_init[chn]) {
return true;
}
if(_ataDriveIdentify(chn)) {
return false;
}
__ata_init[chn] = 1;
return true;
}
int ataShutdown(int chn) {
__ata_init[chn] = 0;
return 1;
}
static bool __ataa_startup(DISC_INTERFACE *disc)
{
return ataIsInserted(0);
}
static bool __ataa_isInserted(DISC_INTERFACE *disc)
{
return ataIsInserted(0);
}
static bool __ataa_readSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataReadSectors(0, (u64)sector, numSectors, buffer);
}
static bool __ataa_writeSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataWriteSectors(0, (u64)sector, numSectors, buffer);
}
static bool __ataa_clearStatus(DISC_INTERFACE *disc)
{
return true;
}
static bool __ataa_shutdown(DISC_INTERFACE *disc)
{
return true;
}
static bool __atab_startup(DISC_INTERFACE *disc)
{
return ataIsInserted(1);
}
static bool __atab_isInserted(DISC_INTERFACE *disc)
{
return ataIsInserted(1);
}
static bool __atab_readSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataReadSectors(1, (u64)sector, numSectors, buffer);
}
static bool __atab_writeSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataWriteSectors(1, (u64)sector, numSectors, buffer);
}
static bool __atab_clearStatus(DISC_INTERFACE *disc)
{
return true;
}
static bool __atab_shutdown(DISC_INTERFACE *disc)
{
return true;
}
static bool __ata1_startup(DISC_INTERFACE *disc)
{
return ataIsInserted(2);
}
static bool __ata1_isInserted(DISC_INTERFACE *disc)
{
return ataIsInserted(2);
}
static bool __ata1_readSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataReadSectors(2, (u64)sector, numSectors, buffer);
}
static bool __ata1_writeSectors(DISC_INTERFACE *disc, sec_t sector, sec_t numSectors, void *buffer)
{
return !ataWriteSectors(2, (u64)sector, numSectors, buffer);
}
static bool __ata1_clearStatus(DISC_INTERFACE *disc)
{
return true;
}
static bool __ata1_shutdown(DISC_INTERFACE *disc)
{
return true;
}
DISC_INTERFACE __io_ataa = {
DEVICE_TYPE_GC_ATA,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_GAMECUBE_SLOTA,
(FN_MEDIUM_STARTUP)&__ataa_startup,
(FN_MEDIUM_ISINSERTED)&__ataa_isInserted,
(FN_MEDIUM_READSECTORS)&__ataa_readSectors,
(FN_MEDIUM_WRITESECTORS)&__ataa_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&__ataa_clearStatus,
(FN_MEDIUM_SHUTDOWN)&__ataa_shutdown,
0x1000000000000,
512
} ;
DISC_INTERFACE __io_atab = {
DEVICE_TYPE_GC_ATA,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_GAMECUBE_SLOTB,
(FN_MEDIUM_STARTUP)&__atab_startup,
(FN_MEDIUM_ISINSERTED)&__atab_isInserted,
(FN_MEDIUM_READSECTORS)&__atab_readSectors,
(FN_MEDIUM_WRITESECTORS)&__atab_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&__atab_clearStatus,
(FN_MEDIUM_SHUTDOWN)&__atab_shutdown,
0x1000000000000,
512
} ;
DISC_INTERFACE __io_ata1 = {
DEVICE_TYPE_GC_ATA,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_GAMECUBE_PORT1,
(FN_MEDIUM_STARTUP)&__ata1_startup,
(FN_MEDIUM_ISINSERTED)&__ata1_isInserted,
(FN_MEDIUM_READSECTORS)&__ata1_readSectors,
(FN_MEDIUM_WRITESECTORS)&__ata1_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&__ata1_clearStatus,
(FN_MEDIUM_SHUTDOWN)&__ata1_shutdown,
0x1000000000000,
512
} ;