RetroArch/wii/libogc/libogc/sdgecko_io.c

1357 lines
29 KiB
C

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <ogcsys.h>
#include "asm.h"
#include "processor.h"
#include "exi.h"
#include "lwp.h"
#include "system.h"
#include "semaphore.h"
#include "card_cmn.h"
//#include "card_fat.h"
#include "card_io.h"
// SDHC support
// added by emu_kidid
#define SECTOR_ADDRESSING 0
#define BYTE_ADDRESSING 1
#define TYPE_SD 0
#define TYPE_SDHC 1
#define MMC_ERROR_PARAM 0x0040
#define MMC_ERROR_ADDRESS 0x0020
#define MMC_ERROR_ERASE_SEQ 0x0010
#define MMC_ERROR_CRC 0x0008
#define MMC_ERROR_ILL 0x0004
#define MMC_ERROR_ERASE_RES 0x0002
#define MMC_ERROR_IDLE 0x0001
#define CARDIO_OP_INITFAILED 0x8000
#define CARDIO_OP_TIMEDOUT 0x4000
#define CARDIO_OP_IOERR_IDLE 0x2000
#define CARDIO_OP_IOERR_PARAM 0x1000
#define CARDIO_OP_IOERR_WRITE 0x0200
#define CARDIO_OP_IOERR_ADDR 0x0100
#define CARDIO_OP_IOERR_CRC 0x0002
#define CARDIO_OP_IOERR_ILL 0x0001
#define CARDIO_OP_IOERR_FATAL (CARDIO_OP_IOERR_PARAM|CARDIO_OP_IOERR_WRITE|CARDIO_OP_IOERR_ADDR|CARDIO_OP_IOERR_CRC|CARDIO_OP_IOERR_ILL)
#define _SHIFTL(v, s, w) \
((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s)))
#define _SHIFTR(v, s, w) \
((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1)))
typedef s32 (*cardiocallback)(s32 drv_no);
u8 g_CID[MAX_DRIVE][16];
u8 g_CSD[MAX_DRIVE][16];
u8 g_CardStatus[MAX_DRIVE][64];
u8 g_mCode[MAX_MI_NUM] = { 0x03 };
u16 g_dCode[MAX_MI_NUM][MAX_DI_NUM] =
{
{
0x033f, /* SD 8Mb */
0x0383, /* SD 16Mb */
0x074b, /* SD 32Mb */
0x0edf, /* SD 64Mb */
0x0f03 /* SD 128Mb */
}
};
static u8 _ioWPFlag;
static u8 _ioClrFlag;
static u32 _ioCardFreq;
static u32 _ioRetryCnt;
static cardiocallback _ioRetryCB = NULL;
static lwpq_t _ioEXILock[MAX_DRIVE];
static u32 _ioPageSize[MAX_DRIVE];
static u32 _ioFlag[MAX_DRIVE];
static u32 _ioError[MAX_DRIVE];
static bool _ioCardInserted[MAX_DRIVE];
static u8 _ioResponse[MAX_DRIVE][128];
static u8 _ioCrc7Table[256];
static u16 _ioCrc16Table[256];
// SDHC support
static u32 _initType[MAX_DRIVE];
static u32 _ioAddressingType[MAX_DRIVE];
extern unsigned long gettick();
static __inline__ u32 __check_response(s32 drv_no,u8 res)
{
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
_ioError[drv_no] = 0;
if(_ioFlag[drv_no]==INITIALIZING && res&MMC_ERROR_IDLE) {
_ioError[drv_no] |= CARDIO_OP_IOERR_IDLE;
return CARDIO_ERROR_READY;
} else {
if(res&MMC_ERROR_PARAM) _ioError[drv_no] |= CARDIO_OP_IOERR_PARAM;
if(res&MMC_ERROR_ADDRESS) _ioError[drv_no] |= CARDIO_OP_IOERR_ADDR;
if(res&MMC_ERROR_CRC) _ioError[drv_no] |= CARDIO_OP_IOERR_CRC;
if(res&MMC_ERROR_ILL) _ioError[drv_no] |= CARDIO_OP_IOERR_ILL;
}
return ((_ioError[drv_no]&CARDIO_OP_IOERR_FATAL)?CARDIO_ERROR_INTERNAL:CARDIO_ERROR_READY);
}
static void __init_crc7()
{
s32 i,j;
u8 c,crc7;
crc7 = 0;
for(i=0;i<256;i++) {
c = i;
crc7 = 0;
for(j=0;j<8;j++) {
crc7 <<= 1;
if((crc7^c)&0x80) crc7 ^= 0x09;
c <<= 1;
}
crc7 &= 0x7f;
_ioCrc7Table[i] = crc7;
}
}
static u8 __make_crc7(void *buffer,u32 len)
{
s32 i;
u8 crc7;
u8 *ptr;
crc7 = 0;
ptr = buffer;
for(i=0;i<len;i++)
crc7 = _ioCrc7Table[(u8)((crc7<<1)^ptr[i])];
return ((crc7<<1)|1);
}
/* Old way, realtime
static u8 __make_crc7(void *buffer,u32 len)
{
u32 mask,cnt,bcnt;
u32 res,val;
u8 *ptr = (u8*)buffer;
cnt = 0;
res = 0;
while(cnt<len) {
bcnt = 0;
mask = 0x80;
while(bcnt<8) {
res <<= 1;
val = *ptr^((res>>bcnt)&0xff);
if(mask&val) {
res |= 0x01;
if(!(res&0x0008)) res |= 0x0008;
else res &= ~0x0008;
} else if(res&0x0008) res |= 0x0008;
else res &= ~0x0008;
mask >>= 1;
bcnt++;
}
ptr++;
cnt++;
}
return (res<<1)&0xff;
}
*/
static void __init_crc16()
{
s32 i,j;
u16 crc16,c;
for(i=0;i<256;i++) {
crc16 = 0;
c = ((u16)i)<<8;
for(j=0;j<8;j++) {
if((crc16^c)&0x8000) crc16 = (crc16<<1)^0x1021;
else crc16 <<= 1;
c <<= 1;
}
_ioCrc16Table[i] = crc16;
}
}
static u16 __make_crc16(void *buffer,u32 len)
{
s32 i;
u8 *ptr;
u16 crc16;
crc16 = 0;
ptr = buffer;
for(i=0;i<len;i++)
crc16 = (crc16<<8)^_ioCrc16Table[((crc16>>8)^(u16)(ptr[i]))];
return crc16;
}
/* Old way, realtime
static u16 __make_crc16(void *buffer,u32 len)
{
u32 mask,cnt,bcnt;
u32 res,val,tmp;
u8 *ptr = (u8*)buffer;
cnt = 0;
res = 0;
while(cnt<len) {
bcnt = 0;
mask = 0x80;
val = *ptr;
while(bcnt<8) {
tmp = val^((res>>(bcnt+8))&0xff);
if(mask&tmp) {
res = (res<<1)|0x0001;
if(!(res&0x0020)) res |= 0x0020;
else res &= ~0x0020;
if(!(res&0x1000)) res |= 0x1000;
else res &= ~0x1000;
} else {
res = (res<<1)&~0x0001;
if(res&0x0020) res |= 0x0020;
else res &= ~0x0020;
if(res&0x1000) res |= 0x1000;
else res &= ~0x1000;
}
mask >>= 1;
bcnt++;
}
ptr++;
cnt++;
}
return (res&0xffff);
}
*/
static u32 __card_checktimeout(s32 drv_no,u32 startT,u32 timeout)
{
u32 endT,diff;
u32 msec;
endT = gettick();
if(endT<startT) {
diff = (endT+(-1-startT))+1;
} else
diff = (endT-startT);
msec = (diff/TB_TIMER_CLOCK);
if(msec<=timeout) return 0;
_ioError[drv_no] |= CARDIO_OP_TIMEDOUT;
return 1;
}
static s32 __exi_unlock(s32 chn,s32 dev)
{
LWP_ThreadBroadcast(_ioEXILock[chn]);
return 1;
}
static void __exi_wait(s32 drv_no)
{
u32 ret;
do {
if((ret=EXI_Lock(drv_no,EXI_DEVICE_0,__exi_unlock))==1) break;
LWP_ThreadSleep(_ioEXILock[drv_no]);
} while(ret==0);
}
static s32 __card_exthandler(s32 chn,s32 dev)
{
_ioCardInserted[chn] = FALSE;
sdgecko_doUnmount(chn);
sdgecko_ejectedCB(chn);
EXI_Unlock(chn);
return 1;
}
static s32 __card_writecmd0(s32 drv_no)
{
u8 crc;
u32 cnt;
u8 dummy[128];
u8 cmd[5] = {0,0,0,0,0};
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
_ioClrFlag = 0xff;
cmd[0] = 0x40;
crc = __make_crc7(cmd,5);
if(_ioWPFlag) {
_ioClrFlag = 0x00;
for(cnt=0;cnt<5;cnt++) cmd[cnt] ^= -1;
}
for(cnt=0;cnt<128;cnt++) dummy[cnt] = _ioClrFlag;
__exi_wait(drv_no);
if(EXI_SelectSD(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
cnt = 0;
while(cnt<20) {
if(EXI_ImmEx(drv_no,dummy,128,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
cnt++;
}
EXI_Deselect(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
crc |= 0x01;
if(_ioWPFlag) crc ^= -1;
if(EXI_ImmEx(drv_no,cmd,5,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(EXI_ImmEx(drv_no,&crc,1,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_READY;
}
static s32 __card_writecmd(s32 drv_no,void *buf,s32 len)
{
u8 crc,*ptr;
u8 dummy[32];
u32 cnt;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ptr = buf;
ptr[0] |= 0x40;
crc = __make_crc7(buf,len);
if(_ioWPFlag) {
for(cnt=0;cnt<len;cnt++) ptr[cnt] ^= -1;
}
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
for(cnt=0;cnt<32;cnt++) dummy[cnt] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,10,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
crc |= 0x01;
if(_ioWPFlag) crc ^= -1;
if(EXI_ImmEx(drv_no,buf,len,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(EXI_ImmEx(drv_no,&crc,1,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_READY;
}
static s32 __card_readresponse(s32 drv_no,void *buf,s32 len)
{
u8 *ptr;
s32 startT,ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = CARDIO_ERROR_READY;
ptr = buf;
*ptr = _ioClrFlag;
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
while(*ptr&0x80) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(!(*ptr&0x80)) break;
if(__card_checktimeout(drv_no,startT,500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr&0x80) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
if(len>1 && ret==CARDIO_ERROR_READY) {
*(++ptr) = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,len-1,EXI_READWRITE)==0) ret = CARDIO_ERROR_IOERROR;
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return ret;
}
static s32 __card_stopreadresponse(s32 drv_no,void *buf,s32 len)
{
u8 *ptr,tmp;
s32 startT,ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ptr = buf;
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
ret = CARDIO_ERROR_READY;
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
while(*ptr&0x80) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(!(*ptr&0x80)) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr&0x80) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
tmp = *ptr;
while(*ptr!=0xff) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr==0xff) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr!=0xff) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
*ptr = tmp;
if(len>1 && ret==CARDIO_ERROR_READY) {
*(++ptr) = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,len-1,EXI_READWRITE)==0) ret = CARDIO_ERROR_IOERROR;
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return ret;
}
static s32 __card_datares(s32 drv_no,void *buf)
{
u8 *ptr;
s32 startT,ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ptr = buf;
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
ret = CARDIO_ERROR_READY;
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
while(*ptr&0x10) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(!(*ptr&0x10)) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr&0x10) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
*(++ptr) = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
while(!*ptr) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(!*ptr) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return ret;
}
static s32 __card_stopresponse(s32 drv_no)
{
s32 ret;
if((ret=__card_stopreadresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
return ret;
}
static s32 __card_dataresponse(s32 drv_no)
{
s32 ret;
u8 res;
if((ret=__card_datares(drv_no,_ioResponse[drv_no]))!=0) return ret;
res = _SHIFTR(_ioResponse[drv_no][0],1,3);
if(res==0x0005) ret = CARDIO_OP_IOERR_CRC;
else if(res==0x0006) ret = CARDIO_OP_IOERR_WRITE;
return ret;
}
static s32 __card_dataread(s32 drv_no,void *buf,u32 len)
{
u8 *ptr;
u32 cnt;
u8 res[2];
u16 crc,crc_org;
s32 startT,ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
ret = CARDIO_ERROR_READY;
ptr = buf;
for(cnt=0;cnt<len;cnt++) ptr[cnt] = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
while(*ptr!=0xfe) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr==0xfe) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(*ptr!=0xfe) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
*ptr = _ioClrFlag;
if(EXI_ImmEx(drv_no,ptr,len,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
/* sleep 1us*/
usleep(1);
res[0] = res[1] = _ioClrFlag;
if(EXI_ImmEx(drv_no,res,2,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
crc_org = ((res[0]<<8)&0xff00)|(res[1]&0xff);
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
crc = __make_crc16(buf,len);
if(crc!=crc_org) ret = CARDIO_OP_IOERR_CRC;
return ret;
}
static s32 __card_multidatawrite(s32 drv_no,void *buf,u32 len)
{
u8 dummy[32];
u16 crc;
u32 cnt;
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
for(cnt=0;cnt<32;cnt++) dummy[cnt] = _ioClrFlag;
crc = __make_crc16(buf,len);
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
dummy[0] = 0xfc;
if(EXI_ImmEx(drv_no,dummy,1,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(EXI_ImmEx(drv_no,buf,len,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
/* sleep 1us*/
usleep(1);
ret = CARDIO_ERROR_READY;
if(EXI_ImmEx(drv_no,&crc,2,EXI_WRITE)==0) ret = CARDIO_ERROR_IOERROR;
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return ret;
}
static s32 __card_multiwritestop(s32 drv_no)
{
s32 ret,cnt,startT;
u8 dummy[32];
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
for(cnt=0;cnt<32;cnt++) dummy[cnt] = _ioClrFlag;
__exi_wait(drv_no);
if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) {
EXI_Unlock(drv_no);
return CARDIO_ERROR_NOCARD;
}
ret = CARDIO_ERROR_READY;
dummy[0] = 0xfd;
if(_ioWPFlag) dummy[0] = 0x02; //!0xfd
if(EXI_ImmEx(drv_no,dummy,1,EXI_WRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
startT = gettick();
ret = CARDIO_ERROR_READY;
while(dummy[0]==0) {
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(dummy[0]) break;
if(__card_checktimeout(drv_no,startT,1500)!=0) {
dummy[0] = _ioClrFlag;
if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) {
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return CARDIO_ERROR_IOERROR;
}
if(!dummy[0]) ret = CARDIO_ERROR_IOTIMEOUT;
break;
}
}
EXI_Deselect(drv_no);
EXI_Unlock(drv_no);
return ret;
}
static s32 __card_response1(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
return __check_response(drv_no,_ioResponse[drv_no][0]);
}
static s32 __card_response2(s32 drv_no)
{
u32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],2))!=0) return ret;
if(!(_ioResponse[drv_no][0]&0x7c) && !(_ioResponse[drv_no][1]&0x9e)) return CARDIO_ERROR_READY;
return CARDIO_ERROR_FATALERROR;
}
static s32 __card_sendappcmd(s32 drv_no)
{
s32 ret;
u8 ccmd[5] = {0,0,0,0,0};
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ccmd[0] = 0x37;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
return ret;
}
static s32 __card_sendopcond(s32 drv_no)
{
u8 ccmd[5] = {0,0,0,0,0};
s32 ret;
s32 startT;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = 0;
startT = gettick();
do {
if(_initType[drv_no]==TYPE_SDHC) {
__card_sendappcmd(drv_no);
ccmd[0] = 0x29;
ccmd[1] = 0x40;
} else
ccmd[0] = 0x01;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
if((ret=__check_response(drv_no,_ioResponse[drv_no][0]))!=0) return ret;
if(!(_ioError[drv_no]&CARDIO_OP_IOERR_IDLE)) return CARDIO_ERROR_READY;
ret = __card_checktimeout(drv_no,startT,1500);
} while(ret==0);
if(_initType[drv_no]==TYPE_SDHC) {
__card_sendappcmd(drv_no);
ccmd[0] = 0x29;
ccmd[1] = 0x40;
} else
ccmd[0] = 0x01;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
if((ret=__check_response(drv_no,_ioResponse[drv_no][0]))!=0) return ret;
if(_ioError[drv_no]&CARDIO_OP_IOERR_IDLE) return CARDIO_ERROR_IOERROR;
return CARDIO_ERROR_READY;
}
static s32 __card_sendCMD8(s32 drv_no)
{
s32 ret;
u8 ccmd[5] = {0,0,0,0,0};
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ccmd[0] = 0x08;
ccmd[3] = 0x01;
ccmd[4] = 0xAA;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0){
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],5))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
return ret;
}
static s32 __card_sendCMD58(s32 drv_no)
{
s32 ret;
u8 ccmd[5] = {0,0,0,0,0};
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ccmd[0]= 0x3A;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],5))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
return ret;
}
static s32 __card_sendcmd(s32 drv_no,u8 cmd,u8 *arg)
{
u8 ccmd[5] = {0,0,0,0,0};
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ccmd[0] = cmd;
if(arg) {
ccmd[1] = arg[0];
ccmd[2] = arg[1];
ccmd[3] = arg[2];
ccmd[4] = arg[3];
}
return __card_writecmd(drv_no,ccmd,5);
}
static s32 __card_setblocklen(s32 drv_no,u32 block_len)
{
u8 cmd[5];
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
if(block_len>PAGE_SIZE512) block_len = PAGE_SIZE512;
cmd[0] = 0x10;
cmd[1] = (block_len>>24)&0xff;
cmd[2] = (block_len>>16)&0xff;
cmd[3] = (block_len>>8)&0xff;
cmd[4] = block_len&0xff;
if((ret=__card_writecmd(drv_no,cmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))<0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
return ret;
}
static s32 __card_readcsd(s32 drv_no)
{
u8 ccmd[5] = {0,0,0,0,0};
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = 0;
ccmd[0] = 0x09;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
if(ret==0) {
if((ret=__card_dataread(drv_no,g_CSD[drv_no],16))!=0) return ret;
}
return ret;
}
static s32 __card_readcid(s32 drv_no)
{
u8 ccmd[5] = {0,0,0,0,0};
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = 0;
ccmd[0] = 0x0A;
if((ret=__card_writecmd(drv_no,ccmd,5))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
ret = __check_response(drv_no,_ioResponse[drv_no][0]);
if(ret==0) {
if((ret=__card_dataread(drv_no,g_CID[drv_no],16))!=0) return ret;
}
return ret;
}
static s32 __card_sd_status(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
if(_ioPageSize[drv_no]!=64) {
_ioPageSize[drv_no] = 64;
if((ret=__card_setblocklen(drv_no,_ioPageSize[drv_no]))!=0) return ret;
}
if((ret=__card_sendappcmd(drv_no))!=0) return ret;
if((ret=__card_sendcmd(drv_no,0x0d,NULL))!=0) return ret;
if((ret=__card_response2(drv_no))!=0) return ret;
ret = __card_dataread(drv_no,g_CardStatus[drv_no],64);
return ret;
}
static s32 __card_softreset(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = 0;
if((ret=__card_writecmd0(drv_no))!=0) {
return ret;
}
if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret;
return __check_response(drv_no,_ioResponse[drv_no][0]);
}
static bool __card_check(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return FALSE;
while((ret=EXI_ProbeEx(drv_no))==0);
if(ret!=1) return FALSE;
if(!(EXI_GetState(drv_no)&EXI_FLAG_ATTACH)) {
if(EXI_Attach(drv_no,__card_exthandler)==0) return FALSE;
sdgecko_insertedCB(drv_no);
}
return TRUE;
}
static s32 __card_retrycb(s32 drv_no)
{
_ioRetryCB = NULL;
_ioRetryCnt++;
return sdgecko_initIO(drv_no);
}
static void __convert_sector(s32 drv_no,u32 sector_no,u8 *arg)
{
if(_ioAddressingType[drv_no]==BYTE_ADDRESSING) {
arg[0] = (sector_no>>15)&0xff;
arg[1] = (sector_no>>7)&0xff;
arg[2] = (sector_no<<1)&0xff;
arg[3] = (sector_no<<9)&0xff;
} else if(_ioAddressingType[drv_no]==SECTOR_ADDRESSING) {
arg[0] = (sector_no>>24)&0xff;
arg[1] = (sector_no>>16)&0xff;
arg[2] = (sector_no>>8)&0xff;
arg[3] = sector_no&0xff;
}
}
void sdgecko_initIODefault()
{
u32 i;
__init_crc7();
__init_crc16();
for(i=0;i<MAX_DRIVE;++i) {
_ioRetryCnt = 0;
_ioError[i] = 0;
_ioCardInserted[i] = FALSE;
_ioFlag[i] = NOT_INITIALIZED;
_ioAddressingType[i] = BYTE_ADDRESSING;
_initType[i] = TYPE_SD;
LWP_InitQueue(&_ioEXILock[i]);
}
}
s32 sdgecko_initIO(s32 drv_no)
{
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
u32 id = 0;
EXI_GetID(drv_no,EXI_DEVICE_0,&id);
if ( id != -1 ) return CARDIO_ERROR_NOCARD;
if(_ioRetryCnt>5) {
_ioRetryCnt = 0;
return CARDIO_ERROR_IOERROR;
}
_ioCardInserted[drv_no] = __card_check(drv_no);
if(_ioCardInserted[drv_no]==TRUE) {
_ioWPFlag = 0;
_ioCardFreq = EXI_SPEED16MHZ;
_initType[drv_no] = TYPE_SD;
_ioFlag[drv_no] = INITIALIZING;
_ioAddressingType[drv_no] = BYTE_ADDRESSING;
if(__card_softreset(drv_no)!=0) {
_ioWPFlag = 1;
if(__card_softreset(drv_no)!=0) goto exit;
}
if(__card_sendCMD8(drv_no)!=0) goto exit;
if((_ioResponse[drv_no][3]==1) && (_ioResponse[drv_no][4]==0xAA)) _initType[drv_no] = TYPE_SDHC;
if(__card_sendopcond(drv_no)!=0) goto exit;
if(__card_readcsd(drv_no)!=0) goto exit;
if(__card_readcid(drv_no)!=0) goto exit;
if(_initType[drv_no]==TYPE_SDHC) {
if(__card_sendCMD58(drv_no)!=0) goto exit;
if(_ioResponse[drv_no][1]&0x40) _ioAddressingType[drv_no] = SECTOR_ADDRESSING;
}
_ioPageSize[drv_no] = 1<<WRITE_BL_LEN(drv_no);
if(__card_setblocklen(drv_no,_ioPageSize[drv_no])!=0) goto exit;
if(__card_sd_status(drv_no)!=0) goto exit;
_ioRetryCnt = 0;
_ioFlag[drv_no] = INITIALIZED;
return CARDIO_ERROR_READY;
exit:
_ioRetryCB = __card_retrycb;
return sdgecko_doUnmount(drv_no);
}
return CARDIO_ERROR_NOCARD;
}
s32 sdgecko_preIO(s32 drv_no)
{
s32 ret;
if(_ioFlag[drv_no]!=INITIALIZED) {
ret = sdgecko_initIO(drv_no);
if(ret!=CARDIO_ERROR_READY) {
return ret;
}
}
return CARDIO_ERROR_READY;
}
s32 sdgecko_readCID(s32 drv_no)
{
s32 ret;
if(drv_no<EXI_CHANNEL_0 || drv_no>=EXI_CHANNEL_2) return CARDIO_ERROR_NOCARD;
ret = sdgecko_preIO(drv_no);
if(ret!=0) return ret;
return __card_readcid(drv_no);
}
s32 sdgecko_readCSD(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = sdgecko_preIO(drv_no);
if(ret!=0) return ret;
return __card_readcsd(drv_no);
}
s32 sdgecko_readStatus(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = sdgecko_preIO(drv_no);
if(ret!=0) return ret;
return __card_sd_status(drv_no);
}
// Multiple sector read by emu_kidid
s32 sdgecko_readSectors(s32 drv_no,u32 sector_no,u32 num_sectors,void *buf)
{
u32 i;
s32 ret;
u8 arg[4];
char *ptr = (char*)buf;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = sdgecko_preIO(drv_no);
if(ret!=0) return ret;
if(num_sectors<1) return CARDIO_ERROR_INTERNAL;
// Must be 512b, otherwise fail!
if(PAGE_SIZE512!=_ioPageSize[drv_no]) {
_ioPageSize[drv_no] = PAGE_SIZE512;
if((ret=__card_setblocklen(drv_no,PAGE_SIZE512))!=0) return ret;
}
// SDHC support fix
__convert_sector(drv_no,sector_no,arg);
if((ret=__card_sendcmd(drv_no,0x12,arg))!=0) return ret;
if((ret=__card_response1(drv_no))!=0) return ret;
for(i=0;i<num_sectors;i++) {
if((ret=__card_dataread(drv_no,ptr,_ioPageSize[drv_no]))!=0) return ret;
ptr += _ioPageSize[drv_no];
}
if((ret=__card_sendcmd(drv_no,0x0C,NULL))!=0) return ret;
return __card_stopresponse(drv_no);
}
s32 sdgecko_writeSectors(s32 drv_no,u32 sector_no,u32 num_sectors,const void *buf)
{
u32 i;
s32 ret;
u8 arg[4];
char *ptr = (char*)buf;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
ret = sdgecko_preIO(drv_no);
if(ret!=0) return ret;
if(num_sectors<1) return CARDIO_ERROR_INTERNAL;
if(PAGE_SIZE512!=_ioPageSize[drv_no]) {
_ioPageSize[drv_no] = PAGE_SIZE512;
if((ret=__card_setblocklen(drv_no,_ioPageSize[drv_no]))!=0) return ret;
}
// send SET_WRITE_BLK_ERASE_CNT cmd
arg[0] = (num_sectors>>24)&0xff;
arg[1] = (num_sectors>>16)&0xff;
arg[2] = (num_sectors>>8)&0xff;
arg[3] = num_sectors&0xff;
if((ret=__card_sendappcmd(drv_no))!=0) return ret;
if((ret=__card_sendcmd(drv_no,0x17,arg))!=0) return ret;
if((ret=__card_response1(drv_no))!=0) return ret;
// SDHC support fix
__convert_sector(drv_no,sector_no,arg);
if((ret=__card_sendcmd(drv_no,0x19,arg))!=0) return ret;
if((ret=__card_response1(drv_no))!=0) return ret;
for(i=0;i<num_sectors;i++) {
if((ret=__card_multidatawrite(drv_no,ptr,_ioPageSize[drv_no]))!=0) return ret;
if((ret=__card_dataresponse(drv_no))!=0) {
if((ret=__card_sendcmd(drv_no,0x0C,arg))!=0) return ret;
return __card_stopresponse(drv_no);
}
ptr += _ioPageSize[drv_no];
}
if((ret=__card_multiwritestop(drv_no))!=0) return ret;
if((ret=__card_sendcmd(drv_no,0x0D,NULL))!=0) return ret;
return __card_response2(drv_no);
}
s32 sdgecko_doUnmount(s32 drv_no)
{
s32 ret;
if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD;
if(__card_check(drv_no)==TRUE && _ioFlag[drv_no]!=NOT_INITIALIZED) {
if((ret=__card_sendappcmd(drv_no))!=0) goto exit;
if((ret=__card_sendcmd(drv_no,0x2a,NULL))!=0) goto exit;
ret = __card_response1(drv_no);
}
_ioFlag[drv_no] = NOT_INITIALIZED;
exit:
if(_ioCardInserted[drv_no]==TRUE) {
_ioCardInserted[drv_no] = FALSE;
EXI_Detach(drv_no);
}
if(_ioRetryCB)
return _ioRetryCB(drv_no);
return CARDIO_ERROR_READY;
}
static void (*pfCallbackIN[MAX_DRIVE])(s32) = {NULL, NULL};
static void (*pfCallbackOUT[MAX_DRIVE])(s32) = {NULL, NULL};
void sdgecko_insertedCB(s32 drv_no)
{
if(pfCallbackIN[drv_no])
pfCallbackIN[drv_no](drv_no);
}
void sdgecko_ejectedCB(s32 drv_no)
{
if(pfCallbackOUT[drv_no])
pfCallbackOUT[drv_no](drv_no);
}
void sdgecko_setSpeed(u32 freq)
{
_ioCardFreq = freq;
}
u32 sdgecko_getPageSize(s32 drv_no)
{
return _ioPageSize[drv_no];
}
u32 sdgecko_setPageSize(s32 drv_no, int size)
{
if(_ioPageSize[drv_no]!=size)
_ioPageSize[drv_no] = size;
return __card_setblocklen(drv_no, _ioPageSize[drv_no]);
}
u32 sdgecko_getAddressingType(s32 drv_no)
{
return _ioAddressingType[drv_no];
}