/* * Intel PRO/Wireless 2200BG/2915ABG driver. * * Written using FreeBSD iwi and Linux ipw2200 * drivers as documentation. * * Not complete. Doesn't send or receive packets, though * the bulk of the code is present. Need to fiddle with wireless * headers and the like. Also need to figure out channel scanning * etc. * * TO DO: * - better story for locking */ #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" #define MIN(a, b) ((a) < (b) ? (a) : (b)) enum { /* device constants */ NCmdRing = 8, NTdRing = 64, NRdRing = 32, Neeprom = 128, Rbsz = 3000, /* registers */ Intr = 0x0008, IntrMask = 0x000C, IntrRx = 0x00000002, IntrStatusChg = 0x00000010, IntrBeaconExp = 0x00000020, IntrCmd = 0x00000800, IntrTx1 = 0x00001000, IntrTx2 = 0x00002000, IntrTx3 = 0x00004000, IntrTx4 = 0x00008000, IntrTx = 0x0000F000, IntrSlmodeCdone = 0x00010000, IntrPrepPwdown = 0x00100000, IntrFwInit = 0x01000000, IntrDisPhyDone = 0x02000000, IntrRadioOff = 0x04000000, IntrFatalError = 0x40000000, IntrParityError = 0x80000000, IntrDefault = IntrRx | IntrCmd | IntrTx | IntrFwInit | IntrRadioOff | IntrFatalError | IntrParityError, IndirectAddr = 0x0010, IndirectData = 0x0014, AutoincAddr = 0x0018, AutoincData = 0x001C, Rst = 0x0020, RstPrincetonReset = 0x00000001, RstSwReset = 0x00000080, RstMasterDisabled = 0x00000100, RstStopMaster = 0x00000200, Ctl = 0x0024, CtlClockReady = 0x00000001, CtlAllowStandby = 0x00000002, CtlInit = 0x00000004, Io = 0x0030, IoRadioEnabled = 0x00000001, CmdBase = 0x0200, CmdSize = 0x0204, Tx1Base = 0x0208, Tx1Size = 0x020C, Tx2Base = 0x0210, Tx2Size = 0x0214, Tx3Base = 0x0218, Tx3Size = 0x021C, Tx4Base = 0x0220, Tx4Size = 0x0224, CmdReadX = 0x0280, Tx1ReadX = 0x0284, Tx2ReadX = 0x0288, Tx3ReadX = 0x028C, Tx4ReadX = 0x0290, RxReadX = 0x02A0, RxBase = 0x0500, Table0Size = 0x0700, Table0Base = 0x0704, CurTxRate = Table0Base, GetTable0Base = 0x0380, CurTable1Base = 0x0384, CurTable2Base = 0x0388, CurTable3Base = 0x038C, ErrorLog = 0x0610, CmdWriteX = 0x0F80, Tx1WriteX = 0x0F84, Tx2WriteX = 0x0F88, Tx3WriteX = 0x0F8C, Tx4WriteX = 0x0F90, RxWriteX = 0x0FA0, ReadInt = 0x0FF4, ReadIntInitHost = 0x20000000, /* indirect registers */ EeCtl = 0x00300040, EeClock = 1, EeSelect = 2, EeOut = 4, EeIn = 0x10, /* eeprom offsets */ EeMac = 0x21, /* header types */ HdrTypeData = 0, HdrTypeCmd = 1, HdrTypeNotify = 3, HdrTypeFrame = 9, /* header flags */ HdrFlagIrq = 0x04, /* Cmd.type */ CmdEnable = 2, CmdSetConfig = 6, CmdSetEssid = 8, CmdSetMacAddr = 11, CmdSetRtsThreshold = 15, CmdSetPower = 17, CmdSetWepKey = 18, CmdAssoc = 21, CmdSetRates = 22, CmdScanAbort = 23, CmdScan = 26, CmdDisable = 33, CmdSetIV = 34, CmdSetTxPower = 35, CmdSetSensitivity = 42, /* modes */ Mode11a = 0, Mode11b = 1, Mode11g = 2, /* Rates.type */ RatesNegotiated = 0, RatesSupported = 1, /* Power.power */ PowerMax = 20, /* Power.mode */ PowerModeCam = 0, /* Assoc.type */ Dissociate = 2, /* Assoc.auth */ AuthOpen = 0, AuthShared = 1, AuthNone = 3, /* Scan.type */ ScanPassive = 0x11, ScanDirected = 0x22, ScanBroadcast = 0x33, ScanBDirected = 0x44, /* Scan.channels indicies */ Chan5Ghz = 0<<6, Chan2Ghz = 1<<6, /* Wepkey.cmd */ WepkeySetKey = 8, /* Notify.type */ NotifyTypeAssociation = 10, NotifyTypeAuth = 11, NotifyTypeScanChannel = 12, NotifyTypeScanComplete = 13, NotifyTypeBeacon = 17, NotifyTypeCalibration = 20, NotifyTypeNoise = 25, /* NotifyAuth.state */ Deauthenticated = 0, Authenticated = 9, /* NotifyAssociation.state */ Dissociated = 0, Associated = 12, /* Td.cmd */ TdCmdTx = 0x0B, /* Td.flags */ TdFlagShPremable = 0x04, TdFlagNoWep = 0x20, TdFlagNeedAck = 0x80, /* Td.nseg */ MaxSeg = 6, /* firmware command blocks */ CbDefaultCtl = 0x8CEA0000, CbMaxData = 8191, /* driver constants */ Nrate = 16, ModeSta = 0, ModeIbss = 1, /* Ctlr.state flags */ CmdPending = 1<<0, Scanning = 1<<1, ScanAbort = 1<<2, RfDisabled = 1<<3, FwInited = 1<<4, FwIbss = 1<<5, Wep = 1<<6, ScanComplete = 1<<7, Okay = 1<<8, /* generic 802.11 constants */ Waddrlen = 6, Wkeylen = 16, Wessidlen = 32, Wnwep = 4, /* firmware file header size */ FwHeader = 8, }; typedef struct Assoc Assoc; typedef struct Cmd Cmd; typedef struct Config Config; typedef struct Frame Frame; typedef struct Fwstation Fwstation; typedef struct Hdr Hdr; typedef struct Notify Notify; typedef struct NotifyNoise NotifyNoise; typedef struct NotifyAuth NotifyAuth; typedef struct NotifyAssoc NotifyAssoc; typedef struct NotifyScanChannel NotifyScanChannel; typedef struct NotifyScanComplete NotifyScanComplete; typedef struct Power Power; typedef struct Rd Rd; typedef struct Rates Rates; typedef struct Scan Scan; typedef struct Td Td; typedef struct WFrame WFrame; typedef struct WKey WKey; typedef struct WepKey WepKey; typedef struct WQFrame4 WQFrame4; /* * IEEE 802.11 structures. */ #pragma pack on struct WFrame { u8int fc[2]; u8int dur[2]; u8int addr1[Waddrlen]; u8int addr2[Waddrlen]; u8int addr3[Waddrlen]; u8int seq[2]; }; struct WQFrame4 { u8int fc[2]; u8int dur[2]; u8int addr1[Waddrlen]; u8int addr2[Waddrlen]; u8int addr3[Waddrlen]; u8int seq[2]; u8int addr4[Waddrlen]; u8int qos[2]; }; #pragma pack off /* * Device structures. */ #pragma pack on struct Hdr { u8int type; u8int seq; u8int flags; u8int pad; }; struct Cmd { Hdr hdr; u8int type; u8int len; u16int reserved; u8int data[120]; }; struct Notify { u32int reserved[2]; u8int type; u8int flags; u16int len; }; struct NotifyNoise { u32int value; }; struct NotifyAuth { u8int state; }; struct NotifyAssoc { u8int state; WFrame frame; u16int capinfo; u16int status; u16int associd; }; struct NotifyScanChannel { u8int nchan; u8int reserved[47]; }; struct NotifyScanComplete { u8int type; u8int nchan; u8int status; u8int reserved; }; struct Frame { u32int reserved; u8int parenttsf[4]; u8int chan; u8int status; u8int rate; u8int rssi; u8int agc; u8int rssidbm; u16int signal; u16int noise; u8int antenna; u8int control; u8int rtsctsrate; u8int rtsctsseen; u16int len; }; struct Td { /* 0 */ Hdr hdr; u32int workptr; /* 8 */ u8int station; u8int reserved[3]; u8int cmd; u8int seq; u16int len; /* 16 */ u8int priority; u8int flags; u8int xflags; u8int weptxkey; /* 20 */ u8int wepkey[Wkeylen]; /* 36 */ u8int rate; u8int antenna; u8int reserved2[10]; /* 48 */ WQFrame4 wqhdr; /* 80 */ u32int iv[2]; /* 88 */ u32int nseg; /* 92 */ u32int segaddr[MaxSeg]; /* 116 */ u32int seglen[MaxSeg]; /* 140 */ }; struct Rates { u8int mode; u8int nrates; u8int type; u8int reserved; u8int rates[12]; }; struct Power { u8int nchan; u8int mode; struct { u8int chan; u8int power; } chan[37]; }; struct Assoc { u8int chan; u8int auth; u8int type; u8int reserved; u16int policy; u8int plen; u8int mode; u8int bssid[Waddrlen]; u8int tstamp[8]; u16int capinfo; u16int lintval; u16int intval; u8int dst[Waddrlen]; u16int atim_window; u8int smr; u8int reserved1; u16int reserved2; }; struct Scan { u32int index; u8int channels[54]; u8int type[26]; u8int reserved[4]; u16int passive; u16int directed; u16int broadcast; u16int bdirected; }; struct Config { u8int bluetoothcoexistence; u8int reserved1; u8int answerbroadcastprobe; u8int allowinvalidframes; u8int enablemulticast; u8int excludeunicastunencrypted; u8int disableunicastdecryption; u8int excludemulticastunencrypted; u8int disablemulticastdecryption; u8int antenna; u8int passcrctohost; u8int bgautodetect; u8int enablectstoself; u8int enablemulticastfiltering; u8int bluetooththreshold; u8int reserved2; u8int allowbeaconandproberesp; u8int allowmgt; u8int noisereported; u8int reserved3; }; struct WepKey { u8int cmd; u8int seq; u8int idx; u8int len; u8int key[Wkeylen]; }; struct Fwstation { u8int mac[Eaddrlen]; u8int reserved; u8int supportmode; }; #pragma pack off struct WKey { char key[Wkeylen]; int len; }; /* * Driver data */ typedef struct Ctlr Ctlr; struct Ctlr { Lock; Lock tlock; int port; Pcidev* pcidev; Ctlr* next; int active; int alloc; QLock alock; int id; int mbps; uchar ea[Eaddrlen]; ushort eeprom[Neeprom]; uint *mmio; uint state; uint rxwaiting; Rendez cmddone; Rendez scanning; Rendez fwinited; Rendez rxrendez; Config cfg; Scan scan; Assoc assoc; Cmd cmd[NCmdRing]; uint cmdlo; uint cmdhi; uint cmdpend; Td td[NTdRing]; Block *tb[NTdRing]; uint txlo; uint txhi; Block *rb[NRdRing]; uint rxlo; uint rxhi; uint opmode; uint rtsthreshold; char essid[Wessidlen]; int essidlen; WKey keys[4]; }; extern uchar ipw2200_bootcode[]; extern uint ipw2200_bootlen; extern uchar ipw2200_bsscode[]; extern uint ipw2200_bsslen; extern uchar ipw2200_ibsscode[]; extern uint ipw2200_ibsslen; extern uchar ipw2200_monitorcode[]; extern uint ipw2200_monitorlen; extern uchar ipw2200_ucode_bsscode[]; extern uint ipw2200_ucode_bsslen; extern uchar ipw2200_ucode_ibsscode[]; extern uint ipw2200_ucode_ibsslen; extern uchar ipw2200_ucode_monitorcode[]; extern uint ipw2200_ucode_monitorlen; static Ctlr *ctlrhead; static Ctlr *ctlrtail; static u8int rates11a[] = { 12, 18, 24, 36, 48, 72, 96, 108 }; static u8int rates11b[] = { 2, 4, 11, 22 }; static u8int rates11g[] = { 130, 132, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; static Config defaultcfg = { .bluetoothcoexistence = 1, .bgautodetect = 1, .enablemulticast = 1, .disableunicastdecryption = 1, //.disablemulticastdecryption = 1, .allowmgt = 1, }; #define r32(c, r) ((c)->mmio[(r)/4]) #define r16(c, r) (((u16int*)(c)->mmio)[(r)/2]) #define r8(c, r) (((u8int*)(c)->mmio)[(r)]) #define w32(c, r, v) (r32(c, r) = (v)) #define w16(c, r, v) (r16(c, r) = (v)) #define w8(c, r, v) (r8(c, r) = (v)) static int reset(Ctlr*); static int init(Ctlr*); static void stop(Ctlr*); static void stopmaster(Ctlr*); static void interrupt(Ureg*, void*); static int abortscan(Ctlr*); static int loaducode(Ctlr*, void*, int); static int loadfirmware(Ctlr*, void*, int); static int config(Ctlr*); static int setchan(Ctlr*, int); static int init(Ctlr*); static int dissociate(Ctlr*); static void transmit(Ether*); static void iw32(Ctlr *ctlr, u32int r, u32int v) { w32(ctlr, IndirectAddr, r); w32(ctlr, IndirectData, v); } static void iw16(Ctlr *ctlr, u32int r, u16int v) { w32(ctlr, IndirectAddr, r); w16(ctlr, IndirectData, v); } static void iw8(Ctlr *ctlr, u32int r, u8int v) { w32(ctlr, IndirectAddr, r); w8(ctlr, IndirectData, v); } static u32int ir32(Ctlr *ctlr, u32int r) { w32(ctlr, IndirectAddr, r); return r32(ctlr, IndirectData); } static u16int ir16(Ctlr *ctlr, u32int r) { w32(ctlr, IndirectAddr, r); return r16(ctlr, IndirectData); } static u8int ir8(Ctlr *ctlr, u32int r) { w32(ctlr, IndirectAddr, r); return r8(ctlr, IndirectData); } /* XXX check if this matches any other driver */ static void eectl(Ctlr *ctlr, int v) { iw32(ctlr, EeCtl, v); microdelay(1); } static void eew(Ctlr *ctlr, int b) { if(b) b = EeOut; eectl(ctlr, EeSelect|b); eectl(ctlr, EeSelect|EeClock|b); } static int eer(Ctlr *ctlr) { eectl(ctlr, EeSelect|EeClock); eectl(ctlr, EeSelect); return ir32(ctlr, EeCtl) & EeIn; } static ushort eeread(Ctlr *ctlr, int addr) { int bit; ushort x; /* reset */ eectl(ctlr, 0); eew(ctlr, 0); eectl(ctlr, EeSelect); /* start bit*/ eew(ctlr, 1); /* read - 10*/ eew(ctlr, 1); eew(ctlr, 0); /* address */ for(bit=0x80; bit>0; bit>>=1) eew(ctlr, addr&bit); eectl(ctlr, EeSelect); /* read data */ x = 0; for(bit=0x8000; bit>0; bit>>=1) if(eer(ctlr)) x |= bit; eectl(ctlr, 0); eectl(ctlr, EeSelect); eectl(ctlr, 0); eectl(ctlr, EeClock); return x; } /* * Rendezvous predicates. */ static int isdone(void *v) { Ctlr *ctlr; ctlr = v; return r32(ctlr, CmdReadX) == ctlr->cmdpend; } static int isfwinited(void *v) { Ctlr *ctlr; ctlr = v; return ctlr->state&FwInited; } static int isscanning(void *v) { Ctlr *ctlr; ctlr = v; return ctlr->state&Scanning; } static int rxwaiting(void *v) { Ctlr *ctlr; ctlr = v; if(!(ctlr->state&Okay)) return 0; return ctlr->rxwaiting; } static void printhex(void *v, int n) { int i; uint *a; a = v; for(i=0; i*4cmd[ctlr->cmdhi]; memset(cmd, 0, sizeof *cmd); if(++ctlr->cmdhi == NCmdRing) ctlr->cmdhi = 0; ctlr->cmdpend = ctlr->cmdhi; cmd->hdr.type = HdrTypeCmd; cmd->hdr.flags = HdrFlagIrq; cmd->type = type; cmd->len = len; memmove(cmd->data, data, len); coherence(); w32(ctlr, CmdWriteX, ctlr->cmdhi); } /* * Issue a command and wait for it. */ static int cmd(Ctlr *ctlr, uchar cmd, void *data, uint len) { if(r32(ctlr, CmdReadX) != r32(ctlr, CmdWriteX)) tsleep(&ctlr->cmddone, isdone, ctlr, 1000); if(r32(ctlr, CmdReadX) != r32(ctlr, CmdWriteX)){ iprint("cmd still pending\n"); w32(ctlr, CmdReadX, r32(ctlr, CmdWriteX)); } acmd(ctlr, cmd, data, len); tsleep(&ctlr->cmddone, isdone, ctlr, 1000); if(r32(ctlr, CmdReadX) != ctlr->cmdpend){ iprint("ipw2200: cmd timeout\n"); return -1; } return 0; } /* * Reset controller - okay for interrupts */ static int reset(Ctlr* ctlr) { int i, ntry; iprint("reset..."); stopmaster(ctlr); w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlInit); w32(ctlr, ReadInt, ReadIntInitHost); for(ntry=0; ntry<1000; ntry++){ if(r32(ctlr, Ctl) & CtlClockReady) break; microdelay(200); } if(ntry == 1000){ print("ipw2200: timeout\n"); return -1; } w32(ctlr, Rst, r32(ctlr, Rst)|RstSwReset); microdelay(10); w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlInit); w32(ctlr, AutoincAddr, 0); for(i=0; i<0xC000; i++) w32(ctlr, AutoincData, 0); for(i=0; ieeprom[i] = eeread(ctlr, i); ctlr->mbps = 11; for(i=0; i<6; i+=2){ ctlr->ea[i] = ctlr->eeprom[EeMac+i/2]; ctlr->ea[i+1] = ctlr->eeprom[EeMac+i/2]>>8; } ctlr->cfg = defaultcfg; return 0; } /* * Stop controller - okay for interrupts. */ static void stop(Ctlr *ctlr) { stopmaster(ctlr); w32(ctlr, Rst, RstSwReset); ctlr->state &= ~Okay; wakeup(&ctlr->rxrendez); /* XXX free anything in rings */ } /* * Stop master - okay for interrupts. */ static void stopmaster(Ctlr *ctlr) { int i; w32(ctlr, IntrMask, 0); w32(ctlr, Rst, RstStopMaster); for(i=0; i<5; i++){ if(r32(ctlr, Rst) & RstMasterDisabled) break; microdelay(10); } if(i == 5) print("ipw2200: master timeout\n"); w32(ctlr, Rst, r32(ctlr, Rst)|RstPrincetonReset); ctlr->state &= ~FwInited; } static void printerror(Ctlr *ctlr) { int i, j; u32int len, log, x[7]; print("fw error log\n"); log = r32(ctlr, ErrorLog); len = ir32(ctlr, log); w32(ctlr, AutoincAddr, log+4); for(i=0; ictlr; ilock(ctlr); cause = r32(ctlr, Intr); handled = 0; if(cause == 0 || cause == 0xFFFFFFFF){ iunlock(ctlr); return; } print("ipw interrupt %.8ux\n", cause); // w32(ctlr, IntrMask, 0); if(cause & (IntrFatalError|IntrParityError)){ handled |= IntrFatalError|IntrParityError; print("fatal error: rx %d %d\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX)); for(i=0; irb[i]; print("\t%p %p %.8ux\n", bp, bp ? bp->wp : nil, r32(ctlr, RxBase+i*4)); } iprint("ipw2200: fatal error %.8ux\n", cause); printerror(ctlr); stop(ctlr); } if(cause & IntrRadioOff){ iprint("R"); handled |= IntrRadioOff; stop(ctlr); } if(cause & IntrFwInit){ handled |= IntrFwInit; // ctlr->fwintr++; iprint("F"); ctlr->state |= FwInited; wakeup(&ctlr->fwinited); } if(cause & IntrCmd){ handled |= IntrCmd; // ctlr->cmdintr++; iprint("C"); ctlr->cmdlo = r32(ctlr, CmdReadX); ctlr->state &= ~CmdPending; wakeup(&ctlr->cmddone); } if(cause & IntrTx){ handled |= IntrTx; iprint("T"); // ctlr->txintr++; } if(cause & IntrRx){ handled |= IntrRx; iprint("R"); // ctlr->rxintr++; wakeup(&ctlr->rxrendez); } w32(ctlr, Intr, cause); w32(ctlr, IntrMask, IntrDefault); if(cause & ~handled) iprint("ipw2200: did not handle %#.8ux\n", cause & ~handled); iunlock(ctlr); if(cause & IntrTx) transmit(edev); } /* * Abort scan - NOT okay for interrupts. */ static int abortscan(Ctlr *ctlr) { ctlr->state |= ScanAbort; return cmd(ctlr, CmdScanAbort, nil, 0); } static struct { int card; int mbps; } rateconv[] = { 10, 2, 20, 4, 55, 11, 110, 22, 13, 12, 15, 18, 5, 24, 7, 36, 9, 48, 11, 72, 1, 96, 3, 108, }; static int rate2rate(int r) { int i; for(i=0; i0; w++, size-=2) iw16(ctlr, 0x200010, *w); iw8(ctlr, 0x200000, 0x00); iw8(ctlr, 0x200000, 0x80); /* wait for a response in the microcode queue */ for(i=0; i<100; i++){ if(ir8(ctlr, 0x200000) & 1) break; microdelay(100); } if(i == 100){ print("ipw2200: microcode intialization timeout: %#.8ux %#.2ux\n", ir32(ctlr, 0x200000), ir8(ctlr, 0x200000)); for(i=0; i<7; i++) print("%.8ux...", ir32(ctlr, 0x200004)); return -1; } /* empty microcode queue */ for(i=0; i<7; i++) ir32(ctlr, 0x200004); iw8(ctlr, 0x200000, 0x00); return 0; } /* * Load firmware - NOT okay for interrupts. */ #define LE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) static int loadfirmware(Ctlr *ctlr, void *fw, int size) { int i; u32int sentinel, ctl, src, dst, len, mlen; uchar *p, *end; if((ulong)fw&3){ print("ipw2200: bad firmware alignment\n"); return -1; } ilock(ctlr); ctlr->state &= ~FwInited; /* Tell the adapter where the command blocks are stored */ iw32(ctlr, 0x3000A0, 0x27000); /* * Store command blocks into adapter's internal memory using register * indirections. The adapter will read the firmware image through DMA * using information stored in command blocks. */ src = PADDR(fw); p = fw; end = p+size; w32(ctlr, AutoincAddr, 0x27000); while(p < end){ dst = LE32(p); len = LE32(p+4); p += 8+len; src += 8; while(len > 0){ mlen = MIN(len, CbMaxData); ctl = CbDefaultCtl | mlen; w32(ctlr, AutoincData, ctl); w32(ctlr, AutoincData, src); w32(ctlr, AutoincData, dst); w32(ctlr, AutoincData, ctl^src^dst); src += mlen; dst += mlen; len -= mlen; } } /* Write a fictive final command block (sentinel) */ sentinel = r32(ctlr, AutoincAddr); w32(ctlr, AutoincData, 0); w32(ctlr, Rst, r32(ctlr, Rst)&~(RstMasterDisabled|RstStopMaster)); /* Tell the adapter to start processing command blocks */ iw32(ctlr, 0x3000A4, 0x540100); /* Wait until the adapter has processed all command blocks */ for(i=0; i<400; i++){ if(ir32(ctlr, 0x3000D0) >= sentinel) break; microdelay(100); } if(i == 400){ iprint("ipw2200: loadfirmware timeout\n"); iunlock(ctlr); return -1; } /* We're done with command blocks processing */ iw32(ctlr, 0x3000A4, 0x540C00); /* Allow interrupts so we know when the firmware is inited */ w32(ctlr, IntrMask, IntrDefault); /* Tell the adapter to initialize the firmware */ w32(ctlr, Rst, 0); w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlAllowStandby); /* Wait at most one second for firmware initialization to complete */ iunlock(ctlr); tsleep(&ctlr->fwinited, isfwinited, ctlr, 1000); if(!(ctlr->state&FwInited)){ iprint("ipw2200: fw not inited\n"); return -1; } return 0; } static int scan(Ctlr*); /* * Configure card - NOT okay for interrupts. */ static int config(Ctlr *ctlr) { int i; u32int data; Power power; Rates rates; WepKey wepkey; WKey *key; if(cmd(ctlr, CmdSetMacAddr, ctlr->ea, Eaddrlen)) return -1; if(cmd(ctlr, CmdSetConfig, &ctlr->cfg, sizeof ctlr->cfg)) return -1; data = PowerModeCam; if(cmd(ctlr, CmdSetPower, &data, sizeof data)) return -1; data = ctlr->rtsthreshold; if(cmd(ctlr, CmdSetRtsThreshold, &data, sizeof data)) return -1; if(ctlr->opmode == ModeIbss){ power.nchan = 11; for(i=0; i<11; i++){ power.chan[i].chan = i+1; power.chan[i].power = PowerMax; } power.mode = Mode11b; if(cmd(ctlr, CmdSetTxPower, &power, sizeof power)) return -1; power.mode = Mode11g; if(cmd(ctlr, CmdSetTxPower, &power, sizeof power)) return -1; } rates.mode = Mode11g; rates.type = RatesSupported; rates.nrates = nelem(rates11g); memmove(rates.rates, rates11g, sizeof rates11g); if(cmd(ctlr, CmdSetRates, &rates, sizeof rates)) return -1; switch(ctlr->id){ case 0x4220: break; default: rates.mode = Mode11a; rates.type = RatesSupported; rates.nrates = nelem(rates11a); memmove(rates.rates, rates11a, sizeof rates11a); if(cmd(ctlr, CmdSetRates, &rates, sizeof rates)) return -1; break; } if(ctlr->essidlen){ if(cmd(ctlr, CmdSetEssid, ctlr->essid, ctlr->essidlen)) return -1; } data = 0; if(!waserror()){ randomread(&data, sizeof data); poperror(); } if(cmd(ctlr, CmdSetIV, &data, sizeof data)) return -1; if(ctlr->state & Wep){ for(i=0; ikeys[i]; wepkey.cmd = WepkeySetKey; wepkey.idx = i; wepkey.len = key->len; wepkey.seq = 0; memset(wepkey.key, 0, sizeof wepkey.key); memmove(wepkey.key, key->key, key->len); if(cmd(ctlr, CmdSetWepKey, &wepkey, sizeof wepkey)) return -1; } } if(cmd(ctlr, CmdEnable, nil, 0)) return -1; /* * Must start scan immediately -- card misbehaves if we wait. */ scan(ctlr); return 0; } static void replenish(Ctlr*); /* * Initialize controller. */ static int init(Ctlr *ctlr) { int i; Block *bp; iprint("init..."); stop(ctlr); if(reset(ctlr)) goto error; if(loadfirmware(ctlr, ipw2200_bootcode+FwHeader, ipw2200_bootlen-FwHeader)) goto error; if(loaducode(ctlr, ipw2200_ucode_bsscode+FwHeader, ipw2200_ucode_bsslen-FwHeader)) goto error; stopmaster(ctlr); ctlr->cmdlo = 0; ctlr->cmdhi = 0; w32(ctlr, CmdBase, PADDR(ctlr->cmd)); w32(ctlr, CmdSize, NCmdRing); w32(ctlr, CmdReadX, ctlr->cmdlo); w32(ctlr, CmdWriteX, ctlr->cmdhi); ctlr->txlo = 0; ctlr->txhi = 0; w32(ctlr, Tx1Base, PADDR(ctlr->td)); w32(ctlr, Tx1Size, NTdRing); w32(ctlr, Tx1ReadX, ctlr->txlo); w32(ctlr, Tx1WriteX, ctlr->txhi); w32(ctlr, Tx2Base, PADDR(ctlr->td)); w32(ctlr, Tx2Size, NTdRing); w32(ctlr, Tx2ReadX, 0); w32(ctlr, Tx2WriteX, 0); w32(ctlr, Tx3Base, PADDR(ctlr->td)); w32(ctlr, Tx3Size, NTdRing); w32(ctlr, Tx3ReadX, 0); w32(ctlr, Tx3WriteX, 0); w32(ctlr, Tx4Base, PADDR(ctlr->td)); w32(ctlr, Tx4Size, NTdRing); w32(ctlr, Tx4ReadX, 0); w32(ctlr, Tx4WriteX, 0); ctlr->rxlo = 0; ctlr->rxhi = 0; replenish(ctlr); for(i=0; irb[i]) w32(ctlr, RxBase+i*4, PADDR(bp->wp)); else w32(ctlr, RxBase+i*4, 0); } w32(ctlr, RxReadX, ctlr->rxlo); w32(ctlr, RxWriteX, ctlr->rxhi); if(loadfirmware(ctlr, ipw2200_bsscode, ipw2200_bsslen)) goto error; ctlr->state &= ~(Scanning|ScanComplete|ScanAbort|Associated); ctlr->state |= Okay; if(config(ctlr)) goto error; return 0; error: stop(ctlr); return -1; } /* * NOT okay for interrupts */ static int dissociate(Ctlr *ctlr) { Assoc assoc; memset(&assoc, 0, sizeof assoc); assoc.type = Dissociate; return cmd(ctlr, CmdAssoc, &assoc, sizeof assoc); } static int setchannel(Ctlr *ctlr, int channel) { Scan *scan; scan = &ctlr->scan; memset(scan, 0, sizeof(Scan)); memset(scan->type, ScanPassive, sizeof scan->type); scan->passive = 2000; scan->channels[0] = 1 | Chan2Ghz; scan->channels[1] = channel; acmd(ctlr, CmdScan, scan, sizeof(Scan)); return 0; } static int associate(Ctlr *ctlr) { Assoc *assoc; memset(&ctlr->assoc, 0, sizeof ctlr->assoc); assoc = &ctlr->assoc; assoc->mode = Mode11g; assoc->capinfo = 1; // CapEss; assoc->chan = 1; // channel /* XXX set assoc->bssid! */ acmd(ctlr, CmdAssoc, assoc, sizeof(Assoc)); return 0; } static int scan(Ctlr *ctlr) { int i; uchar *p; Scan *scan; memset(&ctlr->scan, 0, sizeof ctlr->scan); scan = &ctlr->scan; if(ctlr->essidlen){ scan->bdirected = 100; /* XXX ctlr->dwelltime */ memset(scan->type, ScanBDirected, sizeof scan->type); }else{ scan->broadcast = 100; memset(scan->type, ScanBroadcast, sizeof scan->type); } scan->passive = 20; // scan->directed = 20; scan->bdirected = 20; scan->broadcast = 20; p = scan->channels; // *p++ = Chan5Ghz | 0; *p++ = Chan2Ghz | 11; for(i=1; i<=11; i++) *p++ = i; acmd(ctlr, CmdScan, scan, sizeof(Scan)); return 0; } static void filltd(Td *td, Block *bp) { /* copy ethernet header on bp to Td.wqhdr */ /* fill out td */ memset(td, 0, sizeof *td); td->hdr.type = HdrTypeData; td->hdr.flags = HdrFlagIrq; td->len = BLEN(bp); td->flags = TdFlagNeedAck; td->nseg = 1; td->segaddr[0] = PADDR(bp->rp); td->seglen[0] = BLEN(bp); } static void transmit(Ether* edev) { uint x; Block *bp; Ctlr *ctlr; ctlr = edev->ctlr; ilock(&ctlr->tlock); /* * Free any completed packets. */ x = ctlr->txlo; while(x != r32(ctlr, Tx1ReadX)){ bp = ctlr->tb[x]; ctlr->tb[x] = nil; freeb(bp); iprint("!"); if(++x == NTdRing) x = 0; } ctlr->txlo = x; /* * Try to fill the ring back up. */ x = ctlr->txhi; while((x+1)%NTdRing != r32(ctlr, Tx1ReadX)){ if((bp = qget(edev->oq)) == nil) break; ctlr->tb[x] = bp; filltd(&ctlr->td[x], bp); iprint(">"); if(++x == NTdRing) x = 0; } ctlr->txhi = x; w32(ctlr, Tx1WriteX, x); iunlock(&ctlr->tlock); } static Lock rblock; static Block *rbpool; static Block* rballoc(void) { Block *bp; ilock(&rblock); if((bp = rbpool) != nil){ rbpool = rbpool->next; bp->next = nil; } iunlock(&rblock); return iallocb(Rbsz); } static void rbfree(Block *bp) { bp->rp = bp->lim - Rbsz; bp->wp = bp->rp; ilock(&rblock); bp->next = rbpool; rbpool = bp; iunlock(&rblock); } static void replenish(Ctlr *ctlr) { uint x; Block *bp; print("replenish %d %d\n", ctlr->rxhi, r32(ctlr, RxReadX)); x = ctlr->rxhi; while((x+1)%NRdRing != ctlr->rxlo){ if((bp = ctlr->rb[x]) == nil){ if((bp = rballoc()) == nil) break; iprint("#"); ctlr->rb[x] = bp; } w32(ctlr, RxBase+x*4, PADDR(bp->wp)); if(++x == NRdRing) x = 0; } ctlr->rxhi = x; print("replenish => %d\n", x); w32(ctlr, RxWriteX, x); } static void rxnotify(Ctlr *ctlr, Notify *n) { NotifyScanChannel *chan; NotifyScanComplete *done; NotifyAuth *auth; NotifyAssoc *assoc; print("notify: subtype=%.2ux flags=%.2ux size=%d\n", n->type, n->flags, n->len); printhex(n+1, n->len); switch(n->type){ case NotifyTypeAssociation: assoc = (NotifyAssoc*)(n+1); print("assoc %d %d\n", assoc->state, assoc->status); switch(assoc->state){ case Authenticated: print("assoc auth\n"); break; case Associated: print("assoc yes\n"); break; case Dissociated: print("assoc no\n"); break; } break; case NotifyTypeAuth: auth = (NotifyAuth*)(n+1); switch(auth->state){ case Authenticated: print("authenticated\n"); break; case Deauthenticated: print("deauthenticated\n"); break; default: print("auth state %d\n", auth->state); break; } break; case NotifyTypeScanChannel: chan = (NotifyScanChannel*)(n+1); print("scanning %d\n", chan->nchan); break; case NotifyTypeScanComplete: done = (NotifyScanComplete*)(n+1); USED(done); USED(ctlr); // setchan(ctlr, ctlr->chan); print("scan done\n"); break; case NotifyTypeBeacon: case NotifyTypeCalibration: case NotifyTypeNoise: print("notify %d\n", n->type); break; } } static int rxframe(Ether *edev, Block *bp) { int i; uint *x; Hdr *h; Frame *f; WFrame *wf; h = (Hdr*)bp->rp; f = (Frame*)(h+1); print("rx len=%d chan=%d rssi=%d\n", f->len, f->chan, f->rssi); if((uchar*)(f+1)+f->len > bp->lim){ print("pkt too long\n"); return 0; } wf = (WFrame*)(f+1); bp->rp = (uchar*)(wf+1); x = (uint*)wf; print("wframe:"); for(i=0; i<8; i++) print(" %.8ux", x[i]); print("\ndata: "); x = (uint*)bp->rp; for(i=0; i<8; i++) print(" %.8ux", x[i]); print("\n"); etheriq(edev, bp, 1); return 1; } static void rxproc(void *arg) { int avail; uint x, end; Block *bp; Ctlr *ctlr; Ether *edev; Hdr *hdr; edev = arg; ctlr = edev->ctlr; replenish(ctlr); for(;;){ // ctlr->rxsleep++; sleep(&ctlr->rxrendez, rxwaiting, ctlr); if(!(ctlr->state&Okay)) continue; ctlr->rxwaiting = 0; x = ctlr->rxlo; end = r32(ctlr, RxReadX); if(0 <= end && end < NRdRing) while(x != end){ bp = ctlr->rb[x]; if(bp == nil){ /* oops! */ iprint("?"); break; } iprint("<"); hdr = (Hdr*)bp->rp; switch(hdr->type){ case HdrTypeNotify: rxnotify(ctlr, (Notify*)(hdr+1)); break; case HdrTypeFrame: // ctlr->rxpackets++; if(rxframe(edev, bp)) ctlr->rb[x] = nil; break; } if(ctlr->rb[x]){ rbfree(ctlr->rb[x]); ctlr->rb[x] = nil; } w32(ctlr, RxBase+x*4, 0); if(++x == NRdRing) x = 0; } print("rxproc: lo %d nlo %d end %d hi %d\n", ctlr->rxlo, x, end, ctlr->rxhi); print("rxproc: card %d %d\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX)); ctlr->rxlo = x; avail = (ctlr->rxhi+NRdRing - end) % NRdRing; if(avail < NRdRing/2) replenish(ctlr); } } static void attach(Ether* edev) { int i; Ctlr *ctlr; ctlr = edev->ctlr; qlock(&ctlr->alock); if(ctlr->alloc){ qunlock(&ctlr->alock); return; } if(reset(ctlr)) goto error; ctlr->rtsthreshold = 0x900; /* ctlr->opmode = Station ctlr->xstate = XXX; caps = XXX; */ if(init(ctlr)) goto error; kproc("ipw2200rxproc", rxproc, edev); ctlr->alloc = 1; qunlock(&ctlr->alock); return; error: stop(ctlr); for(i=0; irb[i]){ freeb(ctlr->rb[i]); ctlr->rb[i] = nil; } } qunlock(&ctlr->alock); } static void shutdown(Ether* ether) { Ctlr *ctlr; iprint("shutdown..."); ctlr = ether->ctlr; qlock(&ctlr->alock); if(!ctlr->alloc){ qunlock(&ctlr->alock); return; } stop(ctlr); free(ctlr->cmd); free(ctlr->td); /* free(ctlr->rd); */ ctlr->alloc = 0; qunlock(&ctlr->alock); } static long ifstat(Ether* edev, void* a, long n, ulong offset) { char *p; int i, l, m, x; Ctlr *ctlr; ctlr = edev->ctlr; if(ctlr == nil) error(Enonexist); if(n == 0) return 0; m = READSTR*2; p = malloc(m); iprint("ctlr %p.", ctlr); l = 0; l += snprint(p+l, m-l, "CurTxRate: %ud\n", r32(ctlr, CurTxRate)); l += snprint(p+l, m-l, "ctlr cmd: %ud-%ud\n", ctlr->cmdlo, ctlr->cmdhi); x = r32(ctlr, CmdReadX); if(x < 0 || x >= NCmdRing) x = 0; iprint("x %d.", x); iprint("addr %p.", &ctlr->cmd[x]); iprint("t %d.", ctlr->cmd[x].hdr.type); iprint("t %d.", ctlr->cmd[x].type); l += snprint(p+l, m-l, "card cmd: %ud-%ud (current %ud %ud)\n", x, r32(ctlr, CmdWriteX), ctlr->cmd[x].hdr.type, ctlr->cmd[x].type); l += snprint(p+l, m-l, "ctlr tx1: %ud-%ud\n", ctlr->txlo, ctlr->txhi); l += snprint(p+l, m-l, "card tx1: %ud-%ud\n", r32(ctlr, Tx1ReadX), r32(ctlr, Tx1WriteX)); l += snprint(p+l, m-l, "ctlr rx: %ud-%ud\n", ctlr->rxlo, ctlr->rxhi); l += snprint(p+l, m-l, "card rx: %ud-%ud\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX)); for(i=0; irxlo, ctlr->rxhi); l += snprint(p+l, m-l, "ctlr tx: %ud-%ud\n", ctlr->txlo, ctlr->txhi); l += snprint(p+l, m-l, "rom:"); for(i = 0; i < Neeprom; i++){ if(i && (i&7)==0) l += snprint(p+l, m-l, "\n "); l += snprint(p+l, m-l, " %4.4ux", ctlr->eeprom[i]); } l += snprint(p+l, m-l, "\n"); USED(l); n = readstr(offset, a, n, p); free(p); return n; } enum { CMabortscan, CMassociate, CMchannel, CMconfig, CMdissociate, CMessid, CMinit, CMnop, CMrssi, CMscan, CMstop, }; static Cmdtab ipw2200msg[] = { CMabortscan, "abortscan", 1, CMassociate, "associate", 1, CMchannel, "channel", 2, CMconfig, "config", 1, CMdissociate, "dissociate", 1, CMessid, "essid", 2, CMinit, "init", 1, CMnop, "nop", 0, CMrssi, "rssi", 2, CMscan, "scan", 1, CMstop, "stop", 1, }; static long wctl(Ether* edev, void* buf, long n) { u32int x; Cmdbuf *cb; Cmdtab *ct; Ctlr *ctlr; ctlr = edev->ctlr; if(ctlr == nil) error(Enonexist); cb = parsecmd(buf, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, ipw2200msg, nelem(ipw2200msg)); switch(ct->index){ case CMabortscan: if(abortscan(ctlr)) error("error in abortscan"); break; case CMassociate: if(associate(ctlr)) error("error in associate"); break; case CMchannel: if(setchannel(ctlr, atoi(cb->f[1]))) error("error in setchannel"); break; case CMconfig: if(config(ctlr)) error("error in config"); break; case CMdissociate: if(dissociate(ctlr)) error("error dissociating"); break; case CMessid: if(strlen(cb->f[1]) >= sizeof ctlr->essid) error("essid too long"); strcpy(ctlr->essid, cb->f[1]); ctlr->essidlen = strlen(ctlr->essid); iprint("essid %q\n", ctlr->essid); if(cmd(ctlr, CmdSetEssid, ctlr->essid, ctlr->essidlen)) error("error setting essid"); break; case CMinit: if(init(ctlr)) error("error initing"); break; case CMnop: break; case CMrssi: x = strtol(cb->f[1], 0, 0); acmd(ctlr, CmdSetSensitivity, &x, sizeof x); break; case CMscan: if(scan(ctlr)) error("error scan"); break; case CMstop: stop(ctlr); break; } free(cb); poperror(); return n; } static void ipw2200pci(void) { int id; void *mem; Ctlr *ctlr; Pcidev *p; p = nil; while(p = pcimatch(p, 0, 0)){ if(p->ccrb != 0x02) continue; id = (p->did<<16)|p->vid; switch(id){ default: continue; case (0x4220<<16)|0x8086: /* Intel 2200BG */ case (0x4223<<16)|0x8086: /* Intel 2915ABG */ case (0x4224<<16)|0x8086: /* Intel 2915ABG */ break; } print("found %ux\n", id); mem = vmap(p->mem[0].bar&~0x0F, p->mem[0].size); if(mem == nil){ print("etheripw2200: can't map %#.8lux\n", p->mem[0].bar); continue; } ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil) return; ctlr->port = p->mem[0].bar&~0xF; ctlr->pcidev = p; ctlr->id = id; ctlr->mmio = mem; if(reset(ctlr)){ free(ctlr); continue; } pcisetbme(p); if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; } } static int ipw2200pnp(Ether* edev) { Ctlr *ctlr; uchar ea[Eaddrlen]; if(ctlrhead == nil) ipw2200pci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ 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->pcidev->intl; edev->tbdf = ctlr->pcidev->tbdf; edev->mbps = ctlr->mbps; /* * Check if the adapter's station address is to be overridden. * If not, read it from the EEPROM and set in ether->ea prior to * loading the station address in the hardware. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, edev->ea, Eaddrlen) == 0) memmove(edev->ea, ctlr->ea, Eaddrlen); /* * Linkage to the generic ethernet driver. */ edev->attach = attach; edev->transmit = transmit; edev->interrupt = interrupt; edev->ifstat = ifstat; edev->ctl = wctl; edev->shutdown = shutdown; edev->arg = edev; return 0; } void etheripw2200link(void) { addethercard("ipw2200", ipw2200pnp); }