#include #include #include #include #include #include CARDControl __CARDBlock[2]; DVDDiskID __CARDDiskNone; static u16 __CARDEncode; s32 __CARDReadStatus(s32 chan, u8 *status); s32 __CARDClearStatus(s32 chan); void __CARDSetDiskID(const DVDDiskID *id); static s32 Retry(s32 chan); static BOOL OnReset(BOOL f); static OSResetFunctionInfo ResetFunctionInfo = { OnReset, 127 }; void __CARDDefaultApiCallback(s32 chan, s32 result) { } void __CARDSyncCallback(s32 chan, s32 result) { OSWakeupThread(&__CARDBlock[chan].threadQueue); } void __CARDExtHandler(s32 chan, OSContext *context) { CARDControl *card; CARDCallback callback; card = &__CARDBlock[chan]; if (card->attached) { card->attached = FALSE; EXISetExiCallback(chan, 0); OSCancelAlarm(&card->alarm); callback = card->exiCallback; if (callback) { card->exiCallback = 0; callback(chan, CARD_RESULT_NOCARD); } if (card->result != CARD_RESULT_BUSY) { card->result = CARD_RESULT_NOCARD; } callback = card->extCallback; if (callback && CARD_MAX_MOUNT_STEP <= card->mountStep) { card->extCallback = 0; callback(chan, CARD_RESULT_NOCARD); } } } void __CARDExiHandler(s32 chan, OSContext *context) { CARDControl *card; CARDCallback callback; u8 status; s32 result; card = &__CARDBlock[chan]; OSCancelAlarm(&card->alarm); if (!card->attached) { return; } if (!EXILock(chan, 0, 0)) { result = CARD_RESULT_FATAL_ERROR; goto fatal; } if ((result = __CARDReadStatus(chan, &status)) < 0 || (result = __CARDClearStatus(chan)) < 0) { goto error; } if ((result = (status & 0x18) ? CARD_RESULT_IOERROR : CARD_RESULT_READY) == CARD_RESULT_IOERROR && --card->retry > 0) { result = Retry(chan); if (result >= 0) { return; } goto fatal; } error: EXIUnlock(chan); fatal: callback = card->exiCallback; if (callback) { card->exiCallback = 0; callback(chan, result); } } void __CARDTxHandler(s32 chan, OSContext *context) { CARDControl *card; CARDCallback callback; BOOL err; card = &__CARDBlock[chan]; err = !EXIDeselect(chan); EXIUnlock(chan); callback = card->txCallback; if (callback) { card->txCallback = 0; callback(chan, (!err && EXIProbe(chan)) ? CARD_RESULT_READY : CARD_RESULT_NOCARD); } } void __CARDUnlockedHandler(s32 chan, OSContext *context) { CARDControl *card; CARDCallback callback; card = &__CARDBlock[chan]; callback = card->unlockCallback; if (callback) { card->unlockCallback = 0; callback(chan, EXIProbe(chan) ? CARD_RESULT_UNLOCKED : CARD_RESULT_NOCARD); } } s32 __CARDEnableInterrupt(s32 chan, BOOL enable) { BOOL err; u32 cmd; if (!EXISelect(chan, 0, 4)) { return CARD_RESULT_NOCARD; } cmd = enable ? 0x81010000 : 0x81000000; err = FALSE; err |= !EXIImm(chan, &cmd, 2, 1, NULL); err |= !EXISync(chan); err |= !EXIDeselect(chan); return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY; } s32 __CARDReadStatus(s32 chan, u8 *status) { BOOL err; u32 cmd; if (!EXISelect(chan, 0, 4)) { return CARD_RESULT_NOCARD; } cmd = 0x83000000; err = FALSE; err |= !EXIImm(chan, &cmd, 2, 1, NULL); err |= !EXISync(chan); err |= !EXIImm(chan, status, 1, 0, NULL); err |= !EXISync(chan); err |= !EXIDeselect(chan); return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY; } s32 __CARDClearStatus(s32 chan) { BOOL err; u32 cmd; if (!EXISelect(chan, 0, 4)) { return CARD_RESULT_NOCARD; } cmd = 0x89000000; err = FALSE; err |= !EXIImm(chan, &cmd, 1, 1, 0); err |= !EXISync(chan); err |= !EXIDeselect(chan); return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY; } static void TimeoutHandler(OSAlarm *alarm, OSContext *context) { s32 chan; CARDControl *card; CARDCallback callback; for (chan = 0; chan < 2; ++chan) { card = &__CARDBlock[chan]; if (alarm == &card->alarm) { break; } } if (!card->attached) { return; } EXISetExiCallback(chan, NULL); callback = card->exiCallback; if (callback) { card->exiCallback = 0; callback(chan, CARD_RESULT_IOERROR); } } static void SetupTimeoutAlarm(CARDControl *card) { OSCancelAlarm(&card->alarm); switch (card->cmd[0]) { case 0xF2: OSSetAlarm(&card->alarm, OSMillisecondsToTicks(100), TimeoutHandler); break; case 0xF3: break; case 0xF4: case 0xF1: OSSetAlarm(&card->alarm, OSSecondsToTicks((OSTime)2) * (card->sectorSize / 0x2000), TimeoutHandler); break; } } static s32 Retry(s32 chan) { CARDControl *card; card = &__CARDBlock[chan]; if (!EXISelect(chan, 0, 4)) { EXIUnlock(chan); return CARD_RESULT_NOCARD; } SetupTimeoutAlarm(card); if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) { EXIDeselect(chan); EXIUnlock(chan); return CARD_RESULT_NOCARD; } if (card->cmd[0] == 0x52 && !EXIImmEx(chan, (u8 *)card->workArea + sizeof(CARDID), card->latency, 1)) { EXIDeselect(chan); EXIUnlock(chan); return CARD_RESULT_NOCARD; } if (card->mode == 0xffffffff) { EXIDeselect(chan); EXIUnlock(chan); return CARD_RESULT_READY; } if (!EXIDma(chan, card->buffer, (s32)((card->cmd[0] == 0x52) ? 512 : 128), card->mode, __CARDTxHandler)) { EXIDeselect(chan); EXIUnlock(chan); return CARD_RESULT_NOCARD; } return CARD_RESULT_READY; } static void UnlockedCallback(s32 chan, s32 result) { CARDCallback callback; CARDControl *card; card = &__CARDBlock[chan]; if (result >= 0) { card->unlockCallback = UnlockedCallback; if (!EXILock(chan, 0, __CARDUnlockedHandler)) { result = CARD_RESULT_READY; } else { card->unlockCallback = 0; result = Retry(chan); } } if (result < 0) { switch (card->cmd[0]) { case 0x52: callback = card->txCallback; if (callback) { card->txCallback = 0; callback(chan, result); } break; case 0xF2: case 0xF4: case 0xF1: callback = card->exiCallback; if (callback) { card->exiCallback = 0; callback(chan, result); } break; } } } static s32 __CARDStart(s32 chan, CARDCallback txCallback, CARDCallback exiCallback) { BOOL enabled; CARDControl *card; s32 result; enabled = OSDisableInterrupts(); card = &__CARDBlock[chan]; if (!card->attached) { result = CARD_RESULT_NOCARD; } else { if (txCallback) { card->txCallback = txCallback; } if (exiCallback) { card->exiCallback = exiCallback; } card->unlockCallback = UnlockedCallback; if (!EXILock(chan, 0, __CARDUnlockedHandler)) { result = CARD_RESULT_BUSY; } else { card->unlockCallback = 0; if (!EXISelect(chan, 0, 4)) { EXIUnlock(chan); result = CARD_RESULT_NOCARD; } else { SetupTimeoutAlarm(card); result = CARD_RESULT_READY; } } } OSRestoreInterrupts(enabled); return result; } #define AD1(x) ((u8)(((x) >> 17) & 0x7f)) #define AD1EX(x) ((u8)(AD1(x) | 0x80)); #define AD2(x) ((u8)(((x) >> 9) & 0xff)) #define AD3(x) ((u8)(((x) >> 7) & 0x03)) #define BA(x) ((u8)((x)&0x7f)) s32 __CARDReadSegment(s32 chan, CARDCallback callback) { CARDControl *card; s32 result; card = &__CARDBlock[chan]; card->cmd[0] = 0x52; card->cmd[1] = AD1(card->addr); card->cmd[2] = AD2(card->addr); card->cmd[3] = AD3(card->addr); card->cmd[4] = BA(card->addr); card->cmdlen = 5; card->mode = 0; card->retry = 0; result = __CARDStart(chan, callback, 0); if (result == CARD_RESULT_BUSY) { result = CARD_RESULT_READY; } else if (result >= 0) { if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) || !EXIImmEx(chan, (u8 *)card->workArea + sizeof(CARDID), card->latency, 1) || // XXX use DMA if possible !EXIDma(chan, card->buffer, 512, card->mode, __CARDTxHandler)) { card->txCallback = 0; EXIDeselect(chan); EXIUnlock(chan); result = CARD_RESULT_NOCARD; } else { result = CARD_RESULT_READY; } } return result; } s32 __CARDWritePage(s32 chan, CARDCallback callback) { CARDControl *card; s32 result; card = &__CARDBlock[chan]; card->cmd[0] = 0xF2; card->cmd[1] = AD1(card->addr); card->cmd[2] = AD2(card->addr); card->cmd[3] = AD3(card->addr); card->cmd[4] = BA(card->addr); card->cmdlen = 5; card->mode = 1; card->retry = 3; result = __CARDStart(chan, 0, callback); if (result == CARD_RESULT_BUSY) { result = CARD_RESULT_READY; } else if (result >= 0) { if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) || !EXIDma(chan, card->buffer, 128, card->mode, __CARDTxHandler)) { card->exiCallback = 0; EXIDeselect(chan); EXIUnlock(chan); result = CARD_RESULT_NOCARD; } else { result = CARD_RESULT_READY; } } return result; } s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) { CARDControl *card; s32 result; card = &__CARDBlock[chan]; card->cmd[0] = 0xF1; card->cmd[1] = AD1(addr); card->cmd[2] = AD2(addr); card->cmdlen = 3; card->mode = -1; card->retry = 3; result = __CARDStart(chan, 0, callback); if (result == CARD_RESULT_BUSY) { result = CARD_RESULT_READY; } else if (result >= 0) { if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) { card->exiCallback = NULL; result = CARD_RESULT_NOCARD; } else { result = CARD_RESULT_READY; } EXIDeselect(chan); EXIUnlock(chan); } return result; } void CARDInit(void) { int chan; if (__CARDBlock[0].diskID && __CARDBlock[1].diskID) { return; } DSPInit(); OSInitAlarm(); for (chan = 0; chan < 2; ++chan) { CARDControl *card = &__CARDBlock[chan]; card->result = CARD_RESULT_NOCARD; OSInitThreadQueue(&card->threadQueue); OSCreateAlarm(&card->alarm); } __CARDSetDiskID((DVDDiskID *)OSPhysicalToCached(0x0)); OSRegisterResetFunction(&ResetFunctionInfo); } u16 __CARDGetFontEncode() { return __CARDEncode; } void __CARDSetDiskID(const DVDDiskID *id) { __CARDBlock[0].diskID = id ? id : &__CARDDiskNone; __CARDBlock[1].diskID = id ? id : &__CARDDiskNone; } s32 __CARDGetControlBlock(s32 chan, CARDControl **pcard) { BOOL enabled; s32 result; CARDControl *card; card = &__CARDBlock[chan]; if (chan < 0 || chan >= 2 || card->diskID == NULL) { return CARD_RESULT_FATAL_ERROR; } enabled = OSDisableInterrupts(); if (!card->attached) { result = CARD_RESULT_NOCARD; } else if (card->result == CARD_RESULT_BUSY) { result = CARD_RESULT_BUSY; } else { card->result = CARD_RESULT_BUSY; result = CARD_RESULT_READY; card->apiCallback = 0; *pcard = card; } OSRestoreInterrupts(enabled); return result; } s32 __CARDPutControlBlock(CARDControl *card, s32 result) { BOOL enabled; enabled = OSDisableInterrupts(); if (card->attached) { card->result = result; } else if (card->result == CARD_RESULT_BUSY) { card->result = result; } OSRestoreInterrupts(enabled); return result; } s32 CARDGetResultCode(s32 chan) { CARDControl *card; if (chan < 0 || chan >= 2) { return CARD_RESULT_FATAL_ERROR; } card = &__CARDBlock[chan]; return card->result; } s32 CARDFreeBlocks(s32 chan, s32 *byteNotUsed, s32 *filesNotUsed) { CARDControl *card; s32 result; u16 *fat; CARDDir *dir; CARDDir *ent; u16 fileNo; result = __CARDGetControlBlock(chan, &card); if (result < 0) { return result; } fat = __CARDGetFatBlock(card); dir = __CARDGetDirBlock(card); if (fat == 0 || dir == 0) { return __CARDPutControlBlock(card, CARD_RESULT_BROKEN); } if (byteNotUsed) { *byteNotUsed = (s32)(card->sectorSize * fat[CARD_FAT_FREEBLOCKS]); } if (filesNotUsed) { *filesNotUsed = 0; for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) { ent = &dir[fileNo]; if (ent->fileName[0] == 0xff) { ++*filesNotUsed; } } } return __CARDPutControlBlock(card, CARD_RESULT_READY); } s32 CARDGetSectorSize(s32 chan, u32 *size) { struct CARDControl *card; long result; result = __CARDGetControlBlock(chan, &card); if (result < 0) { return result; } *size = card->sectorSize; return __CARDPutControlBlock(card, 0); } s32 __CARDSync(s32 chan) { CARDControl *block; s32 result; s32 enabled; block = &__CARDBlock[chan]; enabled = OSDisableInterrupts(); while ((result = CARDGetResultCode(chan)) == -1) { OSSleepThread(&block->threadQueue); } OSRestoreInterrupts(enabled); return result; } static BOOL OnReset(BOOL f) { if (!f) { if (CARDUnmount(0) == CARD_RESULT_BUSY || CARDUnmount(1) == CARD_RESULT_BUSY) { return FALSE; } } return TRUE; }