diff options
author | Alexander Polakov <plhk@sdf.org> | 2013-07-09 16:16:28 +0400 |
---|---|---|
committer | Alexander Polakov <plhk@sdf.org> | 2013-07-09 16:16:28 +0400 |
commit | 6cf9d283ed93507492d34e59ae6541951a668432 (patch) | |
tree | 95c7d494dfd30f0b9a8594e9cfafde6f29d05477 | |
parent | 22cc50307ba856e69757ade333e50cdaca83a8c2 (diff) |
Add Ralink RT2860 wireless driver
-rw-r--r-- | sys/src/9/pc/etherrt2860.c | 3019 | ||||
-rw-r--r-- | sys/src/9/pc/mkfile | 1 | ||||
-rw-r--r-- | sys/src/9/pc/pccpuf | 1 | ||||
-rw-r--r-- | sys/src/9/pc/pcf | 1 |
4 files changed, 3022 insertions, 0 deletions
diff --git a/sys/src/9/pc/etherrt2860.c b/sys/src/9/pc/etherrt2860.c new file mode 100644 index 000000000..426aa18b4 --- /dev/null +++ b/sys/src/9/pc/etherrt2860.c @@ -0,0 +1,3019 @@ +/* + * Ralink RT2860 driver + * + * Written without any documentation but Damien Bergaminis + * OpenBSD ral(4) driver sources. Requires ralink firmware + * to be present in /lib/firmware/ral-rt2860 on attach. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "wifi.h" + +/* for consistency */ +typedef signed char s8int; + +enum { + /* PCI registers */ + PciCfg = 0x0000, + PciCfgUsb = (1 << 17), + PciCfgPci = (1 << 16), + PciEectrl = 0x0004, + EectrlC = (1 << 0), + EectrlS = (1 << 1), + EectrlD = (1 << 2), + EectrlShiftD = 2, + EectrlQ = (1 << 3), + EectrlShiftQ = 3, + PciMcuctrl = 0x0008, + PciSysctrl = 0x000c, + PcieJtag = 0x0010, + + Rt3090AuxCtrl = 0x010c, + + Rt3070Opt14 = 0x0114, +}; + +enum { + /* SCH/DMA registers */ + IntStatus = 0x0200, + /* flags for registers IntStatus/IntMask */ + TxCoherent = (1 << 17), + RxCoherent = (1 << 16), + MacInt4 = (1 << 15), + MacInt3 = (1 << 14), + MacInt2 = (1 << 13), + MacInt1 = (1 << 12), + MacInt0 = (1 << 11), + TxRxCoherent = (1 << 10), + McuCmdInt = (1 << 9), + TxDoneInt5 = (1 << 8), + TxDoneInt4 = (1 << 7), + TxDoneInt3 = (1 << 6), + TxDoneInt2 = (1 << 5), + TxDoneInt1 = (1 << 4), + TxDoneInt0 = (1 << 3), + RxDoneInt = (1 << 2), + TxDlyInt = (1 << 1), + RxDlyInt = (1 << 0), + IntMask = 0x0204, + WpdmaGloCfg = 0x0208, + HdrSegLenShift = 8, + BigEndian = (1 << 7), + TxWbDdone = (1 << 6), + WpdmaBtSizeShift = 4, + WpdmaBtSize16 = 0, + WpdmaBtSize32 = 1, + WpdmaBtSize64 = 2, + WpdmaBtSize128 = 3, + RxDmaBusy = (1 << 3), + RxDmaEn = (1 << 2), + TxDmaBusy = (1 << 1), + TxDmaEn = (1 << 0), + WpdmaRstIdx = 0x020c, + DelayIntCfg = 0x0210, + TxdlyIntEn = (1 << 31), + TxmaxPintShift = 24, + TxmaxPtimeShift = 16, + RxdlyIntEn = (1 << 15), + RxmaxPintShift = 8, + RxmaxPtimeShift = 0, + WmmAifsnCfg = 0x0214, + WmmCwminCfg = 0x0218, + WmmCwmaxCfg = 0x021c, + WmmTxop0Cfg = 0x0220, + WmmTxop1Cfg = 0x0224, + GpioCtrl = 0x0228, + GpioDShift = 8, + GpioOShift = 0, + McuCmdReg = 0x022c, +#define TxBasePtr(qid) (0x0230 + (qid) * 16) +#define TxMaxCnt(qid) (0x0234 + (qid) * 16) +#define TxCtxIdx(qid) (0x0238 + (qid) * 16) +#define TxDtxIdx(qid) (0x023c + (qid) * 16) + RxBasePtr = 0x0290, + RxMaxCnt = 0x0294, + RxCalcIdx = 0x0298, + FsDrxIdx = 0x029c, + UsbDmaCfg = 0x02a0 /* RT2870 only */, + UsbTxBusy = (1 << 31), + UsbRxBusy = (1 << 30), + UsbEpoutVldShift = 24, + UsbTxEn = (1 << 23), + UsbRxEn = (1 << 22), + UsbRxAggEn = (1 << 21), + UsbTxopHalt = (1 << 20), + UsbTxClear = (1 << 19), + UsbPhyWdEn = (1 << 16), + UsbPhyManRst = (1 << 15), +#define UsbRxAggLmt(x) ((x) << 8) /* in unit of 1KB */ +#define UsbRxAggTo(x) ((x) & 0xff) /* in unit of 33ns */ + UsCycCnt = 0x02a4, + TestEn = (1 << 24), + TestSelShift = 16, + BtModeEn = (1 << 8), + UsCycCntShift = 0, +}; + +enum { + /* PBF registers */ + SysCtrl = 0x0400, + HstPmSel = (1 << 16), + CapMode = (1 << 14), + PmeOen = (1 << 13), + Clkselect = (1 << 12), + PbfClkEn = (1 << 11), + MacClkEn = (1 << 10), + DmaClkEn = (1 << 9), + McuReady = (1 << 7), + AsyReset = (1 << 4), + PbfReset = (1 << 3), + MacReset = (1 << 2), + DmaReset = (1 << 1), + McuReset = (1 << 0), + HostCmd = 0x0404, + McuCmdSleep = 0x30, + McuCmdWakeup = 0x31, + McuCmdLeds = 0x50, + LedRadio = (1 << 13), + LedLink2ghz = (1 << 14), + LedLink5ghz = (1 << 15), + McuCmdLedRssi = 0x51, + McuCmdLed1 = 0x52, + McuCmdLed2 = 0x53, + McuCmdLed3 = 0x54, + McuCmdRfreset = 0x72, + McuCmdAntsel = 0x73, + McuCmdBbp = 0x80, + McuCmdPslevel = 0x83, + PbfCfg = 0x0408, + Tx1qNumShift = 21, + Tx2qNumShift = 16, + Null0Mode = (1 << 15), + Null1Mode = (1 << 14), + RxDropMode = (1 << 13), + Tx0qManual = (1 << 12), + Tx1qManual = (1 << 11), + Tx2qManual = (1 << 10), + Rx0qManual = (1 << 9), + HccaEn = (1 << 8), + Tx0qEn = (1 << 4), + Tx1qEn = (1 << 3), + Tx2qEn = (1 << 2), + Rx0qEn = (1 << 1), + MaxPcnt = 0x040c, + BufCtrl = 0x0410, +#define WriteTxq(qid) (1 << (11 - (qid))) + Null0Kick = (1 << 7), + Null1Kick = (1 << 6), + BufReset = (1 << 5), +#define ReadTxq(qid) = (1 << (3 - (qid)) + ReadRx0q = (1 << 0), + McuIntSta = 0x0414, + /* flags for registers McuIntSta/McuIntEna */ + McuMacInt8 = (1 << 24), + McuMacInt7 = (1 << 23), + McuMacInt6 = (1 << 22), + McuMacInt4 = (1 << 20), + McuMacInt3 = (1 << 19), + McuMacInt2 = (1 << 18), + McuMacInt1 = (1 << 17), + McuMacInt0 = (1 << 16), + Dtx0Int = (1 << 11), + Dtx1Int = (1 << 10), + Dtx2Int = (1 << 9), + Drx0Int = (1 << 8), + HcmdInt = (1 << 7), + N0txInt = (1 << 6), + N1txInt = (1 << 5), + BcntxInt = (1 << 4), + Mtx0Int = (1 << 3), + Mtx1Int = (1 << 2), + Mtx2Int = (1 << 1), + Mrx0Int = (1 << 0), + McuIntEna = 0x0418, +#define TxqIo(qid) (0x041c + (qid) * 4) + Rx0qIo = 0x0424, + BcnOffset0 = 0x042c, + BcnOffset1 = 0x0430, + TxrxqSta = 0x0434, + TxrxqPcnt = 0x0438, + Rx0qPcntMask = 0xff000000, + Tx2qPcntMask = 0x00ff0000, + Tx1qPcntMask = 0x0000ff00, + Tx0qPcntMask = 0x000000ff, + PbfDbg = 0x043c, + CapCtrl = 0x0440, + CapAdcFeq = (1 << 31), + CapStart = (1 << 30), + ManTrig = (1 << 29), + TrigOffsetShift = 16, + StartAddrShift = 0, +}; + +enum { + /* RT3070 registers */ + Rt3070RfCsrCfg = 0x0500, + Rt3070RfKick = (1 << 17), + Rt3070RfWrite = (1 << 16), + Rt3070EfuseCtrl = 0x0580, + Rt3070SelEfuse = (1 << 31), + Rt3070EfsromKick = (1 << 30), + Rt3070EfsromAinMask = 0x03ff0000, + Rt3070EfsromAinShift = 16, + Rt3070EfsromModeMask = 0x000000c0, + Rt3070EfuseAoutMask = 0x0000003f, + Rt3070EfuseData0 = 0x0590, + Rt3070EfuseData1 = 0x0594, + Rt3070EfuseData2 = 0x0598, + Rt3070EfuseData3 = 0x059c, + Rt3090OscCtrl = 0x05a4, + Rt3070LdoCfg0 = 0x05d4, + Rt3070GpioSwitch = 0x05dc, +}; + +enum { + /* MAC registers */ + AsicVerId = 0x1000, + MacSysCtrl = 0x1004, + RxTsEn = (1 << 7), + WlanHaltEn = (1 << 6), + PbfLoopEn = (1 << 5), + ContTxTest = (1 << 4), + MacRxEn = (1 << 3), + MacTxEn = (1 << 2), + BbpHrst = (1 << 1), + MacSrst = (1 << 0), + MacAddrDw0 = 0x1008, + MacAddrDw1 = 0x100c, + MacBssidDw0 = 0x1010, + MacBssidDw1 = 0x1014, + MultiBcnNumShift = 18, + MultiBssidModeShift = 16, + MaxLenCfg = 0x1018, + MinMpduLenShift = 16, + MaxPsduLenShift = 12, + MaxPsduLen8k = 0, + MaxPsduLen16k = 1, + MaxPsduLen32k = 2, + MaxPsduLen64k = 3, + MaxMpduLenShift = 0, + BbpCsrCfg = 0x101c, + BbpRwParallel = (1 << 19), + BbpParDur1125 = (1 << 18), + BbpCsrKick = (1 << 17), + BbpCsrRead = (1 << 16), + BbpAddrShift = 8, + BbpDataShift = 0, + RfCsrCfg0 = 0x1020, + RfRegCtrl = (1 << 31), + RfLeSel1 = (1 << 30), + RfLeStby = (1 << 29), + RfRegWidthShift = 24, + RfReg0Shift = 0, + RfCsrCfg1 = 0x1024, + RfDur5 = (1 << 24), + RfReg1Shift = 0, + RfCsrCfg2 = 0x1028, + LedCfg = 0x102c, + LedPol = (1 << 30), + YLedModeShift = 28, + GLedModeShift = 26, + RLedModeShift = 24, + LedModeOff = 0, + LedModeBlinkTx = 1, + LedModeSlowBlink = 2, + LedModeOn = 3, + SlowBlkTimeShift = 16, + LedOffTimeShift = 8, + LedOnTimeShift = 0, +}; + +enum { + /* undocumented registers */ + Debug = 0x10f4, +}; + +enum { + /* MAC Timing control registers */ + XifsTimeCfg = 0x1100, + BbRxendEn = (1 << 29), + EifsTimeShift = 20, + OfdmXifsTimeShift = 16, + OfdmSifsTimeShift = 8, + CckSifsTimeShift = 0, + BkoffSlotCfg = 0x1104, + CcDelayTimeShift = 8, + SlotTime = 0, + NavTimeCfg = 0x1108, + NavUpd = (1 << 31), + NavUpdValShift = 16, + NavClrEn = (1 << 15), + NavTimerShift = 0, + ChTimeCfg = 0x110c, + EifsAsChBusy = (1 << 4), + NavAsChBusy = (1 << 3), + RxAsChBusy = (1 << 2), + TxAsChBusy = (1 << 1), + ChStaTimerEn = (1 << 0), + PbfLifeTimer = 0x1110, + BcnTimeCfg = 0x1114, + TsfInsCompShift = 24, + BcnTxEn = (1 << 20), + TbttTimerEn = (1 << 19), + TsfSyncModeShift = 17, + TsfSyncModeDis = 0, + TsfSyncModeSta = 1, + TsfSyncModeIbss = 2, + TsfSyncModeHostap = 3, + TsfTimerEn = (1 << 16), + BcnIntvalShift = 0, + TbttSyncCfg = 0x1118, + BcnCwminShift = 20, + BcnAifsnShift = 16, + BcnExpWinShift = 8, + TbttAdjustShift = 0, + TsfTimerDw0 = 0x111c, + TsfTimerDw1 = 0x1120, + TbttTimer = 0x1124, + IntTimerCfg = 0x1128, + GpTimerShift = 16, + PreTbttTimerShift = 0, + IntTimerEn = 0x112c, + GpTimerEn = (1 << 1), + PreTbttIntEn = (1 << 0), + ChIdleTime = 0x1130, +}; + +enum { + /* MAC Power Save configuration registers */ + MacStatusReg = 0x1200, + RxStatusBusy = (1 << 1), + TxStatusBusy = (1 << 0), + PwrPinCfg = 0x1204, + IoAddaPd = (1 << 3), + IoPllPd = (1 << 2), + IoRaPe = (1 << 1), + IoRfPe = (1 << 0), + AutoWakeupCfg = 0x1208, + AutoWakeupEn = (1 << 15), + SleepTbttNumShift = 8, + WakeupLeadTimeShift = 0, +}; + +enum { + /* MAC TX configuration registers */ +#define EdcaAcCfg(aci) (0x1300 + (aci) * 4) + EdcaTidAcMap = 0x1310, +#define TxPwrCfg(ridx) (0x1314 + (ridx) * 4) + TxPinCfg = 0x1328, + Rt3593LnaPeG2Pol = (1 << 31), + Rt3593LnaPeA2Pol = (1 << 30), + Rt3593LnaPeG2En = (1 << 29), + Rt3593LnaPeA2En = (1 << 28), + Rt3593LnaPe2En = (Rt3593LnaPeA2En | Rt3593LnaPeG2En), + Rt3593PaPeG2Pol = (1 << 27), + Rt3593PaPeA2Pol = (1 << 26), + Rt3593PaPeG2En = (1 << 25), + Rt3593PaPeA2En = (1 << 24), + TrswPol = (1 << 19), + TrswEn = (1 << 18), + RftrPol = (1 << 17), + RftrEn = (1 << 16), + LnaPeG1Pol = (1 << 15), + LnaPeA1Pol = (1 << 14), + LnaPeG0Pol = (1 << 13), + LnaPeA0Pol = (1 << 12), + LnaPeG1En = (1 << 11), + LnaPeA1En = (1 << 10), + LnaPe1En = (LnaPeA1En | LnaPeG1En), + LnaPeG0En = (1 << 9), + LnaPeA0En = (1 << 8), + LnaPe0En = (LnaPeA0En | LnaPeG0En), + PaPeG1Pol = (1 << 7), + PaPeA1Pol = (1 << 6), + PaPeG0Pol = (1 << 5), + PaPeA0Pol = (1 << 4), + PaPeG1En = (1 << 3), + PaPeA1En = (1 << 2), + PaPeG0En = (1 << 1), + PaPeA0En = (1 << 0), + TxBandCfg = 0x132c, + Tx5gBandSelN = (1 << 2), + Tx5gBandSelP = (1 << 1), + TxBandSel = (1 << 0), + TxSwCfg0 = 0x1330, + DlyRftrEnShift = 24, + DlyTrswEnShift = 16, + DlyPapeEnShift = 8, + DlyTxpeEnShift = 0, + TxSwCfg1 = 0x1334, + DlyRftrDisShift = 16, + DlyTrswDisShift = 8, + DlyPapeDisShift = 0, + TxSwCfg2 = 0x1338, + DlyLnaEnShift = 24, + DlyLnaDisShift = 16, + DlyDacEnShift = 8, + DlyDacDisShift = 0, + TxopThresCfg = 0x133c, + TxopRemThresShift = 24, + CfEndThresShift = 16, + RdgInThres = 8, + RdgOutThres = 0, + TxopCtrlCfg = 0x1340, + ExtCwMinShift = 16, + ExtCcaDlyShift = 8, + ExtCcaEn = (1 << 7), + LsigTxopEn = (1 << 6), + TxopTrunEnMimops = (1 << 4), + TxopTrunEnTxop = (1 << 3), + TxopTrunEnRate = (1 << 2), + TxopTrunEnAc = (1 << 1), + TxopTrunEnTimeout = (1 << 0), + TxRtsCfg = 0x1344, + RtsFbkEn = (1 << 24), + RtsThresShift = 8, + RtsRtyLimitShift = 0, + TxTimeoutCfg = 0x1348, + TxopTimeoutShift = 16, + RxAckTimeoutShift = 8, + MpduLifeTimeShift = 4, + TxRtyCfg = 0x134c, + TxAutofbEn = (1 << 30), + AggRtyModeTimer = (1 << 29), + NagRtyModeTimer = (1 << 28), + LongRtyThresShift = 16, + LongRtyLimitShift = 8, + ShortRtyLimitShift = 0, + TxLinkCfg = 0x1350, + RemoteMfsShift = 24, + RemoteMfbShift = 16, + TxCfackEn = (1 << 12), + TxRdgEn = (1 << 11), + TxMrqEn = (1 << 10), + RemoteUmfsEn = (1 << 9), + TxMfbEn = (1 << 8), + RemoteMfbLtShift = 0, + HtFbkCfg0 = 0x1354, + HtFbkCfg1 = 0x1358, + LgFbkCfg0 = 0x135c, + LgFbkCfg1 = 0x1360, + CckProtCfg = 0x1364, + /* possible flags for registers *ProtCfg */ + RtsthEn = (1 << 26), + TxopAllowGf40 = (1 << 25), + TxopAllowGf20 = (1 << 24), + TxopAllowMm40 = (1 << 23), + TxopAllowMm20 = (1 << 22), + TxopAllowOfdm = (1 << 21), + TxopAllowCck = (1 << 20), + TxopAllowAll = (0x3f << 20), + ProtNavShort = (1 << 18), + ProtNavLong = (2 << 18), + ProtCtrlRtsCts = (1 << 16), + ProtCtrlCts = (2 << 16), + OfdmProtCfg = 0x1368, + Mm20ProtCfg = 0x136c, + Mm40ProtCfg = 0x1370, + Gf20ProtCfg = 0x1374, + Gf40ProtCfg = 0x1378, + ExpCtsTime = 0x137c, + /* possible flags for registers EXP_{CTS,ACK}_TIME */ + ExpOfdmTimeShift = 16, + ExpCckTimeShift = 0, + ExpAckTime = 0x1380, +}; + +enum { + /* MAC RX configuration registers */ + RxFiltrCfg = 0x1400, + DropCtrlRsv = (1 << 16), + DropBar = (1 << 15), + DropBa = (1 << 14), + DropPspoll = (1 << 13), + DropRts = (1 << 12), + DropCts = (1 << 11), + DropAck = (1 << 10), + DropCfend = (1 << 9), + DropCfack = (1 << 8), + DropDupl = (1 << 7), + DropBc = (1 << 6), + DropMc = (1 << 5), + DropVerErr = (1 << 4), + DropNotMybss = (1 << 3), + DropUcNome = (1 << 2), + DropPhyErr = (1 << 1), + DropCrcErr = (1 << 0), + AutoRspCfg = 0x1404, + CtrlPwrBit = (1 << 7), + BacAckPolicy = (1 << 6), + CckShortEn = (1 << 4), + Cts40mRefEn = (1 << 3), + Cts40mModeEn = (1 << 2), + BacAckpolicyEn = (1 << 1), + AutoRspEn = (1 << 0), + LegacyBasicRate = 0x1408, + HtBasicRate = 0x140c, + HtCtrlCfg = 0x1410, + SifsCostCfg = 0x1414, + OfdmSifsCostShift = 8, + CckSifsCostShift = 0, + RxParserCfg = 0x1418, +}; + +enum { + /* MAC Security configuration registers */ + TxSecCnt0 = 0x1500, + RxSecCnt0 = 0x1504, + CcmpFcMute = 0x1508, +}; + +enum { + /* MAC HCCA/PSMP configuration registers */ + TxopHldrAddr0 = 0x1600, + TxopHldrAddr1 = 0x1604, + TxopHldrEt = 0x1608, + TxopEtm1En = (1 << 25), + TxopEtm0En = (1 << 24), + TxopEtmThresShift = 16, + TxopEtoEn = (1 << 8), + TxopEtoThresShift = 1, + PerRxRstEn = (1 << 0), + QosCfpollRaDw0 = 0x160c, + QosCfpollA1Dw1 = 0x1610, + QosCfpollQc = 0x1614, +}; + +enum { + /* MAC Statistics Counters */ + RxStaCnt0 = 0x1700, + RxStaCnt1 = 0x1704, + RxStaCnt2 = 0x1708, + TxStaCnt0 = 0x170c, + TxStaCnt1 = 0x1710, + TxStaCnt2 = 0x1714, + TxStatFifo = 0x1718, + TxqMcsShift = 16, + TxqWcidShift = 8, + TxqAckreq = (1 << 7), + TxqAgg = (1 << 6), + TxqOk = (1 << 5), + TxqPidShift = 1, + TxqVld = (1 << 0), +}; + +/* RX WCID search table */ +#define WcidEntry(wcid) (0x1800 + (wcid) * 8) + +enum { + FwBase = 0x2000, + Rt2870FwBase = 0x3000, +}; + +/* Pair-wise key table */ +#define Pkey(wcid) (0x4000 + (wcid) * 32) + +/* IV/EIV table */ +#define Iveiv(wcid) (0x6000 + (wcid) * 8) + +/* WCID attribute table */ +#define WcidAttr(wcid) (0x6800 + (wcid) * 4) + +/* possible flags for register WCID_ATTR */ +enum { + ModeNosec = 0, + ModeWep40 = 1, + ModeWep104 = 2, + ModeTkip = 3, + ModeAesCcmp = 4, + ModeCkip40 = 5, + ModeCkip104 = 6, + ModeCkip128 = 7, + RxPkeyEn = (1 << 0), +}; + +/* Shared Key Table */ +#define Skey(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Key Mode */ +enum { + SkeyMode07 = 0x7000, + SkeyMode815 = 0x7004, + SkeyMode1623 = 0x7008, + SkeyMode2431 = 0x700c, +}; + +enum { + /* Shared Memory between MCU and host */ + H2mMailbox = 0x7010, + H2mBusy = (1 << 24), + TokenNoIntr = 0xff, + H2mMailboxCid = 0x7014, + H2mMailboxStatus = 0x701c, + H2mBbpagent = 0x7028, +#define BcnBase(vap) (0x7800 + (vap) * 512) +}; + +/* + * RT2860 TX descriptor + * -------------------- + * u32int sdp0 Segment Data Pointer 0 + * u16int sdl1 Segment Data Length 1 + * u16int sdl0 Segment Data Length 0 + * u32int sdp1 Segment Data Pointer 1 + * u8int reserved[3] + * u8int flags + */ + +enum { + /* sdl1 flags */ + TxBurst = (1 << 15), + TxLs1 = (1 << 14) /* SDP1 is the last segment */, + /* sdl0 flags */ + TxDdone = (1 << 15), + TxLs0 = (1 << 14) /* SDP0 is the last segment */, + /* flags */ + TxQselShift = 1, + TxQselMgmt = (0 << 1), + TxQselHcca = (1 << 1), + TxQselEdca = (2 << 1), + TxWiv = (1 << 0), +}; + +/* + * TX Wireless Information + * ----------------------- + * u8int flags + * u8int txop + * u16int phy + * u8int xflags + * u8int wcid Wireless Client ID + * u16int len + * u32int iv + * u32int eiv + */ + +enum { + /* flags */ + TxMpduDsityShift = 5, + TxAmpdu = (1 << 4), + TxTs = (1 << 3), + TxCfack = (1 << 2), + TxMmps = (1 << 1), + TxFrag = (1 << 0), + /* txop */ + TxTxopHt = 0, + TxTxopPifs = 1, + TxTxopSifs = 2, + TxTxopBackoff = 3, + /* phy */ + PhyMode = 0xc000, + PhyCck = (0 << 14), + PhyOfdm = (1 << 14), + PhyHt = (2 << 14), + PhyHtGf = (3 << 14), + PhySgi = (1 << 8), + PhyBw40 = (1 << 7), + PhyMcs = 0x7f, + PhyShpre = (1 << 3), + /* xflags */ + TxBawinsizeShift = 2, + TxNseq = (1 << 1), + TxAck = (1 << 0), + /* len */ + TxPidShift = 12, +}; + +/* + * RT2860 RX descriptor + * -------------------- + * u32int sdp0 + * u16int sdl1 unused + * u16int sdl0 + * u32int sdp1 unused + * u32int flags + */ + +enum { + /* sdl flags */ + RxDdone = (1 << 15), + RxLs0 = (1 << 14), + /* flags */ + RxDec = (1 << 16), + RxAmpdu = (1 << 15), + RxL2pad = (1 << 14), + RxRssi = (1 << 13), + RxHtc = (1 << 12), + RxAmsdu = (1 << 11), + RxMicerr = (1 << 10), + RxIcverr = (1 << 9), + RxCrcerr = (1 << 8), + RxMybss = (1 << 7), + RxBc = (1 << 6), + RxMc = (1 << 5), + RxUc2me = (1 << 4), + RxFrag = (1 << 3), + RxNull = (1 << 2), + RxData = (1 << 1), + RxBa = (1 << 0), +}; + +/* + * RX Wireless Information + * ----------------------- + * u8int wcid + * u8int keyidx + * u16int len + * u16int seq + * u16int phy + * u8int rssi[3] + * u8int reserved1 + * u8int snr[2] + * u16int reserved2 + */ + +enum { + /* keyidx flags */ + RxUdfShift = 5, + RxBssIdxShift = 2, + /* len flags */ + RxTidShift = 12, +}; + +enum { + WIFIHDRSIZE = 2+2+3*6+2, + Rdscsize = 16, + Tdscsize = 16, + Rbufsize = 4096, + Tbufsize = 4096, + Rxwisize = 16, + Txwisize = 16, + /* first DMA segment contains TXWI + 802.11 header + 32-bit padding */ + TxwiDmaSz = Txwisize + WIFIHDRSIZE + 2 +}; + +/* RF registers */ +enum { + Rf1 = 0, + Rf2 = 2, + Rf3 = 1, + Rf4 = 3, +}; + +enum { + Rf2820 = 1 /* 2T3R */, + Rf2850 = 2 /* dual-band 2T3R */, + Rf2720 = 3 /* 1T2R */, + Rf2750 = 4 /* dual-band 1T2R */, + Rf3020 = 5 /* 1T1R */, + Rf2020 = 6 /* b/g */, + Rf3021 = 7 /* 1T2R */, + Rf3022 = 8 /* 2T2R */, + Rf3052 = 9 /* dual-band 2T2R */, + Rf3320 = 11 /* 1T1R */, + Rf3053 = 13 /* dual-band 3T3R */, +}; + +static const char* rfnames[] = { + [Rf2820] "RT2820", + [Rf2850] "RT2850", + [Rf2720] "RT2720", + [Rf2750] "RT2750", + [Rf3020] "RT3020", + [Rf2020] "RT2020", + [Rf3021] "RT3021", + [Rf3022] "RT3022", + [Rf3052] "RT3052", + [Rf3320] "RT3320", + [Rf3053] "RT3053", +}; + +enum { + /* USB commands, RT2870 only */ + Rt2870Reset = 1, + Rt2870Write2 = 2, + Rt2870WriteRegion1 = 6, + Rt2870ReadRegion1 = 7, + Rt2870EepromRead = 9, +}; + +enum { + EepromDelay = 1 /* minimum hold time (microsecond) */, + + EepromVersion = 0x01, + EepromMac01 = 0x02, + EepromMac23 = 0x03, + EepromMac45 = 0x04, + EepromPciePslevel = 0x11, + EepromRev = 0x12, + EepromAntenna = 0x1a, + EepromConfig = 0x1b, + EepromCountry = 0x1c, + EepromFreqLeds = 0x1d, + EepromLed1 = 0x1e, + EepromLed2 = 0x1f, + EepromLed3 = 0x20, + EepromLna = 0x22, + EepromRssi12ghz = 0x23, + EepromRssi22ghz = 0x24, + EepromRssi15ghz = 0x25, + EepromRssi25ghz = 0x26, + EepromDeltapwr = 0x28, + EepromPwr2ghzBase1 = 0x29, + EepromPwr2ghzBase2 = 0x30, + EepromTssi12ghz = 0x37, + EepromTssi22ghz = 0x38, + EepromTssi32ghz = 0x39, + EepromTssi42ghz = 0x3a, + EepromTssi52ghz = 0x3b, + EepromPwr5ghzBase1 = 0x3c, + EepromPwr5ghzBase2 = 0x53, + EepromTssi15ghz = 0x6a, + EepromTssi25ghz = 0x6b, + EepromTssi35ghz = 0x6c, + EepromTssi45ghz = 0x6d, + EepromTssi55ghz = 0x6e, + EepromRpwr = 0x6f, + EepromBbpBase = 0x78, + Rt3071EepromRfBase = 0x82, +}; + +enum { + RidxCck1 = 0, + RidxCck11 = 3, + RidxOfdm6 = 4, + RidxMax = 11, +}; + +/* ring and pool count */ +enum { + Nrx = 128, + Ntx = 64, + Ntxpool = Ntx * 2 +}; + +typedef struct FWImage FWImage; +typedef struct TXQ TXQ; +typedef struct RXQ RXQ; +typedef struct Pool Pool; + +typedef struct Ctlr Ctlr; + +struct FWImage { + uint size; + uchar *data; +}; + +struct TXQ +{ + uint n; /* next */ + uint i; /* current */ + Block **b; + u32int *d; /* descriptors */ + + Rendez; + QLock; +}; + +struct RXQ +{ + uint i; + Block **b; + u32int *p; +}; + +struct Pool +{ + uint i; /* current */ + uchar *p; /* txwi */ +}; + +struct Ctlr { + Lock; + QLock; + + Ctlr *link; + Pcidev *pdev; + Wifi *wifi; + + u16int mac_ver; + u16int mac_rev; + u8int rf_rev; + u8int freq; + u8int ntxchains; + u8int nrxchains; + u8int pslevel; + s8int txpow1[54]; + s8int txpow2[54]; + s8int rssi_2ghz[3]; + s8int rssi_5ghz[3]; + u8int lna[4]; + u8int rf24_20mhz; + u8int rf24_40mhz; + u8int patch_dac; + u8int rfswitch; + u8int ext_2ghz_lna; + u8int ext_5ghz_lna; + u8int calib_2ghz; + u8int calib_5ghz; + u8int txmixgain_2ghz; + u8int txmixgain_5ghz; + u8int tssi_2ghz[9]; + u8int tssi_5ghz[9]; + u8int step_2ghz; + u8int step_5ghz; + uint mgtqid; + + struct { + u8int reg; + u8int val; + } bbp[8], rf[10]; + u8int leds; + u16int led[3]; + u32int txpow20mhz[5]; + u32int txpow40mhz_2ghz[5]; + u32int txpow40mhz_5ghz[5]; + + int flags; + + int port; + int power; + int active; + int broken; + int attached; + + u32int *nic; + + /* assigned node ids in hardware node table or -1 if unassigned */ + int bcastnodeid; + int bssnodeid; + u8int wcid; + /* current receiver settings */ + uchar bssid[Eaddrlen]; + int channel; + int prom; + int aid; + + RXQ rx; + TXQ tx[6]; + Pool pool; + + FWImage *fw; +}; + +/* controller flags */ +enum { + AdvancedPs = 1 << 0, + ConnPciE = 1 << 1, +}; + + +static const struct rt2860_rate { + u8int rate; + u8int mcs; + /*enum ieee80211_phytype phy;*/ + u8int ctl_ridx; + u16int sp_ack_dur; + u16int lp_ack_dur; +} rt2860_rates[] = { + { 2, 0,/* IEEE80211_T_DS,*/ 0, 314, 314 }, + { 4, 1,/* IEEE80211_T_DS,*/ 1, 258, 162 }, + { 11, 2,/* IEEE80211_T_DS,*/ 2, 223, 127 }, + { 22, 3,/* IEEE80211_T_DS,*/ 3, 213, 117 }, + { 12, 0,/* IEEE80211_T_OFDM,*/ 4, 60, 60 }, + { 18, 1,/* IEEE80211_T_OFDM,*/ 4, 52, 52 }, + { 24, 2,/* IEEE80211_T_OFDM,*/ 6, 48, 48 }, + { 36, 3,/* IEEE80211_T_OFDM,*/ 6, 44, 44 }, + { 48, 4,/* IEEE80211_T_OFDM,*/ 8, 44, 44 }, + { 72, 5,/* IEEE80211_T_OFDM,*/ 8, 40, 40 }, + { 96, 6,/* IEEE80211_T_OFDM,*/ 8, 40, 40 }, + { 108, 7,/* IEEE80211_T_OFDM,*/ 8, 40, 40 } +}; + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +static const struct { + u32int reg; + u32int val; +} rt2860_def_mac[] = { + { BcnOffset0, 0xf8f0e8e0 }, + { LegacyBasicRate, 0x0000013f }, + { HtBasicRate, 0x00008003 }, + { MacSysCtrl, 0x00000000 }, + { BkoffSlotCfg, 0x00000209 }, + { TxSwCfg0, 0x00000000 }, + { TxSwCfg1, 0x00080606 }, + { TxLinkCfg, 0x00001020 }, + { TxTimeoutCfg, 0x000a2090 }, + { LedCfg, 0x7f031e46 }, + { WmmAifsnCfg, 0x00002273 }, + { WmmCwminCfg, 0x00002344 }, + { WmmCwmaxCfg, 0x000034aa }, + { MaxPcnt, 0x1f3fbf9f }, + { TxRtyCfg, 0x47d01f0f }, + { AutoRspCfg, 0x00000013 }, + { CckProtCfg, 0x05740003 }, + { OfdmProtCfg, 0x05740003 }, + { Gf20ProtCfg, 0x01744004 }, + { Gf40ProtCfg, 0x03f44084 }, + { Mm20ProtCfg, 0x01744004 }, + { Mm40ProtCfg, 0x03f54084 }, + { TxopCtrlCfg, 0x0000583f }, + { TxopHldrEt, 0x00000002 }, + { TxRtsCfg, 0x00092b20 }, + { ExpAckTime, 0x002400ca }, + { XifsTimeCfg, 0x33a41010 }, + { PwrPinCfg, 0x00000003 }, +}; + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +static const struct { + u8int reg; + u8int val; +} rt2860_def_bbp[] = { + { 65, 0x2c }, + { 66, 0x38 }, + { 69, 0x12 }, + { 70, 0x0a }, + { 73, 0x10 }, + { 81, 0x37 }, + { 82, 0x62 }, + { 83, 0x6a }, + { 84, 0x99 }, + { 86, 0x00 }, + { 91, 0x04 }, + { 92, 0x00 }, + { 103, 0x00 }, + { 105, 0x05 }, + { 106, 0x35 }, +}; + +/* + * Default settings for RF registers; values derived from the reference driver. + */ +static const struct rfprog { + u8int chan; + u32int r1, r2, r3, r4; +} rt2860_rf2850[] = { + { 1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 }, + { 2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 }, + { 3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 }, + { 4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 }, + { 5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 }, + { 6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 }, + { 7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 }, + { 8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 }, + { 9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 }, + { 10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 }, + { 11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 }, + { 12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 }, + { 13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 }, + { 14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 }, + { 36, 0x100bb3, 0x130266, 0x056014, 0x001408 }, + { 38, 0x100bb3, 0x130267, 0x056014, 0x001404 }, + { 40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 }, + { 44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 }, + { 46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 }, + { 48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 }, + { 52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 }, + { 54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 }, + { 56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 }, + { 60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 }, + { 62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 }, + { 64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 }, + { 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 }, + { 102, 0x100bb2, 0x1701ac, 0x15e014, 0x001404 }, + { 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 }, + { 108, 0x100bb3, 0x17028c, 0x15e014, 0x001404 }, + { 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 }, + { 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 }, + { 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 }, + { 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 }, + { 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 }, + { 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 }, + { 126, 0x100bb1, 0x1300e0, 0x05e014, 0x001406 }, + { 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 }, + { 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 }, + { 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 }, + { 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 }, + { 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 }, + { 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 }, + { 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 }, + { 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 }, + { 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 }, + { 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 }, + { 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 }, + { 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 }, + { 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 }, + { 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 }, + { 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 }, + { 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 }, +}; + +/* vendors */ +enum { + Ralink = 0x1814, + Awt = 0x1a3b, +}; +/* products */ +enum { + RalinkRT2890 = 0x0681, + RalinkRT2790 = 0x0781, + AwtRT2890 = 0x1059, +}; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static int rbplant(Ctlr*, int); +static void setchan(Ctlr*, uint); +static void selchangroup(Ctlr*, int); +static void setleds(Ctlr*, u16int); + +static uint +get16(uchar *p){ + return *((u16int*)p); +} +static uint +get32(uchar *p){ + return *((u32int*)p); +} +static void +put32(uchar *p, uint v){ + *((u32int*)p) = v; +} +static void +put16(uchar *p, uint v){ + *((u16int*)p) = v; +}; +static void +memwrite(Ctlr *ctlr, u32int off, uchar *data, uint size){ + memmove((uchar*)ctlr->nic + off, data, size); +} +static void +setregion(Ctlr *ctlr, u32int off, uint val, uint size){ + memset((uchar*)ctlr->nic + off, val, size); +} + +static long +rt2860ctl(Ether *edev, void *buf, long n) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifictl(ctlr->wifi, buf, n); + return 0; +} + +static long +rt2860ifstat(Ether *edev, void *buf, long n, ulong off) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifistat(ctlr->wifi, buf, n, off); + return 0; +} + +static void +setoptions(Ether *edev) +{ + Ctlr *ctlr; + char buf[64]; + int i; + + ctlr = edev->ctlr; + for(i = 0; i < edev->nopt; i++){ + if(strncmp(edev->opt[i], "essid=", 6) == 0){ + snprint(buf, sizeof(buf), "essid %s", edev->opt[i]+6); + if(!waserror()){ + wifictl(ctlr->wifi, buf, strlen(buf)); + poperror(); + } + } + } +} + +static void +rxon(Ether *edev, Wnode *bss) +{ + u32int tmp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + if(bss != nil){ + ctlr->channel = bss->channel; + memmove(ctlr->bssid, bss->bssid, Eaddrlen); + ctlr->aid = bss->aid; + if(ctlr->aid != 0){ + if(ctlr->wifi->debug) + print("new assoc!"); + ctlr->bssnodeid = -1; + }else + ctlr->bcastnodeid = -1; + }else{ + memmove(ctlr->bssid, edev->bcast, Eaddrlen); + ctlr->aid = 0; + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + } + if(ctlr->aid != 0) + setleds(ctlr, LedRadio | LedLink2ghz); + else + setleds(ctlr, LedRadio); + + if(ctlr->wifi->debug) + print("#l%d: rxon: bssid %E, aid %x, channel %d wcid %d\n", + edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->wcid); + + /* Set channel */ + setchan(ctlr, ctlr->channel); + selchangroup(ctlr, 0); + microdelay(1000); + + /* enable mrr(?) */ +#define CCK(mcs) (mcs) +#define OFDM(mcs) (1 << 3 | (mcs)) + csr32w(ctlr, LgFbkCfg0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + csr32w(ctlr, LgFbkCfg1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ +#undef OFDM +#undef CCK + /* update slot */ + tmp = csr32r(ctlr, BkoffSlotCfg); + tmp &= ~0xff; + tmp |= /* (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : */ 20; + csr32w(ctlr, BkoffSlotCfg, tmp); + + /* set TX preamble */ + tmp = csr32r(ctlr, AutoRspCfg); + tmp &= ~CckShortEn; +/* if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= CckShortEn; */ + csr32w(ctlr, AutoRspCfg, tmp); + + /* set basic rates */ + csr32w(ctlr, LegacyBasicRate, 0x003); /* 11B */ + + /* Set BSSID */ + csr32w(ctlr, MacBssidDw0, + ctlr->bssid[0] | ctlr->bssid[1] << 8 | ctlr->bssid[2] << 16 | ctlr->bssid[3] << 24); + csr32w(ctlr, MacBssidDw1, + ctlr->bssid[4] | ctlr->bssid[5] << 8); + + if(ctlr->bcastnodeid == -1){ + ctlr->bcastnodeid = 0xff; + memwrite(ctlr, WcidEntry(ctlr->bcastnodeid), edev->bcast, Eaddrlen); + } + if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){ + ctlr->bssnodeid = 0; + memwrite(ctlr, WcidEntry(ctlr->bssnodeid), ctlr->bssid, Eaddrlen); + } +} + +static void +rt2860promiscuous(void *arg, int on) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + if(ctlr->attached == 0) + return; + qlock(ctlr); + ctlr->prom = on; + rxon(edev, ctlr->wifi->bss); + qunlock(ctlr); +} + +static FWImage* +readfirmware(void){ + static char name[] = "ral-rt2860"; + uchar dirbuf[sizeof(Dir)+100], *data; + char buf[128]; + FWImage *fw; + int n, r; + Chan *c; + Dir d; + + if(!iseve()) + error(Eperm); + if(!waserror()){ + snprint(buf, sizeof buf, "/boot/%s", name); + c = namec(buf, Aopen, OREAD, 0); + poperror(); + } else { + snprint(buf, sizeof buf, "/lib/firmware/%s", name); + c = namec(buf, Aopen, OREAD, 0); + } + if(waserror()){ + cclose(c); + nexterror(); + } + n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf); + if(n <= 0) + error("can't stat firmware"); + convM2D(dirbuf, n, &d, nil); + fw = malloc(sizeof(*fw)); + fw->size = d.length; + data = fw->data = smalloc(d.length); + if(waserror()){ + free(fw); + nexterror(); + } + r = 0; + while(r < d.length){ + n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r); + if(n <= 0) + break; + r += n; + } + poperror(); + poperror(); + cclose(c); + return fw; +} + +static char* +boot(Ctlr *ctlr) +{ + int ntries; + + /* set "host program ram write selection" bit */ + csr32w(ctlr, SysCtrl, HstPmSel); + /* write microcode image */ + memwrite(ctlr, FwBase, ctlr->fw->data, ctlr->fw->size); + /* kick microcontroller unit */ + csr32w(ctlr, SysCtrl, 0); + coherence(); + csr32w(ctlr, SysCtrl, McuReset); + + csr32w(ctlr, H2mBbpagent, 0); + csr32w(ctlr, H2mMailbox, 0); + + /* wait until microcontroller is ready */ + coherence(); + for(ntries = 0; ntries < 1000; ntries++){ + if (csr32r(ctlr, SysCtrl) & McuReady) + break; + microdelay(1000); + } + if (ntries == 1000) + return "timeout waiting for MCU to initialize"; + return 0; +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +static int +mcucmd(Ctlr *ctlr, u8int cmd, u16int arg, int wait) +{ + int slot, ntries; + u32int tmp; + u8int cid; + + SET(slot); + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mMailbox) & H2mBusy)) + break; + microdelay(2); + } + if(ntries == 100) + return -1; + + cid = wait ? cmd : TokenNoIntr; + csr32w(ctlr, H2mMailbox, H2mBusy | cid << 16 | arg); + coherence(); + csr32w(ctlr, HostCmd, cmd); + + if(!wait) + return 0; + /* wait for the command to complete */ + for(ntries = 0; ntries < 200; ntries++){ + tmp = csr32r(ctlr, H2mMailboxCid); + /* find the command slot */ + for(slot = 0; slot < 4; slot++, tmp >>= 8) + if ((tmp & 0xff) == cid) + break; + if(slot < 4) + break; + microdelay(100); + } + if(ntries == 200){ + /* clear command and status */ + csr32w(ctlr, H2mMailboxStatus, 0xffffffff); + csr32w(ctlr, H2mMailboxCid, 0xffffffff); + return -1; + } + /* get command status (1 means success) */ + tmp = csr32r(ctlr, H2mMailboxStatus); + tmp = (tmp >> (slot * 8)) & 0xff; + /* clear command and status */ + csr32w(ctlr, H2mMailboxStatus, 0xffffffff); + csr32w(ctlr, H2mMailboxCid, 0xffffffff); + return (tmp == 1) ? 0 : -1; +} + + +/* + * Reading and writing from/to the BBP is different from RT2560 and RT2661. + * We access the BBP through the 8051 microcontroller unit which means that + * the microcode must be loaded first. + */ +static void +bbpwrite(Ctlr *ctlr, u8int reg, u8int val) +{ + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not write to BBP through MCU\n"); + return; + } + + csr32w(ctlr, H2mBbpagent, BbpRwParallel | + BbpCsrKick | reg << 8 | val); + coherence(); + + mcucmd(ctlr, McuCmdBbp, 0, 0); + microdelay(1000); +} + +static u8int +bbpread(Ctlr *ctlr, u8int reg) +{ + u32int val; + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not read from BBP through MCU"); + return 0; + } + + csr32w(ctlr, H2mBbpagent, BbpRwParallel | + BbpCsrKick | BbpCsrRead | reg << 8); + coherence(); + + mcucmd(ctlr, McuCmdBbp, 0, 0); + microdelay(1000); + + for(ntries = 0; ntries < 100; ntries++){ + val = csr32r(ctlr, H2mBbpagent); + if(!(val & BbpCsrKick)) + return val & 0xff; + microdelay(1); + } + print("could not read from BBP through MCU\n"); + + return 0; +} + +static char* +bbpinit(Ctlr *ctlr) +{ + int i, ntries; + char *err; + + /* wait for BBP to wake up */ + for(ntries = 0; ntries < 20; ntries++){ + u8int bbp0 = bbpread(ctlr, 0); + if (bbp0 != 0 && bbp0 != 0xff) + break; + } + if(ntries == 20){ + err = "timeout waiting for BBP to wake up"; + return err; + } + + /* initialize BBP registers to default values */ + for(i = 0; i < nelem(rt2860_def_bbp); i++){ + bbpwrite(ctlr, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + + /* fix BBP84 for RT2860E */ + if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev != 0x0101) + bbpwrite(ctlr, 84, 0x19); + + if(ctlr->mac_ver >= 0x3071){ + bbpwrite(ctlr, 79, 0x13); + bbpwrite(ctlr, 80, 0x05); + bbpwrite(ctlr, 81, 0x33); + }else if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100){ + bbpwrite(ctlr, 69, 0x16); + bbpwrite(ctlr, 73, 0x12); + } + + return nil; + +} + +static void +setleds(Ctlr *ctlr, u16int which) +{ + mcucmd(ctlr, McuCmdLeds, + which | (ctlr->leds & 0x7f), 0); +} + +static char* +txrxon(Ctlr *ctlr) +{ + u32int tmp; + int ntries; + char *err; + + SET(tmp); + /* enable Tx/Rx DMA engine */ + csr32w(ctlr, MacSysCtrl, MacTxEn); + coherence(); + for(ntries = 0; ntries < 200; ntries++){ + tmp = csr32r(ctlr, WpdmaGloCfg); + if((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if(ntries == 200){ + err = "timeout waiting for DMA engine"; + return err; + } + + microdelay(50); + + tmp |= RxDmaEn | TxDmaEn | + WpdmaBtSize64 << WpdmaBtSizeShift; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* set Rx filter */ + tmp = DropCrcErr | DropPhyErr; + if(!ctlr->prom){ + tmp |= DropUcNome | DropDupl | + DropCts | DropBa | DropAck | + DropVerErr | DropCtrlRsv | + DropCfack | DropCfend; + tmp |= DropRts | DropPspoll; + } + csr32w(ctlr, RxFiltrCfg, tmp); + + csr32w(ctlr, MacSysCtrl, MacRxEn | MacTxEn); + + return 0; +} + +/* + * Write to one of the 4 programmable 24-bit RF registers. + */ +static char* +rfwrite(Ctlr *ctlr, u8int reg, u32int val) +{ + u32int tmp; + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if (!(csr32r(ctlr, RfCsrCfg0) & RfRegCtrl)) + break; + microdelay(1); + } + if(ntries == 100) + return "could not write to RF"; + + /* RF registers are 24-bit on the RT2860 */ + tmp = RfRegCtrl | 24 << RfRegWidthShift | + (val & 0x3fffff) << 2 | (reg & 3); + csr32w(ctlr, RfCsrCfg0, tmp); + return nil; +} + +static void +selchangroup(Ctlr *ctlr, int group) +{ + u32int tmp; + u8int agc; + + bbpwrite(ctlr, 62, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 63, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 64, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 86, 0x00); + + if (group == 0){ + if(ctlr->ext_2ghz_lna){ + bbpwrite(ctlr, 82, 0x62); + bbpwrite(ctlr, 75, 0x46); + }else{ + bbpwrite(ctlr, 82, 0x84); + bbpwrite(ctlr, 75, 0x50); + } + }else{ + if (ctlr->ext_5ghz_lna){ + bbpwrite(ctlr, 82, 0xf2); + bbpwrite(ctlr, 75, 0x46); + }else{ + bbpwrite(ctlr, 82, 0xf2); + bbpwrite(ctlr, 75, 0x50); + } + } + + tmp = csr32r(ctlr, TxBandCfg); + tmp &= ~(Tx5gBandSelN | Tx5gBandSelP); + tmp |= (group == 0) ? Tx5gBandSelN : Tx5gBandSelP; + csr32w(ctlr, TxBandCfg, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RftrEn | TrswEn | LnaPe0En; + if(ctlr->nrxchains > 1) + tmp |= LnaPe1En; + if(ctlr->mac_ver == 0x3593 && ctlr->nrxchains > 2) + tmp |= Rt3593LnaPe2En; + if(group == 0){ /* 2GHz */ + tmp |= PaPeG0En; + if(ctlr->ntxchains > 1) + tmp |= PaPeG1En; + if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2) + tmp |= Rt3593PaPeG2En; + }else{ /* 5GHz */ + tmp |= PaPeA0En; + if(ctlr->ntxchains > 1) + tmp |= PaPeA1En; + if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2) + tmp |= Rt3593PaPeA2En; + } + csr32w(ctlr, TxPinCfg, tmp); + + if(ctlr->mac_ver == 0x3593) { + tmp = csr32r(ctlr, GpioCtrl); + if(ctlr->flags & ConnPciE) { + tmp &= ~0x01010000; + if(group == 0) + tmp |= 0x00010000; + }else{ + tmp &= ~0x00008080; + if(group == 0) + tmp |= 0x00000080; + } + tmp = (tmp & ~0x00001000) | 0x00000010; + csr32w(ctlr, GpioCtrl, tmp); + } + + /* set initial AGC value */ + if(group == 0){ /* 2GHz band */ + if(ctlr->mac_ver >= 0x3071) + agc = 0x1c + ctlr->lna[0] * 2; + else + agc = 0x2e + ctlr->lna[0]; + }else{ /* 5GHz band */ + agc = 0x32 + (ctlr->lna[group] * 5) / 3; + } + bbpwrite(ctlr, 66, agc); + + microdelay(1000); + +} + +static void +setchan(Ctlr *ctlr, uint chan) +{ + const struct rfprog *rfprog = rt2860_rf2850; + u32int r2, r3, r4; + s8int txpow1, txpow2; + uint i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if(ctlr->ntxchains == 1) + r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ + if(ctlr->nrxchains == 1) + r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if(ctlr->nrxchains == 2) + r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ + + /* use Tx power values from EEPROM */ + txpow1 = ctlr->txpow1[i]; + txpow2 = ctlr->txpow2[i]; + if(chan > 14){ + if (txpow1 >= 0) + txpow1 = txpow1 << 1 | 1; + else + txpow1 = (7 + txpow1) << 1; + if (txpow2 >= 0) + txpow2 = txpow2 << 1 | 1; + else + txpow2 = (7 + txpow2) << 1; + } + r3 = rfprog[i].r3 | txpow1 << 7; + r4 = rfprog[i].r4 | ctlr->freq << 13 | txpow2 << 4; + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3); + rfwrite(ctlr, Rf4, r4); + + microdelay(200); + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3 | 1); + rfwrite(ctlr, Rf4, r4); + + microdelay(200); + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3); + rfwrite(ctlr, Rf4, r4); +} + +static void +updateprot(Ctlr *ctlr) +{ + u32int tmp; + + tmp = RtsthEn | ProtNavShort | TxopAllowAll; + /* setup protection frame rate (MCS code) */ + tmp |= /*(ic->ic_curmode == IEEE80211_MODE_11A) ? + rt2860_rates[RT2860_RIDX_OFDM6].mcs :*/ + rt2860_rates[RidxCck11].mcs; + + /* CCK frames don't require protection */ + csr32w(ctlr, CckProtCfg, tmp); +/* XXX + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= ProtCtrlRtsCts; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= ProtCtrlCts; + } + csr32w(ctlr, OfdmProtCfg, tmp); */ +} + +static char* +rt2860start(Ether *edev) +{ + u32int tmp; + u8int bbp1, bbp3; + int i, qid, ridx, ntries; + char *err; + Ctlr *ctlr; + + ctlr = edev->ctlr; + csr32w(ctlr, PwrPinCfg, IoRaPe); + + /* disable DMA */ + tmp = csr32r(ctlr, WpdmaGloCfg); + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* PBF hardware reset */ + csr32w(ctlr, SysCtrl, 0xe1f); + coherence(); + csr32w(ctlr, SysCtrl, 0xe00); + + if((err = boot(ctlr)) != nil){ + /*XXX: rt2860stop(ifp, 1);*/ + return err; + } + /* set MAC address */ + csr32w(ctlr, MacAddrDw0, + edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24); + csr32w(ctlr, MacAddrDw1, + edev->ea[4] | edev->ea[5] << 8 | 0xff << 16); + + /* init Tx power for all Tx rates (from EEPROM) */ + for(ridx = 0; ridx < 5; ridx++){ + if (ctlr->txpow20mhz[ridx] == 0xffffffff) + continue; + csr32w(ctlr, TxPwrCfg(ridx), ctlr->txpow20mhz[ridx]); + } + + for (ntries = 0; ntries < 100; ntries++) { + tmp = csr32r(ctlr, WpdmaGloCfg); + if ((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if (ntries == 100) { + err = "timeout waiting for DMA engine"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* reset Rx ring and all 6 Tx rings */ + csr32w(ctlr, WpdmaRstIdx, 0x1003f); + + /* PBF hardware reset */ + csr32w(ctlr, SysCtrl, 0xe1f); + coherence(); + csr32w(ctlr, SysCtrl, 0xe00); + + csr32w(ctlr, PwrPinCfg, IoRaPe | IoRfPe); + + csr32w(ctlr, MacSysCtrl, BbpHrst | MacSrst); + coherence(); + csr32w(ctlr, MacSysCtrl, 0); + + for(i = 0; i < nelem(rt2860_def_mac); i++) + csr32w(ctlr, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); + if(ctlr->mac_ver >= 0x3071){ + /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ + csr32w(ctlr, TxSwCfg0, + 4 << DlyPapeEnShift); + } + + if(!(csr32r(ctlr, PciCfg) & PciCfgPci)){ + ctlr->flags |= ConnPciE; + /* PCIe has different clock cycle count than PCI */ + tmp = csr32r(ctlr, UsCycCnt); + tmp = (tmp & ~0xff) | 0x7d; + csr32w(ctlr, UsCycCnt, tmp); + } + + /* wait while MAC is busy */ + for(ntries = 0; ntries < 100; ntries++){ + if (!(csr32r(ctlr, MacStatusReg) & + (RxStatusBusy | TxStatusBusy))) + break; + microdelay(1000); + } + if(ntries == 100){ + err = "timeout waiting for MAC"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + + /* clear Host to MCU mailbox */ + csr32w(ctlr, H2mBbpagent, 0); + csr32w(ctlr, H2mMailbox, 0); + + mcucmd(ctlr, McuCmdRfreset, 0, 0); + microdelay(1000); + + if((err = bbpinit(ctlr)) != nil){ + /*rt2860_stop(ifp, 1);*/ + return err; + } + /* clear RX WCID search table */ + setregion(ctlr, WcidEntry(0), 0, 512); + /* clear pairwise key table */ + setregion(ctlr, Pkey(0), 0, 2048); + /* clear IV/EIV table */ + setregion(ctlr, Iveiv(0), 0, 512); + /* clear WCID attribute table */ + setregion(ctlr, WcidAttr(0), 0, 256); + /* clear shared key table */ + setregion(ctlr, Skey(0, 0), 0, 8 * 32); + /* clear shared key mode */ + setregion(ctlr, SkeyMode07, 0, 4); + + /* init Tx rings (4 EDCAs + HCCA + Mgt) */ + for(qid = 0; qid < 6; qid++){ + csr32w(ctlr, TxBasePtr(qid), PCIWADDR(ctlr->tx[qid].d)); + csr32w(ctlr, TxMaxCnt(qid), Ntx); + csr32w(ctlr, TxCtxIdx(qid), 0); + } + + /* init Rx ring */ + csr32w(ctlr, RxBasePtr, PCIWADDR(ctlr->rx.p)); + csr32w(ctlr, RxMaxCnt, Nrx); + csr32w(ctlr, RxCalcIdx, Nrx - 1); + + /* setup maximum buffer sizes */ + csr32w(ctlr, MaxLenCfg, 1 << 12 | + (Rbufsize - Rxwisize - 2)); + + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, WpdmaGloCfg); + if ((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if (ntries == 100) { + err = "timeout waiting for DMA engine"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* disable interrupts mitigation */ + csr32w(ctlr, DelayIntCfg, 0); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 8; i++) { + if (ctlr->bbp[i].reg == 0 || ctlr->bbp[i].reg == 0xff) + continue; + bbpwrite(ctlr, ctlr->bbp[i].reg, ctlr->bbp[i].val); + } + + /* select Main antenna for 1T1R devices */ +/* notyet + if (ctlr->rf_rev == Rf2020 || + ctlr->rf_rev == Rf3020 || + ctlr->rf_rev == Rf3320) + rt3090_set_rx_antenna(ctlr, 0); +*/ + + /* send LEDs operating mode to microcontroller */ + mcucmd(ctlr, McuCmdLed1, ctlr->led[0], 0); + mcucmd(ctlr, McuCmdLed2, ctlr->led[1], 0); + mcucmd(ctlr, McuCmdLed3, ctlr->led[2], 0); + +/* XXX: 30xx + if (ctlr->mac_ver >= 0x3071) + rt3090_rf_init(ctlr); +*/ + + mcucmd(ctlr, McuCmdSleep, 0x02ff, 1); + mcucmd(ctlr, McuCmdWakeup, 0, 1); +/* XXX: 30xx + if (ctlr->mac_ver >= 0x3071) + rt3090_rf_wakeup(ctlr); +*/ + + /* disable non-existing Rx chains */ + bbp3 = bbpread(ctlr, 3); + bbp3 &= ~(1 << 3 | 1 << 4); + if (ctlr->nrxchains == 2) + bbp3 |= 1 << 3; + else if (ctlr->nrxchains == 3) + bbp3 |= 1 << 4; + bbpwrite(ctlr, 3, bbp3); + + /* disable non-existing Tx chains */ + bbp1 = bbpread(ctlr, 1); + if (ctlr->ntxchains == 1) + bbp1 = (bbp1 & ~(1 << 3 | 1 << 4)); + else if (ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 2) + bbp1 = (bbp1 & ~(1 << 4)) | 1 << 3; + else if (ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 3) + bbp1 = (bbp1 & ~(1 << 3)) | 1 << 4; + bbpwrite(ctlr, 1, bbp1); +/* XXX: 30xx + if (ctlr->mac_ver >= 0x3071) + rt3090_rf_setup(ctlr); +*/ + /* select default channel */ + setchan(ctlr, 3); + + /* reset RF from MCU */ + mcucmd(ctlr, McuCmdRfreset, 0, 0); + + /* set RTS threshold */ + tmp = csr32r(ctlr, TxRtsCfg); + tmp &= ~0xffff00; + tmp |= 1 /* ic->ic_rtsthreshold */ << 8; + csr32w(ctlr, TxRtsCfg, tmp); + + /* setup initial protection mode */ + updateprot(ctlr); + + /* turn radio LED on */ + setleds(ctlr, LedRadio); + + /* enable Tx/Rx DMA engine */ + if ((err = txrxon(ctlr)) != 0) { + /*rt2860_stop(ifp, 1);*/ + return err; + } + + /* clear pending interrupts */ + csr32w(ctlr, IntStatus, 0xffffffff); + /* enable interrupts */ + csr32w(ctlr, IntMask, 0x3fffc); + + if (ctlr->flags & AdvancedPs) + mcucmd(ctlr, McuCmdPslevel, ctlr->pslevel, 0); + return nil; +} + +/* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ +static u32int +b4inc(u32int b32, s8int delta) +{ + s8int i, b4; + + for (i = 0; i < 8; i++) { + b4 = b32 & 0xf; + b4 += delta; + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return b32; +} + +static void +transmit(Wifi *wifi, Wnode *wn, Block *b) +{ + Ether *edev; + Ctlr *ctlr; + Wifipkt *w; + u8int mcs, qid; + int ridx, /*ctl_ridx,*/ hdrlen; + uchar *p; + int nodeid; + Block *outb; + TXQ *tx; + Pool *pool; + + edev = wifi->ether; + ctlr = edev->ctlr; + + qlock(ctlr); + if(ctlr->attached == 0 || ctlr->broken){ + qunlock(ctlr); + freeb(b); + return; + } + if((wn->channel != ctlr->channel) + || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0))) + rxon(edev, wn); + + if(b == nil){ + /* association note has no data to transmit */ + qunlock(ctlr); + return; + } + + pool = &ctlr->pool; + qid = 0; /* for now */ + ridx = 0; + tx = &ctlr->tx[qid]; + + nodeid = ctlr->bcastnodeid; + w = (Wifipkt*)b->rp; + hdrlen = wifihdrlen(w); + + p = pool->p + pool->i * TxwiDmaSz; + if((w->a1[0] & 1) == 0){ + *(p+4) = TxAck; /* xflags */ + + if(BLEN(b) > 512-4) + *(p+1) = TxTxopBackoff; /* txop */ + + if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){ + nodeid = ctlr->bssnodeid; + ridx = 2; /* BUG: hardcode 11Mbit */ + } + } + + /*ctl_ridx = rt2860_rates[ridx].ctl_ridx;*/ + mcs = rt2860_rates[ridx].mcs; + + /* setup TX Wireless Information */ + *p = 0; /* flags */ + *(p+2) = PhyCck | mcs; /* phy */ + /* let HW generate seq numbers */ + *(p+4) |= TxNseq; /* xflags */ + put16(p + 6, BLEN(b) | (((mcs+1) & 0xf) << TxPidShift) ); /* length */ + +/* put16((uchar*)&w->dur[0], rt2860_rates[ctl_ridx].lp_ack_dur); */ + + *(p+5) = nodeid; /* wcid */ + + /* copy packet header */ + memmove(p + Txwisize, b->rp, hdrlen); + + /* setup tx descriptor */ + /* first segment is TXWI + 802.11 header */ + p = (uchar*)tx->d + Tdscsize * tx->i; + put32(p, PCIWADDR(pool->p + pool->i * TxwiDmaSz)); /* sdp0 */ + put16(p + 6, Txwisize + hdrlen); /* sdl0 */ + *(p + 15) = TxQselEdca; /* flags */ + + /* allocate output buffer */ + b->rp += hdrlen; + tx->b[tx->i] = outb = iallocb(BLEN(b) + 256); + if(outb == nil){ + print("outb = nil\n"); + return; + } + outb->rp = (uchar*)ROUND((uintptr)outb->base, 256); + memset(outb->rp, 0, BLEN(b)); + memmove(outb->rp, b->rp, BLEN(b)); + outb->wp = outb->rp + BLEN(b); + freeb(b); + + /* setup payload segments */ + put32(p + 8, PCIWADDR(outb->rp)); /* sdp1 */ + put16(p + 4, BLEN(outb) | TxLs1); /* sdl1 */ + + p = pool->p + pool->i * TxwiDmaSz; + w = (Wifipkt*)(p + Txwisize); + if(ctlr->wifi->debug){ + print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%ld\n", w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb)); + } + + tx->i = (tx->i + 1) % Ntx; + pool->i = (pool->i + 1) % Ntxpool; + + coherence(); + + /* kick Tx */ + csr32w(ctlr, TxCtxIdx(qid), ctlr->tx[qid].i); + + qunlock(ctlr); + return; +} + +static void +rt2860attach(Ether *edev) +{ + FWImage *fw; + Ctlr *ctlr; + char *err; + + ctlr = edev->ctlr; + eqlock(ctlr); + if(waserror()){ + print("#l%d: %s\n", edev->ctlrno, up->errstr); + /*if(ctlr->power) + poweroff(ctlr);*/ + qunlock(ctlr); + nexterror(); + } + if(ctlr->attached == 0){ + if(ctlr->wifi == nil) + ctlr->wifi = wifiattach(edev, transmit); + + if(ctlr->fw == nil){ + fw = readfirmware(); + ctlr->fw = fw; + } + if((err = rt2860start(edev)) != nil){ + error(err); + } + + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + ctlr->channel = 1; + ctlr->aid = 0; + + setoptions(edev); + + ctlr->attached = 1; + } + qunlock(ctlr); + poperror(); +} + +static void +receive(Ctlr *ctlr) +{ + u32int hw; + RXQ *rx; + Block *b; + uchar *d; + + rx = &ctlr->rx; + if(rx->b == nil){ + print("rx->b == nil!"); + return; + } + hw = csr32r(ctlr, FsDrxIdx) & 0xfff; + while(rx->i != hw){ + u16int sdl0, len; + u32int flags; + uchar *p; + Wifipkt *w; + int hdrlen; + + p = (uchar*)rx->p + Rdscsize * rx->i; + sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */); + if(!(sdl0 & RxDdone)){ + print("rxd ddone bit not set\n"); + break; /* should not happen */ + } + flags = get32(p + 12); + if(flags & (RxCrcerr | RxIcverr)){ + /* print("crc | icv err\n"); */ + goto skip; + } + + b = rx->b[rx->i]; + if(b == nil){ + print("no buf\n"); + goto skip; + } + d = b->rp; + if(ctlr->wifi == nil) + goto skip; + if(rbplant(ctlr, rx->i) < 0){ + print("can't plant"); + goto skip; + } + ctlr->wcid = *b->rp; + len = get16(b->rp + 2 /* wcid, keyidx */) & 0xfff; + b->rp = d + Rxwisize; + b->wp = b->rp + len; + w = (Wifipkt*)b->rp; + hdrlen = wifihdrlen(w); + /* HW may insert 2 padding bytes after 802.11 header */ + if(flags & RxL2pad){ + memmove(b->rp + 2, b->rp, hdrlen); + b->rp += 2; + } + w = (Wifipkt*)b->rp; + if(ctlr->wifi->debug) + print("receive: %E->%E,%E wcid 0x%x \n", w->a2, w->a1, w->a3, ctlr->wcid); + wifiiq(ctlr->wifi, b); +skip: + put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~RxDdone); + rx->i = (rx->i + 1) % Nrx; + } + coherence(); + /* tell HW what we have processed */ + csr32w(ctlr, RxCalcIdx, (rx->i - 1) % Nrx); +} + +static void +stats(Ctlr *ctlr) +{ + u32int stat; + u8int wcid; + + while((stat = csr32r(ctlr, TxStatFifo)) & TxqVld){ + wcid = (stat >> TxqWcidShift) & 0xff; + /* if no ACK was requested, no feedback is available */ + if (!(stat & TxqAckreq) || wcid == 0xff){ + continue; + } + } +} + +static void +rt2860tx(Ctlr *ctlr, u8int q) +{ + u32int hw; + TXQ *tx; + + stats(ctlr); + tx = &ctlr->tx[q]; + hw = csr32r(ctlr, TxDtxIdx(q)); + while(tx->n != hw){ + uchar *p = (uchar*)tx->d + Rdscsize * tx->n; + u16int sdl0; + + if(tx->b[tx->n]){ + freeb(tx->b[tx->n]); + tx->b[tx->n] = nil; + } + sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */); + if(!(sdl0 & TxDdone)){ + print("txd ddone bit not set\n"); + break; /* should not happen */ + } + memset((uchar*)ctlr->pool.p + TxwiDmaSz * tx->n, 0, TxwiDmaSz); + memset((uchar*)tx->d + Tdscsize * tx->n, 0, Tdscsize); + // put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~TxDdone); + tx->n = (tx->n + 1) % Ntx; + } + coherence(); +} + +static void +rt2860interrupt(Ureg*, void *arg) +{ + u32int r; + Ether *edev; + Ctlr *ctlr; + int debug; + + edev = arg; + ctlr = edev->ctlr; + ilock(ctlr); + + debug = ctlr->wifi->debug; + + r = csr32r(ctlr, IntStatus); + if(r == 0xffffffff){ + iunlock(ctlr); + return; + } + if(r == 0){ + iunlock(ctlr); + return; + } + + /* acknowledge interrupts */ + csr32w(ctlr, IntStatus, r); + + if(r & TxRxCoherent){ + u32int tmp; + /* DMA finds data coherent event when checking the DDONE bit */ + if(debug) + print("txrx coherent intr\n"); + + /* restart DMA engine */ + tmp = csr32r(ctlr, WpdmaGloCfg); + tmp &= ~(TxWbDdone | RxDmaEn | TxDmaEn); + csr32w(ctlr, WpdmaGloCfg, tmp); + + txrxon(ctlr); + } + if(r & MacInt2) + stats(ctlr); + + if(r & TxDoneInt5) + rt2860tx(ctlr, 5); + + if(r & RxDoneInt) + receive(ctlr); + + if(r & TxDoneInt4) + rt2860tx(ctlr, 4); + + if(r & TxDoneInt3) + rt2860tx(ctlr, 3); + + if(r & TxDoneInt2) + rt2860tx(ctlr, 2); + + if(r & TxDoneInt1) + rt2860tx(ctlr, 1); + + if(r & TxDoneInt0) + rt2860tx(ctlr, 0); + + iunlock(ctlr); +} + +static void +eepromctl(Ctlr *ctlr, u32int val) +{ + csr32w(ctlr, PciEectrl, val); + coherence(); + microdelay(EepromDelay); +} + +/* + * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, + * 93C66 or 93C86). + */ +static u16int +eeread2(Ctlr *ctlr, u16int addr) +{ + u32int tmp; + u16int val; + int n; + + /* clock C once before the first command */ + eepromctl(ctlr, 0); + + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, EectrlS | EectrlC); + eepromctl(ctlr, EectrlS); + + /* write start bit (1) */ + eepromctl(ctlr, EectrlS | EectrlD); + eepromctl(ctlr, EectrlS | EectrlD | EectrlC); + + /* write READ opcode (10) */ + eepromctl(ctlr, EectrlS | EectrlD); + eepromctl(ctlr, EectrlS | EectrlD | EectrlC); + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, EectrlS | EectrlC); + + /* write address (A5-A0 or A7-A0) */ + n = ((csr32r(ctlr, PciEectrl) & 0x30) == 0) ? 5 : 7; + for(; n >= 0; n--){ + eepromctl(ctlr, EectrlS | + (((addr >> n) & 1) << EectrlShiftD)); + eepromctl(ctlr, EectrlS | + (((addr >> n) & 1) << EectrlShiftD) | EectrlC); + } + + eepromctl(ctlr, EectrlS); + + /* read data Q15-Q0 */ + val = 0; + for(n = 15; n >= 0; n--){ + eepromctl(ctlr, EectrlS | EectrlC); + tmp = csr32r(ctlr, PciEectrl); + val |= ((tmp & EectrlQ) >> EectrlShiftQ) << n; + eepromctl(ctlr, EectrlS); + } + + eepromctl(ctlr, 0); + + /* clear Chip Select and clock C */ + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, 0); + eepromctl(ctlr, EectrlC); + + return val; +} + +/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */ +static u16int +efuseread2(Ctlr *, u16int) +{ + /* XXX: 30xx */ + print("efuse rom not implemented\n"); + return 0; +} + +static char* +eepromread(Ether *edev) +{ + s8int delta_2ghz, delta_5ghz; + u32int tmp; + u16int val; + int ridx, ant, i; + u16int (*rom_read)(Ctlr*, u16int); + Ctlr *ctlr; + + enum { DefLna = 10 }; + + ctlr = edev->ctlr; + /* check whether the ROM is eFUSE ROM or EEPROM */ + rom_read = eeread2; + if(ctlr->mac_ver >= 0x3071){ + tmp = csr32r(ctlr, Rt3070EfuseCtrl); + if(tmp & Rt3070SelEfuse) + rom_read = efuseread2; + } + + /* read MAC address */ + val = rom_read(ctlr, EepromMac01); + edev->ea[0] = val & 0xff; + edev->ea[1] = val >> 8; + val = rom_read(ctlr, EepromMac23); + edev->ea[2] = val & 0xff; + edev->ea[3] = val >> 8; + val = rom_read(ctlr, EepromMac45); + edev->ea[4] = val & 0xff; + edev->ea[5] = val >> 8; + + /* read vendor BBP settings */ + for(i = 0; i < 8; i++){ + val = rom_read(ctlr, EepromBbpBase + i); + ctlr->bbp[i].val = val & 0xff; + ctlr->bbp[i].reg = val >> 8; + } + + if (ctlr->mac_ver >= 0x3071) { + /* read vendor RF settings */ + for(i = 0; i < 10; i++){ + val = rom_read(ctlr, Rt3071EepromRfBase + i); + ctlr->rf[i].val = val & 0xff; + ctlr->rf[i].reg = val >> 8; + } + } + + /* read RF frequency offset from EEPROM */ + val = rom_read(ctlr, EepromFreqLeds); + ctlr->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + if ((val >> 8) != 0xff) { + /* read LEDs operating mode */ + ctlr->leds = val >> 8; + ctlr->led[0] = rom_read(ctlr, EepromLed1); + ctlr->led[1] = rom_read(ctlr, EepromLed2); + ctlr->led[2] = rom_read(ctlr, EepromLed3); + } else { + /* broken EEPROM, use default settings */ + ctlr->leds = 0x01; + ctlr->led[0] = 0x5555; + ctlr->led[1] = 0x2221; + ctlr->led[2] = 0xa9f8; + } + /* read RF information */ + val = rom_read(ctlr, EepromAntenna); + if (val == 0xffff) { + if (ctlr->mac_ver == 0x3593) { + /* default to RF3053 3T3R */ + ctlr->rf_rev = Rf3053; + ctlr->ntxchains = 3; + ctlr->nrxchains = 3; + } else if (ctlr->mac_ver >= 0x3071) { + /* default to RF3020 1T1R */ + ctlr->rf_rev = Rf3020; + ctlr->ntxchains = 1; + ctlr->nrxchains = 1; + } else { + /* default to RF2820 1T2R */ + ctlr->rf_rev = Rf2820; + ctlr->ntxchains = 1; + ctlr->nrxchains = 2; + } + } else { + ctlr->rf_rev = (val >> 8) & 0xf; + ctlr->ntxchains = (val >> 4) & 0xf; + ctlr->nrxchains = val & 0xf; + } + + /* check if RF supports automatic Tx access gain control */ + val = rom_read(ctlr, EepromConfig); + /* check if driver should patch the DAC issue */ + if ((val >> 8) != 0xff) + ctlr->patch_dac = (val >> 15) & 1; + if ((val & 0xff) != 0xff) { + ctlr->ext_5ghz_lna = (val >> 3) & 1; + ctlr->ext_2ghz_lna = (val >> 2) & 1; + /* check if RF supports automatic Tx access gain control */ + ctlr->calib_2ghz = ctlr->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; + /* check if we have a hardware radio switch */ + ctlr->rfswitch = val & 1; + } + if (ctlr->flags & AdvancedPs) { + /* read PCIe power save level */ + val = rom_read(ctlr, EepromPciePslevel); + if ((val & 0xff) != 0xff) { + ctlr->pslevel = val & 0x3; + val = rom_read(ctlr, EepromRev); + if ((val & 0xff80) != 0x9280) + ctlr->pslevel = MIN(ctlr->pslevel, 1); + } + } + + /* read power settings for 2GHz channels */ + for (i = 0; i < 14; i += 2) { + val = rom_read(ctlr, + EepromPwr2ghzBase1 + i / 2); + ctlr->txpow1[i + 0] = (s8int)(val & 0xff); + ctlr->txpow1[i + 1] = (s8int)(val >> 8); + + val = rom_read(ctlr, + EepromPwr2ghzBase2 + i / 2); + ctlr->txpow2[i + 0] = (s8int)(val & 0xff); + ctlr->txpow2[i + 1] = (s8int)(val >> 8); + } + /* fix broken Tx power entries */ + + for (i = 0; i < 14; i++) { + if (ctlr->txpow1[i] < 0 || ctlr->txpow1[i] > 31) + ctlr->txpow1[i] = 5; + if (ctlr->txpow2[i] < 0 || ctlr->txpow2[i] > 31) + ctlr->txpow2[i] = 5; + } + /* read power settings for 5GHz channels */ + for (i = 0; i < 40; i += 2) { + val = rom_read(ctlr, + EepromPwr5ghzBase1 + i / 2); + ctlr->txpow1[i + 14] = (s8int)(val & 0xff); + ctlr->txpow1[i + 15] = (s8int)(val >> 8); + + val = rom_read(ctlr, + EepromPwr5ghzBase2 + i / 2); + ctlr->txpow2[i + 14] = (s8int)(val & 0xff); + ctlr->txpow2[i + 15] = (s8int)(val >> 8); + } + + /* fix broken Tx power entries */ + for (i = 0; i < 40; i++) { + if (ctlr->txpow1[14 + i] < -7 || ctlr->txpow1[14 + i] > 15) + ctlr->txpow1[14 + i] = 5; + if (ctlr->txpow2[14 + i] < -7 || ctlr->txpow2[14 + i] > 15) + ctlr->txpow2[14 + i] = 5; + } + + /* read Tx power compensation for each Tx rate */ + val = rom_read(ctlr, EepromDeltapwr); + delta_2ghz = delta_5ghz = 0; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_2ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_5ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + + for (ridx = 0; ridx < 5; ridx++) { + u32int reg; + + val = rom_read(ctlr, EepromRpwr + ridx * 2); + reg = val; + val = rom_read(ctlr, EepromRpwr + ridx * 2 + 1); + reg |= (u32int)val << 16; + + ctlr->txpow20mhz[ridx] = reg; + ctlr->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + ctlr->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + } + + /* read factory-calibrated samples for temperature compensation */ + val = rom_read(ctlr, EepromTssi12ghz); + ctlr->tssi_2ghz[0] = val & 0xff; /* [-4] */ + ctlr->tssi_2ghz[1] = val >> 8; /* [-3] */ + val = rom_read(ctlr, EepromTssi22ghz); + ctlr->tssi_2ghz[2] = val & 0xff; /* [-2] */ + ctlr->tssi_2ghz[3] = val >> 8; /* [-1] */ + val = rom_read(ctlr, EepromTssi32ghz); + ctlr->tssi_2ghz[4] = val & 0xff; /* [+0] */ + ctlr->tssi_2ghz[5] = val >> 8; /* [+1] */ + val = rom_read(ctlr, EepromTssi42ghz); + ctlr->tssi_2ghz[6] = val & 0xff; /* [+2] */ + ctlr->tssi_2ghz[7] = val >> 8; /* [+3] */ + val = rom_read(ctlr, EepromTssi52ghz); + ctlr->tssi_2ghz[8] = val & 0xff; /* [+4] */ + ctlr->step_2ghz = val >> 8; + /* check that ref value is correct, otherwise disable calibration */ + if (ctlr->tssi_2ghz[4] == 0xff) + ctlr->calib_2ghz = 0; + + val = rom_read(ctlr, EepromTssi15ghz); + ctlr->tssi_5ghz[0] = val & 0xff; /* [-4] */ + ctlr->tssi_5ghz[1] = val >> 8; /* [-3] */ + val = rom_read(ctlr, EepromTssi25ghz); + ctlr->tssi_5ghz[2] = val & 0xff; /* [-2] */ + ctlr->tssi_5ghz[3] = val >> 8; /* [-1] */ + val = rom_read(ctlr, EepromTssi35ghz); + ctlr->tssi_5ghz[4] = val & 0xff; /* [+0] */ + ctlr->tssi_5ghz[5] = val >> 8; /* [+1] */ + val = rom_read(ctlr, EepromTssi45ghz); + ctlr->tssi_5ghz[6] = val & 0xff; /* [+2] */ + ctlr->tssi_5ghz[7] = val >> 8; /* [+3] */ + val = rom_read(ctlr, EepromTssi55ghz); + ctlr->tssi_5ghz[8] = val & 0xff; /* [+4] */ + ctlr->step_5ghz = val >> 8; + /* check that ref value is correct, otherwise disable calibration */ + if (ctlr->tssi_5ghz[4] == 0xff) + ctlr->calib_5ghz = 0; + + /* read RSSI offsets and LNA gains from EEPROM */ + val = rom_read(ctlr, EepromRssi12ghz); + ctlr->rssi_2ghz[0] = val & 0xff; /* Ant A */ + ctlr->rssi_2ghz[1] = val >> 8; /* Ant B */ + val = rom_read(ctlr, EepromRssi22ghz); + if (ctlr->mac_ver >= 0x3071) { + /* + * On RT3090 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 2GHz band. + */ + if ((val & 0xff) != 0xff) + ctlr->txmixgain_2ghz = val & 0x7; + } else + ctlr->rssi_2ghz[2] = val & 0xff; /* Ant C */ + ctlr->lna[2] = val >> 8; /* channel group 2 */ + + val = rom_read(ctlr, EepromRssi15ghz); + ctlr->rssi_5ghz[0] = val & 0xff; /* Ant A */ + ctlr->rssi_5ghz[1] = val >> 8; /* Ant B */ + val = rom_read(ctlr, EepromRssi25ghz); + ctlr->rssi_5ghz[2] = val & 0xff; /* Ant C */ + ctlr->lna[3] = val >> 8; /* channel group 3 */ + + val = rom_read(ctlr, EepromLna); + if (ctlr->mac_ver >= 0x3071) + ctlr->lna[0] = DefLna; + else /* channel group 0 */ + ctlr->lna[0] = val & 0xff; + ctlr->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if (ctlr->lna[2] == 0 || ctlr->lna[2] == 0xff) { + ctlr->lna[2] = ctlr->lna[1]; + } + if (ctlr->lna[3] == 0 || ctlr->lna[3] == 0xff) { + ctlr->lna[3] = ctlr->lna[1]; + } + + /* fix broken RSSI offset entries */ + for (ant = 0; ant < 3; ant++) { + if (ctlr->rssi_2ghz[ant] < -10 || ctlr->rssi_2ghz[ant] > 10) { + ctlr->rssi_2ghz[ant] = 0; + } + if (ctlr->rssi_5ghz[ant] < -10 || ctlr->rssi_5ghz[ant] > 10) { + ctlr->rssi_5ghz[ant] = 0; + } + } + + return 0; +} + +static const char * +getrfname(u8int rev) +{ + if((rev == 0) || (rev >= nelem(rfnames))) + return "unknown"; + if(rfnames[rev][0] == '\0') + return "unknown"; + return rfnames[rev]; +} + +static int +rbplant(Ctlr *ctlr, int i) +{ + Block *b; + uchar *p; + + b = iallocb(Rbufsize + 256); + if(b == nil) + return -1; + b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256); + memset(b->rp, 0, Rbufsize); + ctlr->rx.b[i] = b; + p = (uchar*)&ctlr->rx.p[i * 4]; /* sdp0 */ + memset(p, 0, Rdscsize); + put32(p, PCIWADDR(b->rp)); + p = (uchar*)&ctlr->rx.p[i * 4 + 1]; /* sdl0 */ + p += 2; /* sdl1 */ + put16(p, Rbufsize);; + + return 0; +} + +static char* +allocrx(Ctlr *ctlr, RXQ *rx) +{ + int i; + + if(rx->b == nil) + rx->b = malloc(sizeof(Block*) * Nrx); + if(rx->p == nil) /* Rx descriptors */ + rx->p = mallocalign(Nrx * Rdscsize, 16, 0, 0); + if(rx->b == nil || rx->p == nil) + return "no memory for rx ring"; + memset(rx->p, 0, Nrx * Rdscsize); + for(i=0; i<Nrx; i++){ + if(rx->b[i] != nil){ + freeb(rx->b[i]); + rx->b[i] = nil; + } + if(rbplant(ctlr, i) < 0) + return "no memory for rx descriptors"; + } + rx->i = 0; + return nil; +} + +static void +freerx(Ctlr *, RXQ *rx) +{ + int i; + + for(i = 0; i < Nrx; i++){ + if(rx->b[i] != nil){ + freeb(rx->b[i]); + rx->b[i] = nil; + } + } + free(rx->b); + free(rx->p); + rx->p = nil; + rx->b = nil; + rx->i = 0; +} + +static char* +alloctx(Ctlr *, TXQ *tx) +{ + if(tx->b == nil) + tx->b = malloc(sizeof(Block*) * Ntx); + if(tx->d == nil) /* Tx descriptors */ + tx->d = mallocalign(Ntx * Tdscsize, 16, 0, 0); + if(tx->b == nil || tx->d == nil) + return "no memory for tx ring"; + memset(tx->d, 0, Ntx * Tdscsize); + memset(tx->b, 0, Ntx * sizeof(Block*)); + tx->i = 0; + return nil; +} + +static void +freetx(Ctlr *, TXQ *tx) +{ + free(tx->b); + free(tx->d); + tx->d = nil; + tx->i = 0; +} + +static char* +alloctxpool(Ctlr *ctlr) +{ + Pool *pool; + + pool = &ctlr->pool; + if(pool->p == nil) + pool->p = mallocalign(Ntxpool * TxwiDmaSz, 4096, 0, 0); + if(pool->p == nil) + return "no memory for pool"; + memset(pool->p, 0, Ntxpool * TxwiDmaSz); + pool->i = 0; + return 0; +} + +static char* +initring(Ctlr *ctlr) +{ + int qid; + char *err; + + /* + * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. + */ + for(qid = 0; qid < 6; qid++){ + if ((err = alloctx(ctlr, &ctlr->tx[qid])) != nil) + goto fail1; + } + if ((err = allocrx(ctlr, &ctlr->rx)) != nil) + goto fail1; + + if ((err = alloctxpool(ctlr)) != nil) + goto fail2; + /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ + + ctlr->mgtqid = (ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100) ? + 3 : 5; + + return nil; +fail2: freerx(ctlr, &ctlr->rx); + return err; +fail1: while (--qid >= 0) + freetx(ctlr, &ctlr->tx[qid]); + return err; +} + +static int +rt2860init(Ether *edev) +{ + Ctlr *ctlr; + int ntries; + char *err; + u32int tmp; + + SET(tmp); + ctlr = edev->ctlr; + /* wait for NIC to initialize */ + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, AsicVerId); + if(tmp != 0 && tmp != 0xffffffff) + break; + microdelay(10); + } + if(ntries == 100){ + print("timeout waiting for NIC to initialize"); + return -1; + } + ctlr->mac_ver = tmp >> 16; + ctlr->mac_rev = tmp & 0xffff; + + if(ctlr->mac_ver != 0x2860){ + switch(ctlr->pdev->did){ + default: + break; + case RalinkRT2890: + case RalinkRT2790: + case AwtRT2890: + ctlr->flags = AdvancedPs; + break; + } + } + /* retrieve RF rev. no and various other things from EEPROM */ + eepromread(edev); + + print("MAC/BBP RT%X (rev 0x%04X), RF %s (MIMO %dT%dR)\n", + ctlr->mac_ver, ctlr->mac_rev, + getrfname(ctlr->rf_rev), ctlr->ntxchains, ctlr->nrxchains); + if((err = initring(ctlr)) != nil){ + print("error: %s", err); + return -1; + } + + return 0; +} + +static Ctlr *rt2860head, *rt2860tail; + +static void +rt2860pci(void) +{ + Pcidev *pdev; + + pdev = nil; + while(pdev = pcimatch(pdev, 0, 0)) { + Ctlr *ctlr; + void *mem; + + if(pdev->ccrb != 2 || pdev->ccru != 0x80) + continue; + if(pdev->vid != 0x1814) /* Ralink */ + continue; + + switch(pdev->did){ + default: + continue; + case 0x0781: /* RT2790 */ + break; + } + + pcisetbme(pdev); + pcisetpms(pdev, 0); + + ctlr = malloc(sizeof(Ctlr)); + if(ctlr == nil) { + print("rt2860: unable to alloc Ctlr\n"); + continue; + } + ctlr->port = pdev->mem[0].bar & ~0x0F; + mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size); + if(mem == nil) { + print("rt2860: can't map %8.8luX\n", pdev->mem[0].bar); + free(ctlr); + continue; + } + ctlr->nic = mem; + ctlr->pdev = pdev; + + if(rt2860head != nil) + rt2860tail->link = ctlr; + else + rt2860head = ctlr; + rt2860tail = ctlr; + } +} + +static int +rt2860pnp(Ether* edev) +{ + Ctlr *ctlr; + + if(rt2860head == nil) + rt2860pci(); +again: + for(ctlr = rt2860head; ctlr != nil; ctlr = ctlr->link){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pdev->intl; + edev->tbdf = ctlr->pdev->tbdf; + edev->arg = edev; + edev->interrupt = rt2860interrupt; + edev->attach = rt2860attach; + edev->ifstat = rt2860ifstat; + edev->ctl = rt2860ctl; + edev->promiscuous = rt2860promiscuous; + edev->multicast = nil; + edev->mbps = 10; + + if(rt2860init(edev) < 0){ + edev->ctlr = nil; + goto again; + } + return 0; +} + +void +etherrt2860link(void) +{ + addethercard("rt2860", rt2860pnp); +} diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile index c4d1226e1..dc054ab33 100644 --- a/sys/src/9/pc/mkfile +++ b/sys/src/9/pc/mkfile @@ -121,6 +121,7 @@ trap.$O: /sys/include/tos.h uartaxp.$O: uartaxp.i etherm10g.$O: etherm10g2k.i etherm10g4k.i etheriwl.$O: wifi.h +etherrt2860.$O: wifi.h wifi.$O: wifi.h init.h:D: ../port/initcode.c init9.c diff --git a/sys/src/9/pc/pccpuf b/sys/src/9/pc/pccpuf index 9a808bf81..8054b921a 100644 --- a/sys/src/9/pc/pccpuf +++ b/sys/src/9/pc/pccpuf @@ -68,6 +68,7 @@ link ethersmc devi82365 cis etherwavelan wavelan devi82365 cis pci etheriwl pci wifi + etherrt2860 pci wifi ethermedium netdevmedium loopbackmedium diff --git a/sys/src/9/pc/pcf b/sys/src/9/pc/pcf index 6bb744d53..27c94c9d1 100644 --- a/sys/src/9/pc/pcf +++ b/sys/src/9/pc/pcf @@ -69,6 +69,7 @@ link ethersmc devi82365 cis etherwavelan wavelan devi82365 cis pci etheriwl pci wifi + etherrt2860 pci wifi ethermedium pcmciamodem netdevmedium |