summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Polakov <plhk@sdf.org>2013-07-09 16:16:28 +0400
committerAlexander Polakov <plhk@sdf.org>2013-07-09 16:16:28 +0400
commit6cf9d283ed93507492d34e59ae6541951a668432 (patch)
tree95c7d494dfd30f0b9a8594e9cfafde6f29d05477
parent22cc50307ba856e69757ade333e50cdaca83a8c2 (diff)
Add Ralink RT2860 wireless driver
-rw-r--r--sys/src/9/pc/etherrt2860.c3019
-rw-r--r--sys/src/9/pc/mkfile1
-rw-r--r--sys/src/9/pc/pccpuf1
-rw-r--r--sys/src/9/pc/pcf1
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