diff options
Diffstat (limited to 'src/Wifi.cpp')
-rw-r--r-- | src/Wifi.cpp | 439 |
1 files changed, 318 insertions, 121 deletions
diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 4da253e..12926c6 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -159,11 +159,45 @@ void Wifi::Reset() #undef BBREG_FIXED const Firmware& fw = NDS.SPI.GetFirmware(); + const auto& fwheader = fw.GetHeader(); - RFVersion = fw.GetHeader().RFChipType; + RFVersion = fwheader.RFChipType; memset(RFRegs, 0, 4*0x40); - Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType; + // load channel index/data from the firmware + // the current channel will be determined by RF settings + // so we compare the two 'most important' RF registers to these values to figure out which channel is selected + + if (RFVersion == 3) + { + RFChannelIndex[0] = fwheader.Type3Config.RFIndex1; + RFChannelIndex[1] = fwheader.Type3Config.RFIndex2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type3Config.RFData1[i]; + RFChannelData[i][1] = fwheader.Type3Config.RFData2[i]; + } + } + else + { + RFChannelIndex[0] = fwheader.Type2Config.InitialRF56Values[2] >> 2; + RFChannelIndex[1] = fwheader.Type2Config.InitialRF56Values[5] >> 2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type2Config.InitialRF56Values[i*6 + 0] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 1] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 2] & 0x03) << 16); + RFChannelData[i][1] = fwheader.Type2Config.InitialRF56Values[i*6 + 3] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 4] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 5] & 0x03) << 16); + } + } + + CurChannel = 0; + + Firmware::FirmwareConsoleType console = fwheader.ConsoleType; if (console == Firmware::FirmwareConsoleType::DS) IOPORT(0x000) = 0x1440; else if (console == Firmware::FirmwareConsoleType::DSLite) @@ -182,6 +216,8 @@ void Wifi::Reset() // TODO: find out what the initial values are IOPORT(W_PowerUS) = 0x0001; + //IOPORT(W_BeaconInterval) = 100; + USTimestamp = 0; USCounter = 0; @@ -209,7 +245,6 @@ void Wifi::Reset() CmdCounter = 0; USUntilPowerOn = 0; - ForcePowerOn = false; IsMP = false; IsMPClient = false; @@ -244,6 +279,8 @@ void Wifi::DoSavestate(Savestate* file) file->Var8(&RFVersion); file->VarArray(RFRegs, 4*0x40); + file->Var32((u32*)&CurChannel); + file->Var64(&USCounter); file->Var64(&USCompare); file->Bool32(&BlockBeaconIRQ14); @@ -285,7 +322,6 @@ void Wifi::DoSavestate(Savestate* file) file->Var16(&MPLastSeqno); file->Var32((u32*)&USUntilPowerOn); - file->Bool32(&ForcePowerOn); file->Bool32(&IsMP); file->Bool32(&IsMPClient); @@ -351,33 +387,38 @@ void Wifi::SetPowerCnt(u32 val) } -void Wifi::SetIRQ(u32 irq) +void Wifi::CheckIRQ(u16 oldflags) { - u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE); - - IOPORT(W_IF) |= (1<<irq); - u32 newflags = IOPORT(W_IF) & IOPORT(W_IE); + u16 newflags = IOPORT(W_IF) & IOPORT(W_IE); if ((oldflags == 0) && (newflags != 0)) NDS.SetIRQ(1, IRQ_Wifi); } +void Wifi::SetIRQ(u32 irq) +{ + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + + IOPORT(W_IF) |= (1<<irq); + CheckIRQ(oldflags); +} + void Wifi::SetIRQ13() { SetIRQ(13); - if (!(IOPORT(W_PowerTX) & 0x0002)) + if ((IOPORT(W_ModeWEP) & 0x7) != 3) { - IOPORT(0x034) = 0x0002; - //PowerDown(); - // FIXME!! - IOPORT(W_RFPins) = 0x0046; - IOPORT(W_RFStatus) = 9; + if (!(IOPORT(W_PowerTX) & (1<<1))) + { + UpdatePowerStatus(-1); + } } } void Wifi::SetIRQ14(int source) // 0=USCOMPARE 1=BEACONCOUNT 2=forced { + // CHECKME: is this also done for USCOMPARE IRQ? if (source != 2) IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval); @@ -409,10 +450,10 @@ void Wifi::SetIRQ15() { SetIRQ(15); - if (IOPORT(W_PowerTX) & 0x0001) + // unlike auto sleep, auto wakeup works under all power management modes + if (IOPORT(W_PowerTX) & (1<<0)) { - IOPORT(W_RFPins) |= 0x0080; - IOPORT(W_RFStatus) = 1; + UpdatePowerStatus(1); } } @@ -426,16 +467,110 @@ void Wifi::SetStatus(u32 status) } -void Wifi::PowerDown() +void Wifi::UpdatePowerStatus(int power) // 1=on 0=no change -1=off { - IOPORT(W_TXReqRead) &= ~0x000F; - IOPORT(W_PowerState) |= 0x0200; + // TRANSCEIVER POWER MANAGEMENT + // + // * W_PowerForce overrides all else + // * W_ModeReset bit0 forcibly turns off the transceiver when cleared + // * power is normally turned on or off either by IRQ15/IRQ13 or by W_PowerState + // depending on the power management mode selected in W_ModeWEP + // * W_PowerDownCtrl controls how deep a regular power-down is - // if the RF hardware is powered down while still sending or receiving, - // the current frame is completed before going idle - if (!ComStatus) + int curflags = 0; + if (IOPORT(W_TRXPower) == 1) curflags |= 1; + if (!(IOPORT(W_PowerState) & (1<<9))) curflags |= 2; + int reqflags = curflags; + + if (IOPORT(W_PowerForce) & (1<<15)) + { + reqflags = (IOPORT(W_PowerForce) & (1<<0)) ? 0 : 3; + } + else if (!(IOPORT(W_ModeReset) & (1<<0))) { - SetStatus(9); + reqflags = 0; + } + else + { + if (power == 0) + { + if ((IOPORT(W_PowerState) & 0x0202) == 0x0202) + power = 1; + else if ((IOPORT(W_PowerState) & 0x0201) == 0x0001) + power = -1; + } + + // W_PowerDownCtrl: + // * bit 0 inhibits a regular power-down + // * bit 1 forces a wakeup, atleast partial + + if ((power == -1) && (IOPORT(W_PowerDownCtrl) & (1<<0))) + power = 0; + + /*if (power == 1) + reqflags = 3; + else if (power == -1) + reqflags = IOPORT(W_PowerDownCtrl); + else if (IOPORT(W_PowerDownCtrl) & (1<<1)) + reqflags = (curflags == 3) ? 3 : IOPORT(W_PowerDownCtrl);*/ + + // TODO: support partial power statuses (W_PowerDownCtrl=1 or 2) + + if (power == 1) + reqflags = 3; + else if (power == -1) + reqflags = IOPORT(W_PowerDownCtrl) ? 3 : 0; + else if (IOPORT(W_PowerDownCtrl) & (1<<1)) + reqflags = 3; + } + + if (reqflags == curflags) + return; + + if (reqflags & 1) + { + if (!(curflags & 1)) + { + IOPORT(W_TRXPower) = 1; + SetStatus(1); + } + } + else + { + // signal the transceiver is going to turn off (checkme) + IOPORT(W_TRXPower) = 2; + + if (!ComStatus) + { + IOPORT(W_TRXPower) = 0; + SetStatus(9); + } + } + + if (reqflags & 2) + { + // power on + + IOPORT(W_PowerState) |= (1<<8); + if ((!(curflags & 2)) && (USUntilPowerOn == 0)) + { + Log(LogLevel::Debug, "wifi: TRX power ON\n"); + + USUntilPowerOn = -2048; + SetIRQ(11); + } + } + else + { + // power off + + if (curflags & 2) + Log(LogLevel::Debug, "wifi: TRX power OFF\n"); + + IOPORT(W_PowerState) &= ~(1<<0); + IOPORT(W_PowerState) &= ~(1<<8); + IOPORT(W_PowerState) |= (1<<9); + USUntilPowerOn = 0; } } @@ -521,6 +656,9 @@ void Wifi::TXSendFrame(const TXSlot* slot, int num) if (noseqno == 2) *(u16*)&TXBuffer[0xC] |= (1<<11); + if (CurChannel == 0) return; + TXBuffer[9] = CurChannel; + switch (num) { case 0: @@ -600,6 +738,9 @@ void Wifi::StartTX_Cmd() slot->CurPhase = 13; slot->CurPhaseTime = CmdCounter - 100; } + + // starting a CMD transfer wakes up the transceiver automatically + UpdatePowerStatus(1); } void Wifi::StartTX_Beacon() @@ -678,6 +819,9 @@ void Wifi::SendMPDefaultReply() // TODO reply[0x8] = 0x14; + if (CurChannel == 0) return; + reply[0x9] = CurChannel; + *(u16*)&reply[0xC + 0x00] = 0x0158; *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO?? *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0); @@ -767,6 +911,9 @@ void Wifi::SendMPAck(u16 cmdcount, u16 clientfail) if (TXSlots[1].Rate == 2) ack[0x8] = 0x14; else ack[0x8] = 0xA; + if (CurChannel == 0) return; + ack[0x9] = CurChannel; + *(u16*)&ack[0xC + 0x00] = 0x0218; *(u16*)&ack[0xC + 0x02] = 0; *(u16*)&ack[0xC + 0x04] = 0x0903; @@ -1110,8 +1257,11 @@ void Wifi::FinishRX() if (!ComStatus) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) + { + IOPORT(W_TRXPower) = 0; SetStatus(9); + } else SetStatus(1); } @@ -1380,7 +1530,7 @@ void Wifi::FinishRX() void Wifi::MPClientReplyRX(int client) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1421,7 +1571,7 @@ void Wifi::MPClientReplyRX(int client) bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return false; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1433,7 +1583,7 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames int rxlen; int framelen; u16 framectl; - u8 txrate; + u8 txrate, chan; u64 timestamp; for (;;) @@ -1468,6 +1618,13 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames continue; } + chan = RXBuffer[9]; + if (chan != CurChannel || CurChannel == 0) + { + Log(LogLevel::Debug, "received frame but bad channel %d (expected %d)\n", chan, CurChannel); + continue; + } + framectl = *(u16*)&RXBuffer[12+0]; txrate = RXBuffer[8]; @@ -1528,7 +1685,6 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames // we also need to determine how far we can run after having received this frame RXTimestamp = timestamp; - //if (RXTimestamp < USTimestamp) printf("CRAP!! %04X %016llX %016llX\n", framectl, RXTimestamp, USTimestamp); if (RXTimestamp < USTimestamp) RXTimestamp = USTimestamp; NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8)); @@ -1570,11 +1726,13 @@ void Wifi::MSTimer() } } - IOPORT(W_BeaconCount1)--; - if (IOPORT(W_BeaconCount1) == 0) + if (IOPORT(W_BeaconCount1) != 0) { - SetIRQ14(1); + IOPORT(W_BeaconCount1)--; + if (IOPORT(W_BeaconCount1) == 0) SetIRQ14(1); } + if (IOPORT(W_BeaconCount1) == 0) + IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval); if (IOPORT(W_BeaconCount2) != 0) { @@ -1605,19 +1763,19 @@ void Wifi::USTimer(u32 param) if (!(USTimestamp & 0x3FF & kTimeCheckMask)) WifiAP->MSTimer(); - bool switchOffPowerSaving = false; if (USUntilPowerOn < 0) { USUntilPowerOn += kTimerInterval; - switchOffPowerSaving = (USUntilPowerOn >= 0) && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn); - } - if ((USUntilPowerOn >= 0) && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving)) - { - IOPORT(W_PowerState) = 0; - IOPORT(W_RFPins) = 1; - IOPORT(W_RFPins) = 0x0084; - SetIRQ(11); + if (USUntilPowerOn >= 0) + { + USUntilPowerOn = 0; + + IOPORT(W_PowerState) = 0; + SetStatus(1); + + UpdatePowerStatus(0); + } } if (IOPORT(W_USCountCnt)) @@ -1660,7 +1818,7 @@ void Wifi::USTimer(u32 param) u16 txbusy = IOPORT(W_TXBusy); if (txbusy) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { ComStatus = 0; TXCurSlot = -1; @@ -1695,9 +1853,10 @@ void Wifi::USTimer(u32 param) bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot); if (finished) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { IOPORT(W_TXBusy) = 0; + IOPORT(W_TRXPower) = 0; SetStatus(9); } @@ -1754,8 +1913,9 @@ void Wifi::USTimer(u32 param) RXCounter = 0; } // TODO: proper error management - if ((!ComStatus) && (IOPORT(W_PowerState) & 0x0300)) + if ((!ComStatus) && (IOPORT(W_PowerState) & (1<<9))) { + IOPORT(W_TRXPower) = 0; SetStatus(9); } } @@ -1766,6 +1926,28 @@ void Wifi::USTimer(u32 param) } +void Wifi::ChangeChannel() +{ + u32 val1 = RFRegs[RFChannelIndex[0]]; + u32 val2 = RFRegs[RFChannelIndex[1]]; + + CurChannel = 0; + + for (int i = 0; i < 14; i++) + { + if (val1 == RFChannelData[i][0] && val2 == RFChannelData[i][1]) + { + CurChannel = i+1; + break; + } + } + + if (CurChannel > 0) + Log(LogLevel::Debug, "wifi: switching to channel %d\n", CurChannel); + else + Log(LogLevel::Debug, "wifi: invalid channel values %05X:%05X\n", val1, val2); +} + void Wifi::RFTransfer_Type2() { u32 id = (IOPORT(W_RFData2) >> 2) & 0x1F; @@ -1780,6 +1962,9 @@ void Wifi::RFTransfer_Type2() { u32 data = IOPORT(W_RFData1) | ((IOPORT(W_RFData2) & 0x0003) << 16); RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } @@ -1796,6 +1981,9 @@ void Wifi::RFTransfer_Type3() { u32 data = IOPORT(W_RFData1) & 0xFF; RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } @@ -1819,6 +2007,7 @@ u16 Wifi::Read(u32 addr) switch (addr) { case W_Random: // random generator. not accurate + // TODO: rotate the sequence based on the ARM7 cycle counter (if this is important) Random = (Random & 0x1) ^ (((Random & 0x3FF) << 1) | (Random >> 10)); return Random; @@ -1899,7 +2088,6 @@ u16 Wifi::Read(u32 addr) } } - //printf("WIFI: read %08X\n", addr); return IOPORT(addr&0xFFF); } @@ -1923,28 +2111,20 @@ void Wifi::Write(u32 addr, u16 val) case W_ModeReset: { u16 oldval = IOPORT(W_ModeReset); + IOPORT(W_ModeReset) = val & 0x0001; if (!(oldval & 0x0001) && (val & 0x0001)) { - if (!(USUntilPowerOn < 0 && ForcePowerOn)) - { - //printf("mode reset power on %08x\n", NDS::ARM7->R[15]); - IOPORT(0x034) = 0x0002; - IOPORT(0x27C) = 0x0005; - // TODO: 02A2?? + IOPORT(0x27C) = 0x0005; + // TODO: 02A2?? - if (IOPORT(W_PowerUnk) & 0x0002) - { - USUntilPowerOn = -2048; - IOPORT(W_PowerState) |= 0x100; - } - } + UpdatePowerStatus(0); } else if ((oldval & 0x0001) && !(val & 0x0001)) { - //printf("mode reset shutdown %08x\n", NDS::ARM7->R[15]); IOPORT(0x27C) = 0x000A; - PowerDown(); + + UpdatePowerStatus(0); } if (val & 0x2000) @@ -1986,23 +2166,43 @@ void Wifi::Write(u32 addr, u16 val) IOPORT(0x230) = 0x0047; } } - break; + return; case W_ModeWEP: val &= 0x007F; - //printf("writing mode web %x\n", val); - if ((val & 0x7) == 1) - IOPORT(W_PowerUnk) |= 0x0002; - if ((val & 0x7) == 2) - IOPORT(W_PowerUnk) = 0x0003; - break; + IOPORT(W_ModeWEP) = val; + + if (IOPORT(W_PowerTX) & (1<<1)) + { + if ((val & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((val & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + if ((val & 0x7) != 3) + IOPORT(W_PowerState) &= 0x0300; + UpdatePowerStatus(0); + } + return; + + case W_IE: + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IE) = val; + CheckIRQ(oldflags); + } + return; case W_IF: IOPORT(W_IF) &= ~val; return; case W_IFSet: - IOPORT(W_IF) |= (val & 0xFBFF); - Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IF) |= (val & 0xFBFF); + CheckIRQ(oldflags); + Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + } return; case W_AIDLow: @@ -2012,67 +2212,63 @@ void Wifi::Write(u32 addr, u16 val) IOPORT(W_AIDFull) = val & 0x07FF; return; - case W_PowerState: - //printf("writing power state %x %08x\n", val, NDS::ARM7->R[15]); - IOPORT(W_PowerState) |= val & 0x0002; + case W_PowerUS: + IOPORT(W_PowerUS) = val & 0x0003; + UpdatePowerOn(); + return; - if (IOPORT(W_ModeReset) & 0x0001 && IOPORT(W_PowerState) & 0x0002) + case W_PowerTX: + IOPORT(W_PowerTX) = val & 0x0003; + if (val & (1<<1)) { - /*if (IOPORT(W_PowerState) & 0x100) - { - AlwaysPowerOn = true; - USUntilPowerOn = -1; - } - else */ - if (IOPORT(W_PowerForce) == 1) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + UpdatePowerStatus(0); } return; - case W_PowerForce: - //if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val); + case W_PowerState: + if ((IOPORT(W_ModeWEP) & 0x7) != 3) + return; + + val = (IOPORT(W_PowerState) & 0x0300) | (val & 0x0003); + if ((val & 0x0300) == 0x0200) + val &= ~(1<<0); + else + val &= ~(1<<1); + + if (!(val & (1<<9))) + val &= ~(1<<8); + + IOPORT(W_PowerState) = val; + UpdatePowerStatus(0); + return; + + case W_PowerForce: val &= 0x8001; - //printf("writing power force %x %08x\n", val, NDS::ARM7->R[15]); - if (val == 0x8001) - { - //printf("force power off\n"); - IOPORT(0x034) = 0x0002; - IOPORT(W_PowerState) = 0x0200; - IOPORT(W_TXReqRead) = 0; - PowerDown(); - } - if (val == 1 && IOPORT(W_PowerState) & 0x0002) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } - if (val == 0x8000) + IOPORT(W_PowerForce) = val; + UpdatePowerStatus(0); + return; + + case W_PowerDownCtrl: + IOPORT(W_PowerDownCtrl) = val & 0x0003; + + if (IOPORT(W_PowerTX) & (1<<1)) { - //printf("force power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = true; + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; } - break; - case W_PowerUS: - IOPORT(W_PowerUS) = val & 0x0003; - UpdatePowerOn(); + + if (val != 0 && val != 3) + Log(LogLevel::Warn, "wifi: unusual W_PowerDownCtrl value %04X\n", val); + + UpdatePowerStatus(0); return; - case W_PowerUnk: - val &= 0x0003; - //printf("writing power unk %x\n", val); - if ((IOPORT(W_ModeWEP) & 0x7) == 1) - val |= 2; - else if ((IOPORT(W_ModeWEP) & 0x7) == 2) - val = 3; - break; case W_USCountCnt: val &= 0x0001; break; case W_USCompareCnt: @@ -2231,6 +2427,7 @@ void Wifi::Write(u32 addr, u16 val) // read-only ports case 0x000: + case 0x034: case 0x044: case 0x054: case 0x098: |