From 2ec3d955ed85c7e82a96474fdc9e76bff258fe5c Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 6 Jun 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-06-06 --- bitsy/clock.c | 2 +- bitsy/devuda1341.c | 26 +- bitsy/etherwavelan.c | 1275 +------------------------------------- bitsy/fns.h | 1 - bitsy/sdata.c | 24 +- bitsy/wavelan.c | 1 + bitsy/wavelan.h | 4 + pc/etherwavelan.c | 1378 +----------------------------------------- pc/wavelan.c | 1211 ++++++++++++++++++++++++++++++++----- pc/wavelan.h | 304 ++++++++++ 10 files changed, 1427 insertions(+), 2799 deletions(-) create mode 100644 bitsy/wavelan.c create mode 100644 bitsy/wavelan.h create mode 100644 pc/wavelan.h diff --git a/bitsy/clock.c b/bitsy/clock.c index 08b0932bdfae6ab634bd9e938a3db15f0987c26d..8d6eb3bd2654ead6d302fd4a87f5a0e994719f0c 100644 --- a/bitsy/clock.c +++ b/bitsy/clock.c @@ -145,7 +145,7 @@ delay(int ms) } void -µdelay(ulong µs) +microdelay(int µs) { ulong start; int i; diff --git a/bitsy/devuda1341.c b/bitsy/devuda1341.c index bfe9d63cff4a7583c11c0cd547f26241e2e5ebf4..3b071eea05141cb4b84cbc97a7622667cc7b2636 100644 --- a/bitsy/devuda1341.c +++ b/bitsy/devuda1341.c @@ -246,7 +246,7 @@ L3_acquirepins(void) { gpioregs->set = (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io); gpioregs->direction |= (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io); - µdelay(L3_AcquireTime); + microdelay(L3_AcquireTime); } /* @@ -256,7 +256,7 @@ static void L3_releasepins(void) { gpioregs->direction &= ~(GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io); - µdelay(L3_ReleaseTime); + microdelay(L3_ReleaseTime); } /* @@ -279,12 +279,12 @@ L3_getbit(void) int data; gpioregs->clear = GPIO_L3_SCLK_o; - µdelay(L3_ClockLowTime); + microdelay(L3_ClockLowTime); data = (gpioregs->level & GPIO_L3_SDA_io) ? 1 : 0; gpioregs->set = GPIO_L3_SCLK_o; - µdelay(L3_ClockHighTime); + microdelay(L3_ClockHighTime); return data; } @@ -304,10 +304,10 @@ L3_sendbit(int bit) gpioregs->clear = GPIO_L3_SDA_io; /* Assumes L3_DataSetupTime < L3_ClockLowTime */ - µdelay(L3_ClockLowTime); + microdelay(L3_ClockLowTime); gpioregs->set = GPIO_L3_SCLK_o; - µdelay(L3_ClockHighTime); + microdelay(L3_ClockHighTime); } /* @@ -328,12 +328,12 @@ L3_sendbyte(char data, int mode) break; default: /* Subsequent bytes */ gpioregs->clear = GPIO_L3_MODE_o; - µdelay(L3_HaltTime); + microdelay(L3_HaltTime); gpioregs->set = GPIO_L3_MODE_o; break; } - µdelay(L3_ModeSetupTime); + microdelay(L3_ModeSetupTime); for (i = 0; i < 8; i++) L3_sendbit(data >> i); @@ -341,7 +341,7 @@ L3_sendbyte(char data, int mode) if (mode == 0) /* Address mode */ gpioregs->set = GPIO_L3_MODE_o; - µdelay(L3_ModeHoldTime); + microdelay(L3_ModeHoldTime); } /* @@ -364,17 +364,17 @@ L3_getbyte(int mode) break; default: /* Subsequent bytes */ gpioregs->clear = GPIO_L3_MODE_o; - µdelay(L3_HaltTime); + microdelay(L3_HaltTime); gpioregs->set = GPIO_L3_MODE_o; break; } - µdelay(L3_ModeSetupTime); + microdelay(L3_ModeSetupTime); for (i = 0; i < 8; i++) data |= (L3_getbit() << i); - µdelay(L3_ModeHoldTime); + microdelay(L3_ModeHoldTime); return data; } @@ -649,7 +649,7 @@ setreg(char *name, int val, int n) if(strcmp(name, "pause") == 0){ for(i = 0; i < n; i++) - µdelay(val); + microdelay(val); return; } diff --git a/bitsy/etherwavelan.c b/bitsy/etherwavelan.c index 16c93dc70337f1175987ca7e9ac0cd5598033f68..fabfb91a5c59dedee8c04bdaadc7381a63b94f26 100644 --- a/bitsy/etherwavelan.c +++ b/bitsy/etherwavelan.c @@ -1,22 +1,5 @@ -/* - Lucent Wavelan IEEE 802.11 pcmcia. - There is almost no documentation for the card. - the driver is done using both the FreeBSD, Linux and - original Plan 9 drivers as `documentation'. +/* Bitsy pcmcia code for wavelan.c */ - Has been used with the card plugged in during all up time. - no cards removals/insertions yet. - - For known BUGS see the comments below. Besides, - the driver keeps interrupts disabled for just too - long. When it gets robust, locks should be revisited. - - BUGS: check endian, alignment and mem/io issues; - multicast; - receive watchdog interrupts. - TODO: automatic power management; - improve locking. - */ #include "u.h" #include "../port/lib.h" #include "mem.h" @@ -26,1267 +9,35 @@ #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" - -#define DEBUG if(1){}else print - -#define SEEKEYS 0 - -// Lucent's Length-Type-Value records to talk to the wavelan. -// most operational parameters are read/set using this. -enum -{ - WTyp_Stats = 0xf100, - WTyp_Ptype = 0xfc00, - WTyp_Mac = 0xfc01, - WTyp_WantName = 0xfc02, - WTyp_Chan = 0xfc03, - WTyp_NetName = 0xfc04, - WTyp_ApDens = 0xfc06, - WTyp_MaxLen = 0xfc07, - WTyp_PM = 0xfc09, - WTyp_PMWait = 0xfc0c, - WTyp_NodeName = 0xfc0e, - WTyp_Crypt = 0xfc20, - WTyp_XClear = 0xfc22, - WTyp_CreateIBSS = 0xfc81, - WTyp_RtsThres = 0xfc83, - WTyp_TxRate = 0xfc84, - WTx1Mbps = 0x0, - WTx2Mbps = 0x1, - WTxAuto = 0x3, - WTyp_Prom = 0xfc85, - WTyp_Keys = 0xfcb0, - WTyp_TxKey = 0xfcb1, - WTyp_StationID = 0xfd20, - WTyp_CurName = 0xfd41, - WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station - WTyp_CurTxRate = 0xfd44, // Current TX rate - WTyp_HasCrypt = 0xfd4f, - WTyp_Tick = 0xfce0, -}; - -// Controller -enum -{ - WDfltIRQ = 3, // default irq - WDfltIOB = 0x180, // default IO base - - WIOLen = 0x40, // Hermes IO length - - WTmOut = 65536, // Cmd time out - - WPTypeManaged = 1, - WPTypeWDS = 2, - WPTypeAdHoc = 3, - WDfltPType = WPTypeManaged, - - WDfltApDens = 1, - WDfltRtsThres = 2347, // == disabled - WDfltTxRate = WTxAuto, // 2Mbps - - WMaxLen = 2304, - WNameLen = 32, - - WNKeys = 4, - WKeyLen = 14, - WMinKeyLen = 5, - - // Wavelan hermes registers - WR_Cmd = 0x00, - WCmdIni = 0x0000, - WCmdEna = 0x0001, - WCmdDis = 0x0002, - WCmdTx = 0x000b, - WCmdMalloc = 0x000a, - WCmdAskStats = 0x0011, - WCmdMsk = 0x003f, - WCmdAccRd = 0x0021, - WCmdReclaim = 0x0100, - WCmdAccWr = 0x0121, - WCmdBusy = 0x8000, - WR_Parm0 = 0x02, - WR_Parm1 = 0x04, - WR_Parm2 = 0x06, - WR_Sts = 0x08, - WR_InfoId = 0x10, - WR_Sel0 = 0x18, - WR_Sel1 = 0x1a, - WR_Off0 = 0x1c, - WR_Off1 = 0x1e, - WBusyOff = 0x8000, - WErrOff = 0x4000, - WResSts = 0x7f00, - WR_RXId = 0x20, - WR_Alloc = 0x22, - WR_EvSts = 0x30, - WR_IntEna = 0x32, - WCmdEv = 0x0010, - WRXEv = 0x0001, - WTXEv = 0x0002, - WTxErrEv = 0x0004, - WAllocEv = 0x0008, - WInfoEv = 0x0080, - WIDropEv = 0x2000, - WTickEv = 0x8000, - WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, - - WR_EvAck = 0x34, - WR_Data0 = 0x36, - WR_Data1 = 0x38, - - WR_PciCor = 0x26, - WR_PciHcr = 0x2E, - - // Frame stuff - - WF_Err = 0x0003, - WF_1042 = 0x2000, - WF_Tunnel = 0x4000, - WF_WMP = 0x6000, - - WF_Data = 0x0008, - - WSnapK1 = 0xaa, - WSnapK2 = 0x00, - WSnapCtlr = 0x03, - WSnap0 = (WSnapK1|(WSnapK1<<8)), - WSnap1 = (WSnapK2|(WSnapCtlr<<8)), - WSnapHdrLen = 6, - - WF_802_11_Off = 0x44, - WF_802_3_Off = 0x2e, - -}; - -typedef struct Ctlr Ctlr; -typedef struct Wltv Wltv; -typedef struct WFrame WFrame; -typedef struct Stats Stats; -typedef struct WStats WStats; -typedef struct WKey WKey; - -struct WStats -{ - ulong ntxuframes; // unicast frames - ulong ntxmframes; // multicast frames - ulong ntxfrags; // fragments - ulong ntxubytes; // unicast bytes - ulong ntxmbytes; // multicast bytes - ulong ntxdeferred; // deferred transmits - ulong ntxsretries; // single retries - ulong ntxmultiretries; // multiple retries - ulong ntxretrylimit; - ulong ntxdiscards; - ulong nrxuframes; // unicast frames - ulong nrxmframes; // multicast frames - ulong nrxfrags; // fragments - ulong nrxubytes; // unicast bytes - ulong nrxmbytes; // multicast bytes - ulong nrxfcserr; - ulong nrxdropnobuf; - ulong nrxdropnosa; - ulong nrxcantdecrypt; - ulong nrxmsgfrag; - ulong nrxmsgbadfrag; - ulong end; -}; - -struct WFrame -{ - ushort sts; - ushort rsvd0; - ushort rsvd1; - ushort qinfo; - ushort rsvd2; - ushort rsvd3; - ushort txctl; - ushort framectl; - ushort id; - uchar addr1[Eaddrlen]; - uchar addr2[Eaddrlen]; - uchar addr3[Eaddrlen]; - ushort seqctl; - uchar addr4[Eaddrlen]; - ushort dlen; - uchar dstaddr[Eaddrlen]; - uchar srcaddr[Eaddrlen]; - ushort len; - ushort dat[3]; - ushort type; -}; - -struct WKey -{ - ushort len; - char dat[WKeyLen]; -}; - -struct Wltv -{ - ushort len; - ushort type; - union - { - struct { - ushort val; - ushort pad; - }; - struct { - uchar addr[8]; - }; - struct { - ushort slen; - char s[WNameLen]; - }; - struct { - char name[WNameLen]; - }; - struct { - WKey keys[WNKeys]; - }; - }; -}; - -// What the driver thinks. Not what the card thinks. -struct Stats -{ - ulong nints; - ulong ndoubleint; - ulong nrx; - ulong ntx; - ulong ntxrq; - ulong nrxerr; - ulong ntxerr; - ulong nalloc; // allocation (reclaim) events - ulong ninfo; - ulong nidrop; - ulong nwatchdogs; // transmit time outs, actually - int ticks; - int tickintr; - int signal; - int noise; -}; - -struct Ctlr -{ - Lock; - Rendez timer; - - int attached; - int slot; - int iob; - int createibss; - int ptype; - int apdensity; - int rtsthres; - int txbusy; - int txrate; - int txdid; - int txmid; - int txtmout; - int maxlen; - int chan; - int pmena; - int pmwait; - - char netname[WNameLen]; - char wantname[WNameLen]; - char nodename[WNameLen]; - WFrame txf; - uchar txbuf[1536]; - - int hascrypt; // card has encryption - int crypt; // encryption off/on - int txkey; // transmit key - Wltv keys; // default keys - int xclear; // exclude clear packets off/on - - int ctlrno; - - /* for PCI-based devices */ - Ctlr *next; - ushort *mmb; - int active; - Pcidev *pcidev; - - Stats; - WStats; -}; - -#define csr_outs(ctlr,r,arg) outs((ctlr)->iob+(r),(arg)) -#define csr_ins(ctlr,r) ins((ctlr)->iob+(r)) -#define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev)) - -// w_... routines do not ilock the Ctlr and should -// be called locked. - -static void -w_intdis(Ctlr* ctlr) -{ - csr_outs(ctlr, WR_IntEna, 0); - csr_ack(ctlr, 0xffff); -} - -static void -w_intena(Ctlr* ctlr) -{ - csr_outs(ctlr, WR_IntEna, WEvs); -} - -static int -w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) -{ - int i, rc; - - csr_outs(ctlr, WR_Parm0, arg); - csr_outs(ctlr, WR_Cmd, cmd); - for (i = 0; itype)){ - DEBUG("wavelan: access read failed\n"); - return -1; - } - if (w_seek(ctlr,ltv->type,0,1)){ - DEBUG("wavelan: seek failed\n"); - return -1; - } - len = csr_ins(ctlr, WR_Data1); - if (len > ltv->len) - return -1; - ltv->len = len; - if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ - DEBUG("wavelan: type %x != code %x\n",ltv->type,code); - return -1; - } - if(ltv->len > 0) - inss((ctlr)->iob+(WR_Data1), <v->val, ltv->len-1); - - return 0; -} - -static void -w_outltv(Ctlr* ctlr, Wltv* ltv) -{ - if(w_seek(ctlr,ltv->type, 0, 1)) - return; - outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1); - w_cmd(ctlr, WCmdAccWr, ltv->type); -} - -static void -ltv_outs(Ctlr* ctlr, int type, ushort val) -{ - Wltv ltv; - - ltv.len = 2; - ltv.type = type; - ltv.val = val; - w_outltv(ctlr, <v); -} - -static int -ltv_ins(Ctlr* ctlr, int type) -{ - Wltv ltv; - - ltv.len = 2; - ltv.type = type; - ltv.val = 0; - if(w_inltv(ctlr, <v)) - return -1; - return ltv.val; -} - -static void -ltv_outstr(Ctlr* ctlr, int type, char* val) -{ - Wltv ltv; - int len; - - len = strlen(val); - if(len > sizeof(ltv.s)) - len = sizeof(ltv.s); - memset(<v, 0, sizeof(ltv)); - ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; - ltv.type = type; - -// This should be ltv.slen = len; according to Axel Belinfante - ltv.slen = len; - - strncpy(ltv.s, val, len); - w_outltv(ctlr, <v); -} - -static char Unkname[] = "who knows"; -static char Nilname[] = "card does not tell"; - -static char* -ltv_inname(Ctlr* ctlr, int type) -{ - static Wltv ltv; - int len; - - memset(<v,0,sizeof(ltv)); - ltv.len = WNameLen/2+2; - ltv.type = type; - if (w_inltv(ctlr, <v)) - return Unkname; - len = ltv.slen; - if(len == 0 || ltv.s[0] == 0) - return Nilname; - if(len >= sizeof ltv.s) - len = sizeof ltv.s - 1; - ltv.s[len] = '\0'; - return ltv.s; -} - -static int -w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) -{ - if (w_seek(ctlr, type, off, 1)){ - DEBUG("wavelan: w_read: seek failed"); - return 0; - } - inss((ctlr)->iob+(WR_Data1), buf, len/2); - - return len; -} - -static int -w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) -{ - int tries; - - for (tries=0; tries < WTmOut; tries++){ - if (w_seek(ctlr, type, off, 0)){ - DEBUG("wavelan: w_write: seek failed\n"); - return 0; - } - - outss((ctlr)->iob+(WR_Data0), buf, len/2); - - csr_outs(ctlr, WR_Data0, 0xdead); - csr_outs(ctlr, WR_Data0, 0xbeef); - if (w_seek(ctlr, type, off + len, 0)){ - DEBUG("wavelan: write seek failed\n"); - return 0; - } - if (csr_ins(ctlr, WR_Data0) == 0xdead) - if (csr_ins(ctlr, WR_Data0) == 0xbeef) - return len; - DEBUG("wavelan: Hermes bug byte.\n"); - return 0; - } - DEBUG("wavelan: tx timeout\n"); - return 0; -} +#include "wavelan.h" static int -w_alloc(Ctlr* ctlr, int len) +wavelanpcmciareset(Ether *ether) { - int rc; - int i,j; - - if (w_cmd(ctlr, WCmdMalloc, len)==0) - for (i = 0; ictlr; - - if (!ctlr) - return -1; - - w_intdis(ctlr); - w_cmd(ctlr, WCmdDis, 0); - w_intdis(ctlr); - if(w_cmd(ctlr, WCmdIni, 0)) - return -1; - w_intdis(ctlr); - - ltv_outs(ctlr, WTyp_Tick, 8); - ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); - ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); - ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); - ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); - ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); - ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); - ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); - ltv_outs(ctlr, WTyp_PM, ctlr->pmena); - if (*ctlr->netname) - ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); - if (*ctlr->wantname) - ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); - ltv_outs(ctlr, WTyp_Chan, ctlr->chan); - if (*ctlr->nodename) - ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); - ltv.len = 4; - ltv.type = WTyp_Mac; - memmove(ltv.addr, ether->ea, Eaddrlen); - w_outltv(ctlr, <v); - - ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); - - if (ctlr->hascrypt){ - ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); - ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); - w_outltv(ctlr, &ctlr->keys); - ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); - } - - // BUG: set multicast addresses - - if (w_cmd(ctlr, WCmdEna, 0)){ - DEBUG("wavelan: Enable failed"); - return -1; - } - ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); - ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); - if (ctlr->txdid == -1 || ctlr->txmid == -1) - DEBUG("wavelan: alloc failed"); - ctlr->txbusy = 0; - w_intena(ctlr); - return 0; -} - -static void -w_rxdone(Ether* ether) -{ - Ctlr* ctlr = (Ctlr*) ether->ctlr; - int len, sp; - WFrame f; - Block* bp=0; - Etherpkt* ep; - - sp = csr_ins(ctlr, WR_RXId); - len = w_read(ctlr, sp, 0, &f, sizeof(f)); - if (len == 0){ - DEBUG("wavelan: read frame error\n"); - goto rxerror; - } - if (f.sts&WF_Err){ - goto rxerror; - } - switch(f.sts){ - case WF_1042: - case WF_Tunnel: - case WF_WMP: - len = f.dlen + WSnapHdrLen; - bp = iallocb(ETHERHDRSIZE + len + 2); - if (!bp) - goto rxerror; - ep = (Etherpkt*) bp->wp; - memmove(ep->d, f.addr1, Eaddrlen); - memmove(ep->s, f.addr2, Eaddrlen); - memmove(ep->type,&f.type,2); - bp->wp += ETHERHDRSIZE; - if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ - DEBUG("wavelan: read 802.11 error\n"); - goto rxerror; - } - bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); - break; - default: - len = ETHERHDRSIZE + f.dlen + 2; - bp = iallocb(len); - if (!bp) - goto rxerror; - if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ - DEBUG("wavelan: read 800.3 error\n"); - goto rxerror; - } - bp->wp += len; - } - - ctlr->nrx++; - etheriq(ether,bp,1); - ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; - ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; - return; - -rxerror: - freeb(bp); - ctlr->nrxerr++; -} - -static void -w_txstart(Ether* ether) -{ - Etherpkt *pkt; Ctlr *ctlr; - Block *bp; - int len, off; - - if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy) - return; - - if((bp = qget(ether->oq)) == nil) - return; - pkt = (Etherpkt*)bp->rp; - - // - // If the packet header type field is > 1500 it is an IP or - // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. - // - memset(&ctlr->txf, 0, sizeof(ctlr->txf)); - if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ - ctlr->txf.framectl = WF_Data; - memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); - memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); - memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); - memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); - memmove(&ctlr->txf.type, pkt->type, 2); - bp->rp += ETHERHDRSIZE; - len = BLEN(bp); - off = WF_802_11_Off; - ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; - hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); - hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); - hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); - } - else{ - len = BLEN(bp); - off = WF_802_3_Off; - ctlr->txf.dlen = len; - } - w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); - w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); - - if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ - DEBUG("wavelan: transmit failed\n"); - ctlr->ntxerr++; - } - else{ - ctlr->txbusy = 1; - ctlr->txtmout = 2; - } - freeb(bp); -} - -static void -w_txdone(Ctlr* ctlr, int sts) -{ - ctlr->txbusy = 0; - ctlr->txtmout = 0; - if (sts & WTxErrEv) - ctlr->ntxerr++; - else - ctlr->ntx++; -} - -static int -w_stats(Ctlr* ctlr) -{ - int i, rc, sp; - Wltv ltv; - ulong* p = (ulong*)&ctlr->WStats; - ulong* pend = (ulong*)&ctlr->end; - - sp = csr_ins(ctlr, WR_InfoId); - ltv.len = ltv.type = 0; - w_read(ctlr, sp, 0, <v, 4); - if (ltv.type == WTyp_Stats){ - ltv.len--; - for (i = 0; i < ltv.len && p < pend; i++){ - rc = csr_ins(ctlr, WR_Data1); - if (rc > 0xf000) - rc = ~rc & 0xffff; - p[i] += rc; - } - return 0; - } - return -1; -} - -static void -w_intr(Ether *ether) -{ - int rc, txid; - Ctlr* ctlr = (Ctlr*) ether->ctlr; - - if (ctlr->attached == 0){ - csr_ack(ctlr, 0xffff); - csr_outs(ctlr, WR_IntEna, 0); - return; - } - - csr_outs(ctlr, WR_IntEna, 0); - rc = csr_ins(ctlr, WR_EvSts); - csr_ack(ctlr, ~WEvs); // Not interested on them - - if (rc & WRXEv){ - w_rxdone(ether); - csr_ack(ctlr, WRXEv); - } - if (rc & WTXEv){ - w_txdone(ctlr, rc); - csr_ack(ctlr, WTXEv); - } - if (rc & WAllocEv){ - ctlr->nalloc++; - txid = csr_ins(ctlr, WR_Alloc); - csr_ack(ctlr, WAllocEv); - if (txid == ctlr->txdid){ - if ((rc & WTXEv) == 0) - w_txdone(ctlr, rc); - } - } - if (rc & WInfoEv){ - ctlr->ninfo++; - w_stats(ctlr); - csr_ack(ctlr, WInfoEv); - } - if (rc & WTxErrEv){ - w_txdone(ctlr, rc); - csr_ack(ctlr, WTxErrEv); - } - if (rc & WIDropEv){ - ctlr->nidrop++; - csr_ack(ctlr, WIDropEv); - } - - w_intena(ctlr); - w_txstart(ether); -} - -// Watcher to ensure that the card still works properly and -// to request WStats updates once a minute. -// BUG: it runs much more often, see the comment below. - -static void -w_timer(void* arg) -{ - Ether* ether = (Ether*) arg; - Ctlr* ctlr = (Ctlr*)ether->ctlr; - - for(;;){ - tsleep(&ctlr->timer, return0, 0, 50); - ctlr = (Ctlr*)ether->ctlr; - if (ctlr == 0) - break; - if (ctlr->attached == 0) - continue; - ctlr->ticks++; - - ilock(&ctlr->Lock); - - // Seems that the card gets frames BUT does - // not send the interrupt; this is a problem because - // I suspect it runs out of receive buffers and - // stops receiving until a transmit watchdog - // reenables the card. - // The problem is serious because it leads to - // poor rtts. - // This can be seen clearly by commenting out - // the next if and doing a ping: it will stop - // receiving (although the icmp replies are being - // issued from the remote) after a few seconds. - // Of course this `bug' could be because I'm reading - // the card frames in the wrong way; due to the - // lack of documentation I cannot know. - -// if (csr_ins(ctlr, WR_EvSts)&WEvs){ -// ctlr->tickintr++; -// w_intr(ether); -// } - - if ((ctlr->ticks % 10) == 0) { - if (ctlr->txtmout && --ctlr->txtmout == 0){ - ctlr->nwatchdogs++; - w_txdone(ctlr, WTxErrEv); - if (w_enable(ether)){ - DEBUG("wavelan: wdog enable failed\n"); - } - w_txstart(ether); - } - if ((ctlr->ticks % 120) == 0) - if (ctlr->txbusy == 0) - w_cmd(ctlr, WCmdAskStats, WTyp_Stats); - } - iunlock(&ctlr->Lock); - } - pexit("terminated",0); -} - -static void -multicast(void*, uchar*, int) -{ - // BUG: to be added. -} - -static void -attach(Ether* ether) -{ - Ctlr* ctlr; - char name[64]; - int rc; - - if (ether->ctlr == 0) - return; - - snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); - ctlr = (Ctlr*) ether->ctlr; - if (ctlr->attached == 0){ - ilock(&ctlr->Lock); - rc = w_enable(ether); - iunlock(&ctlr->Lock); - if(rc == 0){ - ctlr->attached = 1; - kproc(name, w_timer, ether); - } else - print("#l%d: enable failed\n",ether->ctlrno); - } -} - -#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) -#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) - -static long -ifstat(Ether* ether, void* a, long n, ulong offset) -{ - Ctlr *ctlr = (Ctlr*) ether->ctlr; - char *k, *p; - int i, l, txid; - - ether->oerrs = ctlr->ntxerr; - ether->crcs = ctlr->nrxfcserr; - ether->frames = 0; - ether->buffs = ctlr->nrxdropnobuf; - ether->overflows = 0; - - // - // Offset must be zero or there's a possibility the - // new data won't match the previous read. - // - if(n == 0 || offset != 0) - return 0; - - p = malloc(READSTR); - l = 0; - - PRINTSTAT("Signal: %d\n", ctlr->signal-149); - PRINTSTAT("Noise: %d\n", ctlr->noise-149); - PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); - PRINTSTAT("Interrupts: %lud\n", ctlr->nints); - PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); - PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); - PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); - PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); - PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); - PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); - PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); - PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); - PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); - PRINTSTAT("Ticks: %ud\n", ctlr->ticks); - PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); - k = ((ctlr->attached) ? "attached" : "not attached"); - PRINTSTAT("Card %s", k); - k = ((ctlr->txbusy)? ", txbusy" : ""); - PRINTSTAT("%s\n", k); - - if (ctlr->hascrypt){ - PRINTSTR("Keys: "); - for (i = 0; i < WNKeys; i++){ - if (ctlr->keys.keys[i].len == 0) - PRINTSTR("none "); - else if (SEEKEYS == 0) - PRINTSTR("set "); - else - PRINTSTAT("%s ", ctlr->keys.keys[i].dat); - } - PRINTSTR("\n"); - } - - // real card stats - ilock(&ctlr->Lock); - PRINTSTR("\nCard stats: \n"); - PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); - PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); - i = ltv_ins(ctlr, WTyp_Ptype); - PRINTSTAT("Port type: %d\n", i); - PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); - PRINTSTAT("Current Transmit rate: %d\n", - ltv_ins(ctlr, WTyp_CurTxRate)); - PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); - PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); - PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); - if(i == 3) - PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); - else { - Wltv ltv; - PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); - ltv.type = WTyp_BaseID; - ltv.len = 4; - if (w_inltv(ctlr, <v)) - print("#l%d: unable to read base station mac addr\n", ether->ctlrno); - l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", - ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); - } - PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); - PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); - if (ltv_ins(ctlr, WTyp_HasCrypt) == 0) - PRINTSTR("WEP: not supported\n"); - else { - if (ltv_ins(ctlr, WTyp_Crypt) == 0) - PRINTSTR("WEP: disabled\n"); - else{ - PRINTSTR("WEP: enabled\n"); - k = ((ctlr->xclear)? "excluded": "included"); - PRINTSTAT("Clear packets: %s\n", k); - txid = ltv_ins(ctlr, WTyp_TxKey); - PRINTSTAT("Transmit key id: %d\n", txid); - } - } - iunlock(&ctlr->Lock); - - PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); - PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); - PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); - PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); - PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); - PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); - PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); - PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); - PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); - PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); - PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); - PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); - PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); - PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); - PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); - PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); - PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); - PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); - PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); - PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); - PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); - USED(l); - n = readstr(offset, a, n, p); - free(p); - return n; -} -#undef PRINTSTR -#undef PRINTSTAT - -static int -option(Ctlr* ctlr, char* buf, long n) -{ - char *p; - int i, r; - WKey *key; - Cmdbuf *cb; - - r = 0; - - cb = parsecmd(buf, n); - if(cb->nf < 2) - r = -1; - else if(cistrcmp(cb->f[0], "essid") == 0){ - if (cistrcmp(cb->f[1],"default") == 0) - p = ""; - else - p = cb->f[1]; - if(ctlr->ptype == 3){ - memset(ctlr->netname, 0, sizeof(ctlr->netname)); - strncpy(ctlr->netname, p, WNameLen); - } - else{ - memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); - strncpy(ctlr->wantname, p, WNameLen); - } - } - else if(cistrcmp(cb->f[0], "station") == 0){ - memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); - strncpy(ctlr->nodename, cb->f[1], WNameLen); - } - else if(cistrcmp(cb->f[0], "channel") == 0){ - if((i = atoi(cb->f[1])) >= 1 && i <= 16) - ctlr->chan = i; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "mode") == 0){ - if(cistrcmp(cb->f[1], "managed") == 0) - ctlr->ptype = WPTypeManaged; - else if(cistrcmp(cb->f[1], "wds") == 0) - ctlr->ptype = WPTypeWDS; - else if(cistrcmp(cb->f[1], "adhoc") == 0) - ctlr->ptype = WPTypeAdHoc; - else if((i = atoi(cb->f[1])) >= 1 && i <= 3) - ctlr->ptype = i; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "ibss") == 0){ - if(cistrcmp(cb->f[1], "on") == 0) - ctlr->createibss = 1; - else - ctlr->createibss = 0; - } - else if(cistrcmp(cb->f[0], "crypt") == 0){ - if(cistrcmp(cb->f[1], "off") == 0) - ctlr->crypt = 0; - else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) - ctlr->crypt = 1; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "clear") == 0){ - if(cistrcmp(cb->f[1], "on") == 0) - ctlr->xclear = 0; - else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) - ctlr->xclear = 1; - else - r = -1; - } - else if(cistrncmp(cb->f[0], "key", 3) == 0){ - if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ - ctlr->txkey = i-1; - key = &ctlr->keys.keys[ctlr->txkey]; - key->len = strlen(cb->f[1]); - if (key->len > WKeyLen) - key->len = WKeyLen; - memset(key->dat, 0, sizeof(key->dat)); - memmove(key->dat, cb->f[1], key->len); - } - else - r = -1; - } - else if(cistrcmp(cb->f[0], "txkey") == 0){ - if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) - ctlr->txkey = i-1; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "pm") == 0){ - if(cistrcmp(cb->f[1], "off") == 0) - ctlr->pmena = 0; - else if(cistrcmp(cb->f[1], "on") == 0){ - ctlr->pmena = 1; - if(cb->nf == 3){ - i = atoi(cb->f[2]); - // check range here? what are the units? - ctlr->pmwait = i; - } - } - else - r = -1; - } - else - r = -2; - free(cb); - - return r; -} - -static long -ctl(Ether* ether, void* buf, long n) -{ - Ctlr *ctlr; - - if((ctlr = ether->ctlr) == nil) - error(Enonexist); - if(ctlr->attached == 0) - error(Eshutdown); - - ilock(&ctlr->Lock); - if(option(ctlr, buf, n)){ - iunlock(&ctlr->Lock); - error(Ebadctl); - } - if(ctlr->txbusy) - w_txdone(ctlr, WTxErrEv); - w_enable(ether); - w_txstart(ether); - iunlock(&ctlr->Lock); - - return n; -} - -static void -transmit(Ether* ether) -{ - Ctlr* ctlr = ether->ctlr; - - if (ctlr == 0) - return; - - ilock(&ctlr->Lock); - ctlr->ntxrq++; - w_txstart(ether); - iunlock(&ctlr->Lock); -} - -static void -promiscuous(void* arg, int on) -{ - Ether* ether = (Ether*)arg; - Ctlr* ctlr = ether->ctlr; - - if (ctlr == nil) - error("card not found"); - if (ctlr->attached == 0) - error("card not attached"); - ilock(&ctlr->Lock); - ltv_outs(ctlr, WTyp_Prom, (on?1:0)); - iunlock(&ctlr->Lock); -} - -static void -interrupt(Ureg* ,void* arg) -{ - Ether* ether = (Ether*) arg; - Ctlr* ctlr = (Ctlr*) ether->ctlr; - - if (ctlr == 0) - return; - ilock(&ctlr->Lock); - ctlr->nints++; - w_intr(ether); - iunlock(&ctlr->Lock); -} - -static int -reset(Ether* ether) -{ - Wltv ltv; - Ctlr* ctlr; if((ctlr = malloc(sizeof(Ctlr))) == nil) return -1; - ilock(&ctlr->Lock); + ilock(ctlr); + ctlr->ctlrno = ether->ctlrno; if (ether->port==0) ether->port=WDfltIOB; ctlr->iob = ether->port; - w_intdis(ctlr); - if (w_cmd(ctlr,WCmdIni,0)){ - print("#l%d: init failed\n", ether->ctlrno); - goto abort; - } - w_intdis(ctlr); - ltv_outs(ctlr, WTyp_Tick, 8); - - ctlr->chan = 0; - ctlr->ptype = WDfltPType; - ctlr->txkey = 0; - ctlr->createibss = 0; - ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; - ctlr->keys.type = WTyp_Keys; - if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) - ctlr->crypt = 1; - *ctlr->netname = *ctlr->wantname = 0; - strcpy(ctlr->nodename, "Plan 9 STA"); - - ctlr->netname[WNameLen-1] = 0; - ctlr->wantname[WNameLen-1] = 0; - ctlr->nodename[WNameLen-1] =0; - - ltv.type = WTyp_Mac; - ltv.len = 4; - if (w_inltv(ctlr, <v)){ - print("#l%d: unable to read mac addr\n", - ether->ctlrno); - goto abort; + if(wavelanreset(ether, ctlr) < 0){ + iunlock(ctlr); + free(ctlr); + ether->ctlr = nil; + return -1; } - memmove(ether->ea, ltv.addr, Eaddrlen); - - if (ctlr->chan == 0) - ctlr->chan = ltv_ins(ctlr, WTyp_Chan); - ctlr->apdensity = WDfltApDens; - ctlr->rtsthres = WDfltRtsThres; - ctlr->txrate = WDfltTxRate; - ctlr->maxlen = WMaxLen; - ctlr->pmena = 0; - ctlr->pmwait = 100; - ctlr->signal = 1; - ctlr->noise = 1; - - // link to ether - ether->ctlr = ctlr; - ether->mbps = 10; - ether->attach = attach; - ether->interrupt = interrupt; - ether->transmit = transmit; - ether->ifstat = ifstat; - ether->ctl = ctl; - ether->promiscuous = promiscuous; - ether->multicast = multicast; - ether->arg = ether; - - DEBUG("#l%d: port %lux type %s", - ether->ctlrno, ether->port, ether->type); - DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", - ether->ea[0], ether->ea[1], ether->ea[2], - ether->ea[3], ether->ea[4], ether->ea[5]); - - iunlock(&ctlr->Lock); + iunlock(ctlr); return 0; - -abort: - iunlock(&ctlr->Lock); - free(ctlr); - ether->ctlr = nil; - return -1; } - + void etherwavelanlink(void) { - addethercard("wavelan", reset); + addethercard("wavelan", wavelanpcmciareset); } diff --git a/bitsy/fns.h b/bitsy/fns.h index f03b38d87d20900f28be5e3ca64c88cf163fde76..d63e150fd4fc457b0b8d7b49b01914789caede37 100644 --- a/bitsy/fns.h +++ b/bitsy/fns.h @@ -18,7 +18,6 @@ void clockpower(int); #define coherence() #define dcflush(a, b) void delay(int); -void µdelay(ulong); void µcpower(int); void deepsleep(void); void dmainit(void); diff --git a/bitsy/sdata.c b/bitsy/sdata.c index 76307ab99521cb353eca93afc31c9ed92c556f1e..bca2748d55552d7c771fd8d1f9ed368131abd668 100644 --- a/bitsy/sdata.c +++ b/bitsy/sdata.c @@ -368,7 +368,7 @@ ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); break; } - µdelay(4); + microdelay(4); } atadebug(cmdport, ctlport, "ataready: timeout"); @@ -431,7 +431,7 @@ atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) return 0; outb(cmdport+Count, rwm); outb(cmdport+Command, Csm); - µdelay(4); + microdelay(4); as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); inb(cmdport+Status); if(as < 0 || (as & (Df|Err))) @@ -482,7 +482,7 @@ ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) if(as < 0) return as; outb(cmdport+Command, command); - µdelay(4); + microdelay(4); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); if(as < 0) @@ -600,11 +600,11 @@ atasrst(int ctlport) * Also, there will be problems here if overlapped commands * are ever supported. */ - µdelay(20); + microdelay(20); outb(ctlport+Dc, Srst); - µdelay(20); + microdelay(20); outb(ctlport+Dc, 0); - µdelay(4*1000); + microdelay(4*1000); } static SDev* @@ -631,7 +631,7 @@ ataprobe(int cmdport, int ctlport, int irq) dev = Dev0; if(inb(ctlport+As) & Bsy){ outb(cmdport+Dh, dev); - µdelay(5); + microdelay(5); trydev1: atadebug(cmdport, ctlport, "ataprobe bsy"); outb(cmdport+Cyllo, 0xAA); @@ -732,7 +732,7 @@ tryedd1: } else{ outb(cmdport+Dh, Dev0); - µdelay(1); + microdelay(1); } } #endif @@ -846,13 +846,13 @@ atanop(Drive* drive, int subcommand) ctlr->command = Cnop; /* debugging */ outb(cmdport+Command, Cnop); - µdelay(1); + microdelay(1); ctlport = ctlr->ctlport; for(timeo = 0; timeo < 1000; timeo++){ as = inb(ctlport+As); if(!(as & Bsy)) break; - µdelay(1); + microdelay(1); } drive->error |= Abrt; } @@ -1078,7 +1078,7 @@ atapktio(Drive* drive, uchar* cmd, int clen) outb(cmdport+Command, Cpkt); if((drive->info[Iconfig] & 0x0060) != 0x0020){ - µdelay(1); + microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); if(as < 0) r = SDtimeout; @@ -1180,7 +1180,7 @@ atageniostart(Drive* drive, int lba) switch(drive->command){ case Cws: case Cwsm: - µdelay(1); + microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000); if(as < 0 || (as & Err)){ iunlock(ctlr); diff --git a/bitsy/wavelan.c b/bitsy/wavelan.c new file mode 100644 index 0000000000000000000000000000000000000000..fcf743151b7eef5a2521eddaa43c443904b38a42 --- /dev/null +++ b/bitsy/wavelan.c @@ -0,0 +1 @@ +#include "../pc/wavelan.c" diff --git a/bitsy/wavelan.h b/bitsy/wavelan.h new file mode 100644 index 0000000000000000000000000000000000000000..ceda19381b44a7f7605911f4b7c0bd2c6bcdc964 --- /dev/null +++ b/bitsy/wavelan.h @@ -0,0 +1,4 @@ +typedef struct Pcidev Pcidev; +/* not used, but needed in a struct declaration */ + +#include "../pc/wavelan.h" diff --git a/pc/etherwavelan.c b/pc/etherwavelan.c index c208c0ab9b1129041d8468743017ce421d7c89bc..b20bf98f4033ec5def880d6a66dc71fdde74debe 100644 --- a/pc/etherwavelan.c +++ b/pc/etherwavelan.c @@ -1,22 +1,5 @@ -/* - Lucent Wavelan IEEE 802.11 pcmcia. - There is almost no documentation for the card. - the driver is done using both the FreeBSD, Linux and - original Plan 9 drivers as `documentation'. +/* Pci/pcmcia code for wavelan.c */ - Has been used with the card plugged in during all up time. - no cards removals/insertions yet. - - For known BUGS see the comments below. Besides, - the driver keeps interrupts disabled for just too - long. When it gets robust, locks should be revisited. - - BUGS: check endian, alignment and mem/io issues; - multicast; - receive watchdog interrupts. - TODO: automatic power management; - improve locking. - */ #include "u.h" #include "../port/lib.h" #include "mem.h" @@ -27,1349 +10,13 @@ #include "../port/netif.h" #include "etherif.h" -#define DEBUG if(1){}else print - -#define SEEKEYS 0 - -// Lucent's Length-Type-Value records to talk to the wavelan. -// most operational parameters are read/set using this. -enum -{ - WTyp_Stats = 0xf100, - WTyp_Ptype = 0xfc00, - WTyp_Mac = 0xfc01, - WTyp_WantName = 0xfc02, - WTyp_Chan = 0xfc03, - WTyp_NetName = 0xfc04, - WTyp_ApDens = 0xfc06, - WTyp_MaxLen = 0xfc07, - WTyp_PM = 0xfc09, - WTyp_PMWait = 0xfc0c, - WTyp_NodeName = 0xfc0e, - WTyp_Crypt = 0xfc20, - WTyp_XClear = 0xfc22, - WTyp_CreateIBSS = 0xfc81, - WTyp_RtsThres = 0xfc83, - WTyp_TxRate = 0xfc84, - WTx1Mbps = 0x0, - WTx2Mbps = 0x1, - WTxAuto = 0x3, - WTyp_Prom = 0xfc85, - WTyp_Keys = 0xfcb0, - WTyp_TxKey = 0xfcb1, - WTyp_StationID = 0xfd20, - WTyp_CurName = 0xfd41, - WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station - WTyp_CurTxRate = 0xfd44, // Current TX rate - WTyp_HasCrypt = 0xfd4f, - WTyp_Tick = 0xfce0, -}; - -// Controller -enum -{ - WDfltIRQ = 3, // default irq - WDfltIOB = 0x180, // default IO base - - WIOLen = 0x40, // Hermes IO length - - WTmOut = 65536, // Cmd time out - - WPTypeManaged = 1, - WPTypeWDS = 2, - WPTypeAdHoc = 3, - WDfltPType = WPTypeManaged, - - WDfltApDens = 1, - WDfltRtsThres = 2347, // == disabled - WDfltTxRate = WTxAuto, // 2Mbps - - WMaxLen = 2304, - WNameLen = 32, - - WNKeys = 4, - WKeyLen = 14, - WMinKeyLen = 5, - - // Wavelan hermes registers - WR_Cmd = 0x00, - WCmdIni = 0x0000, - WCmdEna = 0x0001, - WCmdDis = 0x0002, - WCmdTx = 0x000b, - WCmdMalloc = 0x000a, - WCmdAskStats = 0x0011, - WCmdMsk = 0x003f, - WCmdAccRd = 0x0021, - WCmdReclaim = 0x0100, - WCmdAccWr = 0x0121, - WCmdBusy = 0x8000, - WR_Parm0 = 0x02, - WR_Parm1 = 0x04, - WR_Parm2 = 0x06, - WR_Sts = 0x08, - WR_InfoId = 0x10, - WR_Sel0 = 0x18, - WR_Sel1 = 0x1a, - WR_Off0 = 0x1c, - WR_Off1 = 0x1e, - WBusyOff = 0x8000, - WErrOff = 0x4000, - WResSts = 0x7f00, - WR_RXId = 0x20, - WR_Alloc = 0x22, - WR_EvSts = 0x30, - WR_IntEna = 0x32, - WCmdEv = 0x0010, - WRXEv = 0x0001, - WTXEv = 0x0002, - WTxErrEv = 0x0004, - WAllocEv = 0x0008, - WInfoEv = 0x0080, - WIDropEv = 0x2000, - WTickEv = 0x8000, - WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, - - WR_EvAck = 0x34, - WR_Data0 = 0x36, - WR_Data1 = 0x38, - - WR_PciCor = 0x26, - WR_PciHcr = 0x2E, - - // Frame stuff - - WF_Err = 0x0003, - WF_1042 = 0x2000, - WF_Tunnel = 0x4000, - WF_WMP = 0x6000, - - WF_Data = 0x0008, - - WSnapK1 = 0xaa, - WSnapK2 = 0x00, - WSnapCtlr = 0x03, - WSnap0 = (WSnapK1|(WSnapK1<<8)), - WSnap1 = (WSnapK2|(WSnapCtlr<<8)), - WSnapHdrLen = 6, - - WF_802_11_Off = 0x44, - WF_802_3_Off = 0x2e, - -}; - -typedef struct Ctlr Ctlr; -typedef struct Wltv Wltv; -typedef struct WFrame WFrame; -typedef struct Stats Stats; -typedef struct WStats WStats; -typedef struct WKey WKey; - -struct WStats -{ - ulong ntxuframes; // unicast frames - ulong ntxmframes; // multicast frames - ulong ntxfrags; // fragments - ulong ntxubytes; // unicast bytes - ulong ntxmbytes; // multicast bytes - ulong ntxdeferred; // deferred transmits - ulong ntxsretries; // single retries - ulong ntxmultiretries; // multiple retries - ulong ntxretrylimit; - ulong ntxdiscards; - ulong nrxuframes; // unicast frames - ulong nrxmframes; // multicast frames - ulong nrxfrags; // fragments - ulong nrxubytes; // unicast bytes - ulong nrxmbytes; // multicast bytes - ulong nrxfcserr; - ulong nrxdropnobuf; - ulong nrxdropnosa; - ulong nrxcantdecrypt; - ulong nrxmsgfrag; - ulong nrxmsgbadfrag; - ulong end; -}; - -struct WFrame -{ - ushort sts; - ushort rsvd0; - ushort rsvd1; - ushort qinfo; - ushort rsvd2; - ushort rsvd3; - ushort txctl; - ushort framectl; - ushort id; - uchar addr1[Eaddrlen]; - uchar addr2[Eaddrlen]; - uchar addr3[Eaddrlen]; - ushort seqctl; - uchar addr4[Eaddrlen]; - ushort dlen; - uchar dstaddr[Eaddrlen]; - uchar srcaddr[Eaddrlen]; - ushort len; - ushort dat[3]; - ushort type; -}; - -struct WKey -{ - ushort len; - char dat[WKeyLen]; -}; - -struct Wltv -{ - ushort len; - ushort type; - union - { - struct { - ushort val; - ushort pad; - }; - struct { - uchar addr[8]; - }; - struct { - ushort slen; - char s[WNameLen]; - }; - struct { - char name[WNameLen]; - }; - struct { - WKey keys[WNKeys]; - }; - }; -}; - -// What the driver thinks. Not what the card thinks. -struct Stats -{ - ulong nints; - ulong ndoubleint; - ulong nrx; - ulong ntx; - ulong ntxrq; - ulong nrxerr; - ulong ntxerr; - ulong nalloc; // allocation (reclaim) events - ulong ninfo; - ulong nidrop; - ulong nwatchdogs; // transmit time outs, actually - int ticks; - int tickintr; - int signal; - int noise; -}; - -struct Ctlr -{ - Lock; - Rendez timer; - - int attached; - int slot; - int iob; - int createibss; - int ptype; - int apdensity; - int rtsthres; - int txbusy; - int txrate; - int txdid; - int txmid; - int txtmout; - int maxlen; - int chan; - int pmena; - int pmwait; - - char netname[WNameLen]; - char wantname[WNameLen]; - char nodename[WNameLen]; - WFrame txf; - uchar txbuf[1536]; - - int hascrypt; // card has encryption - int crypt; // encryption off/on - int txkey; // transmit key - Wltv keys; // default keys - int xclear; // exclude clear packets off/on - - int ctlrno; - - /* for PCI-based devices */ - Ctlr *next; - ushort *mmb; - int active; - Pcidev *pcidev; - - Stats; - WStats; -}; - -/* - * When we're using a PCI device and memory-mapped I/O, - * the registers are spaced out as though each takes 32 bits, - * even though they are only 16-bit registers. Thus, - * ctlr->mmb[reg] is the right way to access register reg, - * even though a priori you'd expect to use ctlr->mmb[reg/2]. - */ -static void -csr_outs(Ctlr *ctlr, int reg, ushort arg) -{ - if(ctlr->mmb) - ctlr->mmb[reg] = arg; - else - outs(ctlr->iob+reg, arg); -} - -static ushort -csr_ins(Ctlr *ctlr, int reg) -{ - if(ctlr->mmb) - return ctlr->mmb[reg]; - else - return ins(ctlr->iob+reg); -} - -static void -csr_ack(Ctlr *ctlr, int ev) -{ - csr_outs(ctlr, WR_EvAck, ev); -} - -static void -csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat) -{ - ushort *rp, *wp; - - if(ctlr->mmb){ - rp = &ctlr->mmb[reg]; - wp = dat; - while(ndat-- > 0) - *wp++ = *rp; - }else - inss(ctlr->iob+reg, dat, ndat); -} - -static void -csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat) -{ - ushort *rp, *wp; - - if(ctlr->mmb){ - rp = dat; - wp = &ctlr->mmb[reg]; - while(ndat-- > 0) - *wp = *rp++; - }else - outss(ctlr->iob+reg, dat, ndat); -} - -// w_... routines do not ilock the Ctlr and should -// be called locked. - -static void -w_intdis(Ctlr* ctlr) -{ - csr_outs(ctlr, WR_IntEna, 0); - csr_ack(ctlr, 0xffff); -} - -static void -w_intena(Ctlr* ctlr) -{ - csr_outs(ctlr, WR_IntEna, WEvs); -} - -static int -w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) -{ - int i, rc; - - for(i=0; ictlrno, cmd, csr_ins(ctlr, WR_Cmd)); - return -1; - } - - csr_outs(ctlr, WR_Parm0, arg); - csr_outs(ctlr, WR_Cmd, cmd); - - for(i=0; ictlrno, cmd, i); - if(i == IniTmOut){ - print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); - return -1; - } - } - rc = csr_ins(ctlr, WR_Sts); - csr_ack(ctlr, WCmdEv); - - if((rc&WCmdMsk) != (cmd&WCmdMsk)){ - print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); - return -1; - } - if(rc&WResSts){ - /* - * Don't print; this happens on every WCmdAccWr for some reason. - */ - if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); - return -1; - } - return 0; -} - -static int -w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan) -{ - int i, rc; - static ushort sel[] = { WR_Sel0, WR_Sel1 }; - static ushort off[] = { WR_Off0, WR_Off1 }; - - if (chan != 0 && chan != 1) - panic("wavelan: bad chan\n"); - csr_outs(ctlr, sel[chan], id); - csr_outs(ctlr, off[chan], offset); - for (i=0; itype)){ - DEBUG("wavelan: access read failed\n"); - return -1; - } - if (w_seek(ctlr,ltv->type,0,1)){ - DEBUG("wavelan: seek failed\n"); - return -1; - } - len = csr_ins(ctlr, WR_Data1); - if (len > ltv->len) - return -1; - ltv->len = len; - if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ - USED(code); - DEBUG("wavelan: type %x != code %x\n",ltv->type,code); - return -1; - } - if(ltv->len > 0) - csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); - - return 0; -} - -static void -w_outltv(Ctlr* ctlr, Wltv* ltv) -{ - if(w_seek(ctlr,ltv->type, 0, 1)) - return; - csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); - w_cmd(ctlr, WCmdAccWr, ltv->type); -} - -static void -ltv_outs(Ctlr* ctlr, int type, ushort val) -{ - Wltv ltv; - - ltv.len = 2; - ltv.type = type; - ltv.val = val; - w_outltv(ctlr, <v); -} - -static int -ltv_ins(Ctlr* ctlr, int type) -{ - Wltv ltv; - - ltv.len = 2; - ltv.type = type; - ltv.val = 0; - if(w_inltv(ctlr, <v)) - return -1; - return ltv.val; -} - -static void -ltv_outstr(Ctlr* ctlr, int type, char* val) -{ - Wltv ltv; - int len; - - len = strlen(val); - if(len > sizeof(ltv.s)) - len = sizeof(ltv.s); - memset(<v, 0, sizeof(ltv)); - ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; - ltv.type = type; - -// This should be ltv.slen = len; according to Axel Belinfante - ltv.slen = len; - - strncpy(ltv.s, val, len); - w_outltv(ctlr, <v); -} - -static char Unkname[] = "who knows"; -static char Nilname[] = "card does not tell"; - -static char* -ltv_inname(Ctlr* ctlr, int type) -{ - static Wltv ltv; - int len; - - memset(<v,0,sizeof(ltv)); - ltv.len = WNameLen/2+2; - ltv.type = type; - if (w_inltv(ctlr, <v)) - return Unkname; - len = ltv.slen; - if(len == 0 || ltv.s[0] == 0) - return Nilname; - if(len >= sizeof ltv.s) - len = sizeof ltv.s - 1; - ltv.s[len] = '\0'; - return ltv.s; -} - -static int -w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) -{ - if (w_seek(ctlr, type, off, 1)){ - DEBUG("wavelan: w_read: seek failed"); - return 0; - } - csr_inss(ctlr, WR_Data1, buf, len/2); - - return len; -} - -static int -w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) -{ - int tries; - - for (tries=0; tries < WTmOut; tries++){ - if (w_seek(ctlr, type, off, 0)){ - DEBUG("wavelan: w_write: seek failed\n"); - return 0; - } - - csr_outss(ctlr, WR_Data0, buf, len/2); - - csr_outs(ctlr, WR_Data0, 0xdead); - csr_outs(ctlr, WR_Data0, 0xbeef); - if (w_seek(ctlr, type, off + len, 0)){ - DEBUG("wavelan: write seek failed\n"); - return 0; - } - if (csr_ins(ctlr, WR_Data0) == 0xdead) - if (csr_ins(ctlr, WR_Data0) == 0xbeef) - return len; - DEBUG("wavelan: Hermes bug byte.\n"); - return 0; - } - DEBUG("wavelan: tx timeout\n"); - return 0; -} - -static int -w_alloc(Ctlr* ctlr, int len) -{ - int rc; - int i,j; - - if (w_cmd(ctlr, WCmdMalloc, len)==0) - for (i = 0; ictlr; - - if (!ctlr) - return -1; - - w_intdis(ctlr); - w_cmd(ctlr, WCmdDis, 0); - w_intdis(ctlr); - if(w_cmd(ctlr, WCmdIni, 0)) - return -1; - w_intdis(ctlr); - - ltv_outs(ctlr, WTyp_Tick, 8); - ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); - ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); - ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); - ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); - ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); - ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); - ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); - ltv_outs(ctlr, WTyp_PM, ctlr->pmena); - if (*ctlr->netname) - ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); - if (*ctlr->wantname) - ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); - ltv_outs(ctlr, WTyp_Chan, ctlr->chan); - if (*ctlr->nodename) - ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); - ltv.len = 4; - ltv.type = WTyp_Mac; - memmove(ltv.addr, ether->ea, Eaddrlen); - w_outltv(ctlr, <v); - - ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); - - if (ctlr->hascrypt){ - ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); - ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); - w_outltv(ctlr, &ctlr->keys); - ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); - } - - // BUG: set multicast addresses - - if (w_cmd(ctlr, WCmdEna, 0)){ - DEBUG("wavelan: Enable failed"); - return -1; - } - ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); - ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); - if (ctlr->txdid == -1 || ctlr->txmid == -1) - DEBUG("wavelan: alloc failed"); - ctlr->txbusy = 0; - w_intena(ctlr); - return 0; -} - -static void -w_rxdone(Ether* ether) -{ - Ctlr* ctlr = (Ctlr*) ether->ctlr; - int len, sp; - WFrame f; - Block* bp=0; - Etherpkt* ep; - - sp = csr_ins(ctlr, WR_RXId); - len = w_read(ctlr, sp, 0, &f, sizeof(f)); - if (len == 0){ - DEBUG("wavelan: read frame error\n"); - goto rxerror; - } - if (f.sts&WF_Err){ - goto rxerror; - } - switch(f.sts){ - case WF_1042: - case WF_Tunnel: - case WF_WMP: - len = f.dlen + WSnapHdrLen; - bp = iallocb(ETHERHDRSIZE + len + 2); - if (!bp) - goto rxerror; - ep = (Etherpkt*) bp->wp; - memmove(ep->d, f.addr1, Eaddrlen); - memmove(ep->s, f.addr2, Eaddrlen); - memmove(ep->type,&f.type,2); - bp->wp += ETHERHDRSIZE; - if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ - DEBUG("wavelan: read 802.11 error\n"); - goto rxerror; - } - bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); - break; - default: - len = ETHERHDRSIZE + f.dlen + 2; - bp = iallocb(len); - if (!bp) - goto rxerror; - if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ - DEBUG("wavelan: read 800.3 error\n"); - goto rxerror; - } - bp->wp += len; - } - - ctlr->nrx++; - etheriq(ether,bp,1); - ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; - ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; - return; - -rxerror: - freeb(bp); - ctlr->nrxerr++; -} - -static void -w_txstart(Ether* ether) -{ - Etherpkt *pkt; - Ctlr *ctlr; - Block *bp; - int len, off; - - if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy) - return; - - if((bp = qget(ether->oq)) == nil) - return; - pkt = (Etherpkt*)bp->rp; - - // - // If the packet header type field is > 1500 it is an IP or - // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. - // - memset(&ctlr->txf, 0, sizeof(ctlr->txf)); - if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ - ctlr->txf.framectl = WF_Data; - memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); - memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); - memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); - memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); - memmove(&ctlr->txf.type, pkt->type, 2); - bp->rp += ETHERHDRSIZE; - len = BLEN(bp); - off = WF_802_11_Off; - ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; - hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); - hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); - hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); - } - else{ - len = BLEN(bp); - off = WF_802_3_Off; - ctlr->txf.dlen = len; - } - w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); - w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); - - if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ - DEBUG("wavelan: transmit failed\n"); - ctlr->ntxerr++; - } - else{ - ctlr->txbusy = 1; - ctlr->txtmout = 2; - } - freeb(bp); -} - -static void -w_txdone(Ctlr* ctlr, int sts) -{ - ctlr->txbusy = 0; - ctlr->txtmout = 0; - if (sts & WTxErrEv) - ctlr->ntxerr++; - else - ctlr->ntx++; -} - -static int -w_stats(Ctlr* ctlr) -{ - int i, rc, sp; - Wltv ltv; - ulong* p = (ulong*)&ctlr->WStats; - ulong* pend = (ulong*)&ctlr->end; - - sp = csr_ins(ctlr, WR_InfoId); - ltv.len = ltv.type = 0; - w_read(ctlr, sp, 0, <v, 4); - if (ltv.type == WTyp_Stats){ - ltv.len--; - for (i = 0; i < ltv.len && p < pend; i++){ - rc = csr_ins(ctlr, WR_Data1); - if (rc > 0xf000) - rc = ~rc & 0xffff; - p[i] += rc; - } - return 0; - } - return -1; -} - -static void -w_intr(Ether *ether) -{ - int rc, txid; - Ctlr* ctlr = (Ctlr*) ether->ctlr; - - if (ctlr->attached == 0){ - csr_ack(ctlr, 0xffff); - csr_outs(ctlr, WR_IntEna, 0); - return; - } - - rc = csr_ins(ctlr, WR_EvSts); - csr_ack(ctlr, ~WEvs); // Not interested in them - if (rc & WRXEv){ - w_rxdone(ether); - csr_ack(ctlr, WRXEv); - } - if (rc & WTXEv){ - w_txdone(ctlr, rc); - csr_ack(ctlr, WTXEv); - } - if (rc & WAllocEv){ - ctlr->nalloc++; - txid = csr_ins(ctlr, WR_Alloc); - csr_ack(ctlr, WAllocEv); - if (txid == ctlr->txdid){ - if ((rc & WTXEv) == 0) - w_txdone(ctlr, rc); - } - } - if (rc & WInfoEv){ - ctlr->ninfo++; - w_stats(ctlr); - csr_ack(ctlr, WInfoEv); - } - if (rc & WTxErrEv){ - w_txdone(ctlr, rc); - csr_ack(ctlr, WTxErrEv); - } - if (rc & WIDropEv){ - ctlr->nidrop++; - csr_ack(ctlr, WIDropEv); - } - w_txstart(ether); -} - -// Watcher to ensure that the card still works properly and -// to request WStats updates once a minute. -// BUG: it runs much more often, see the comment below. - -static void -w_timer(void* arg) -{ - Ether* ether = (Ether*) arg; - Ctlr* ctlr = (Ctlr*)ether->ctlr; - - for(;;){ - tsleep(&ctlr->timer, return0, 0, 50); - ctlr = (Ctlr*)ether->ctlr; - if (ctlr == 0) - break; - if (ctlr->attached == 0) - continue; - ctlr->ticks++; - - ilock(ctlr); - - // Seems that the card gets frames BUT does - // not send the interrupt; this is a problem because - // I suspect it runs out of receive buffers and - // stops receiving until a transmit watchdog - // reenables the card. - // The problem is serious because it leads to - // poor rtts. - // This can be seen clearly by commenting out - // the next if and doing a ping: it will stop - // receiving (although the icmp replies are being - // issued from the remote) after a few seconds. - // Of course this `bug' could be because I'm reading - // the card frames in the wrong way; due to the - // lack of documentation I cannot know. - - if (csr_ins(ctlr, WR_EvSts)&WEvs){ - ctlr->tickintr++; - w_intr(ether); - } - - if ((ctlr->ticks % 10) == 0) { - if (ctlr->txtmout && --ctlr->txtmout == 0){ - ctlr->nwatchdogs++; - w_txdone(ctlr, WTxErrEv); - if (w_enable(ether)){ - DEBUG("wavelan: wdog enable failed\n"); - } - w_txstart(ether); - } - if ((ctlr->ticks % 120) == 0) - if (ctlr->txbusy == 0) - w_cmd(ctlr, WCmdAskStats, WTyp_Stats); - } - iunlock(ctlr); - } - pexit("terminated",0); -} - -static void -multicast(void*, uchar*, int) -{ - // BUG: to be added. -} - -static void -attach(Ether* ether) -{ - Ctlr* ctlr; - char name[64]; - int rc; - - if (ether->ctlr == 0) - return; - - snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); - ctlr = (Ctlr*) ether->ctlr; - if (ctlr->attached == 0){ - ilock(ctlr); - rc = w_enable(ether); - iunlock(ctlr); - if(rc == 0){ - ctlr->attached = 1; - kproc(name, w_timer, ether); - } else - print("#l%d: enable failed\n",ether->ctlrno); - } -} - -#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) -#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) - -static long -ifstat(Ether* ether, void* a, long n, ulong offset) -{ - Ctlr *ctlr = (Ctlr*) ether->ctlr; - char *k, *p; - int i, l, txid; - - ether->oerrs = ctlr->ntxerr; - ether->crcs = ctlr->nrxfcserr; - ether->frames = 0; - ether->buffs = ctlr->nrxdropnobuf; - ether->overflows = 0; - - // - // Offset must be zero or there's a possibility the - // new data won't match the previous read. - // - if(n == 0 || offset != 0) - return 0; - - p = malloc(READSTR); - l = 0; - - PRINTSTAT("Signal: %d\n", ctlr->signal-149); - PRINTSTAT("Noise: %d\n", ctlr->noise-149); - PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); - PRINTSTAT("Interrupts: %lud\n", ctlr->nints); - PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); - PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); - PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); - PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); - PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); - PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); - PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); - PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); - PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); - PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); - PRINTSTAT("Ticks: %ud\n", ctlr->ticks); - PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); - k = ((ctlr->attached) ? "attached" : "not attached"); - PRINTSTAT("Card %s", k); - k = ((ctlr->txbusy)? ", txbusy" : ""); - PRINTSTAT("%s\n", k); - - if (ctlr->hascrypt){ - PRINTSTR("Keys: "); - for (i = 0; i < WNKeys; i++){ - if (ctlr->keys.keys[i].len == 0) - PRINTSTR("none "); - else if (SEEKEYS == 0) - PRINTSTR("set "); - else - PRINTSTAT("%s ", ctlr->keys.keys[i].dat); - } - PRINTSTR("\n"); - } - - // real card stats - ilock(ctlr); - PRINTSTR("\nCard stats: \n"); - PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); - PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); - i = ltv_ins(ctlr, WTyp_Ptype); - PRINTSTAT("Port type: %d\n", i); - PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); - PRINTSTAT("Current Transmit rate: %d\n", - ltv_ins(ctlr, WTyp_CurTxRate)); - PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); - PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); - PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); - if(i == 3) - PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); - else { - Wltv ltv; - PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); - ltv.type = WTyp_BaseID; - ltv.len = 4; - if (w_inltv(ctlr, <v)) - print("#l%d: unable to read base station mac addr\n", ether->ctlrno); - l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", - ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); - } - PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); - PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); - if (ltv_ins(ctlr, WTyp_HasCrypt) == 0) - PRINTSTR("WEP: not supported\n"); - else { - if (ltv_ins(ctlr, WTyp_Crypt) == 0) - PRINTSTR("WEP: disabled\n"); - else{ - PRINTSTR("WEP: enabled\n"); - k = ((ctlr->xclear)? "excluded": "included"); - PRINTSTAT("Clear packets: %s\n", k); - txid = ltv_ins(ctlr, WTyp_TxKey); - PRINTSTAT("Transmit key id: %d\n", txid); - } - } - iunlock(ctlr); - - PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); - PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); - PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); - PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); - PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); - PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); - PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); - PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); - PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); - PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); - PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); - PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); - PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); - PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); - PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); - PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); - PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); - PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); - PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); - PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); - PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); - USED(l); - n = readstr(offset, a, n, p); - free(p); - return n; -} -#undef PRINTSTR -#undef PRINTSTAT - -static int -option(Ctlr* ctlr, char* buf, long n) -{ - char *p; - int i, r; - WKey *key; - Cmdbuf *cb; - - r = 0; - - cb = parsecmd(buf, n); - if(cb->nf < 2) - r = -1; - else if(cistrcmp(cb->f[0], "essid") == 0){ - if (cistrcmp(cb->f[1],"default") == 0) - p = ""; - else - p = cb->f[1]; - if(ctlr->ptype == 3){ - memset(ctlr->netname, 0, sizeof(ctlr->netname)); - strncpy(ctlr->netname, p, WNameLen); - } - else{ - memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); - strncpy(ctlr->wantname, p, WNameLen); - } - } - else if(cistrcmp(cb->f[0], "station") == 0){ - memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); - strncpy(ctlr->nodename, cb->f[1], WNameLen); - } - else if(cistrcmp(cb->f[0], "channel") == 0){ - if((i = atoi(cb->f[1])) >= 1 && i <= 16) - ctlr->chan = i; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "mode") == 0){ - if(cistrcmp(cb->f[1], "managed") == 0) - ctlr->ptype = WPTypeManaged; - else if(cistrcmp(cb->f[1], "wds") == 0) - ctlr->ptype = WPTypeWDS; - else if(cistrcmp(cb->f[1], "adhoc") == 0) - ctlr->ptype = WPTypeAdHoc; - else if((i = atoi(cb->f[1])) >= 1 && i <= 3) - ctlr->ptype = i; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "ibss") == 0){ - if(cistrcmp(cb->f[1], "on") == 0) - ctlr->createibss = 1; - else - ctlr->createibss = 0; - } - else if(cistrcmp(cb->f[0], "crypt") == 0){ - if(cistrcmp(cb->f[1], "off") == 0) - ctlr->crypt = 0; - else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) - ctlr->crypt = 1; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "clear") == 0){ - if(cistrcmp(cb->f[1], "on") == 0) - ctlr->xclear = 0; - else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) - ctlr->xclear = 1; - else - r = -1; - } - else if(cistrncmp(cb->f[0], "key", 3) == 0){ - if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ - ctlr->txkey = i-1; - key = &ctlr->keys.keys[ctlr->txkey]; - key->len = strlen(cb->f[1]); - if (key->len > WKeyLen) - key->len = WKeyLen; - memset(key->dat, 0, sizeof(key->dat)); - memmove(key->dat, cb->f[1], key->len); - } - else - r = -1; - } - else if(cistrcmp(cb->f[0], "txkey") == 0){ - if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) - ctlr->txkey = i-1; - else - r = -1; - } - else if(cistrcmp(cb->f[0], "pm") == 0){ - if(cistrcmp(cb->f[1], "off") == 0) - ctlr->pmena = 0; - else if(cistrcmp(cb->f[1], "on") == 0){ - ctlr->pmena = 1; - if(cb->nf == 3){ - i = atoi(cb->f[2]); - // check range here? what are the units? - ctlr->pmwait = i; - } - } - else - r = -1; - } - else - r = -2; - free(cb); - - return r; -} - -static long -ctl(Ether* ether, void* buf, long n) -{ - Ctlr *ctlr; - - if((ctlr = ether->ctlr) == nil) - error(Enonexist); - if(ctlr->attached == 0) - error(Eshutdown); - - ilock(ctlr); - if(option(ctlr, buf, n)){ - iunlock(ctlr); - error(Ebadctl); - } - if(ctlr->txbusy) - w_txdone(ctlr, WTxErrEv); - w_enable(ether); - w_txstart(ether); - iunlock(ctlr); - - return n; -} - -static void -transmit(Ether* ether) -{ - Ctlr* ctlr = ether->ctlr; - - if (ctlr == 0) - return; - - ilock(ctlr); - ctlr->ntxrq++; - w_txstart(ether); - iunlock(ctlr); -} - -static void -promiscuous(void* arg, int on) -{ - Ether* ether = (Ether*)arg; - Ctlr* ctlr = ether->ctlr; - - if (ctlr == nil) - error("card not found"); - if (ctlr->attached == 0) - error("card not attached"); - ilock(ctlr); - ltv_outs(ctlr, WTyp_Prom, (on?1:0)); - iunlock(ctlr); -} - -static void -interrupt(Ureg* ,void* arg) -{ - Ether* ether = (Ether*) arg; - Ctlr* ctlr = (Ctlr*) ether->ctlr; - - if (ctlr == 0) - return; - ilock(ctlr); - ctlr->nints++; - w_intr(ether); - iunlock(ctlr); -} - -static char* wavenames[] = { - "WaveLAN/IEEE", - "TrueMobile 1150", - "Instant Wireless ; Network PC CARD", - nil, -}; - -static int -genreset(Ether* ether, Ctlr *ctlr) -{ - int i; - char *p; - Wltv ltv; - - w_intdis(ctlr); - if (w_cmd(ctlr,WCmdIni,0)){ - print("#l%d: init failed\n", ether->ctlrno); - return -1; - } - w_intdis(ctlr); - ltv_outs(ctlr, WTyp_Tick, 8); - - ctlr->chan = 0; - ctlr->ptype = WDfltPType; - ctlr->txkey = 0; - ctlr->createibss = 0; - ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; - ctlr->keys.type = WTyp_Keys; - if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) - ctlr->crypt = 1; - *ctlr->netname = *ctlr->wantname = 0; - strcpy(ctlr->nodename, "Plan 9 STA"); - - for(i = 0; i < ether->nopt; i++){ - if(p = strchr(ether->opt[i], '=')) - *p = ' '; - option(ctlr, ether->opt[i], strlen(ether->opt[i])); - } - - ctlr->netname[WNameLen-1] = 0; - ctlr->wantname[WNameLen-1] = 0; - ctlr->nodename[WNameLen-1] =0; - - ltv.type = WTyp_Mac; - ltv.len = 4; - if (w_inltv(ctlr, <v)){ - print("#l%d: unable to read mac addr\n", - ether->ctlrno); - return -1; - } - memmove(ether->ea, ltv.addr, Eaddrlen); - - if (ctlr->chan == 0) - ctlr->chan = ltv_ins(ctlr, WTyp_Chan); - ctlr->apdensity = WDfltApDens; - ctlr->rtsthres = WDfltRtsThres; - ctlr->txrate = WDfltTxRate; - ctlr->maxlen = WMaxLen; - ctlr->pmena = 0; - ctlr->pmwait = 100; - ctlr->signal = 1; - ctlr->noise = 1; - - // link to ether - ether->ctlr = ctlr; - ether->mbps = 10; - ether->attach = attach; - ether->interrupt = interrupt; - ether->transmit = transmit; - ether->ifstat = ifstat; - ether->ctl = ctl; - ether->promiscuous = promiscuous; - ether->multicast = multicast; - ether->arg = ether; - -// DEBUG("#l%d: irq %ld port %lx type %s", -// ether->ctlrno, ether->irq, ether->port, ether->type); -// DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", -// ether->ea[0], ether->ea[1], ether->ea[2], -// ether->ea[3], ether->ea[4], ether->ea[5]); - - return 0; -} +#include "wavelan.h" static int wavelanpcmciareset(Ether *ether) { int i; + char *p; Ctlr *ctlr; if((ctlr = malloc(sizeof(Ctlr))) == nil) @@ -1381,6 +28,7 @@ wavelanpcmciareset(Ether *ether) if (ether->port==0) ether->port=WDfltIOB; ctlr->iob = ether->port; + if (ether->irq==0) ether->irq=WDfltIRQ; @@ -1414,7 +62,7 @@ wavelanpcmciareset(Ether *ether) // DEBUG("#l%d: port=0x%lx irq=%ld\n", // ether->ctlrno, ether->port, ether->irq); - if(genreset(ether, ctlr) < 0){ + if(wavelanreset(ether, ctlr) < 0){ abort: iofree(ether->port); abort1: @@ -1423,6 +71,13 @@ wavelanpcmciareset(Ether *ether) ether->ctlr = nil; return -1; } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + iunlock(ctlr); return 0; } @@ -1482,6 +137,7 @@ static int wavelanpcireset(Ether *ether) { int i; + char *p; Ctlr *ctlr; if(ctlrhead == nil) @@ -1517,11 +173,17 @@ wavelanpcireset(Ether *ether) print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n", ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd)); - if(genreset(ether, ctlr) < 0){ + if(wavelanreset(ether, ctlr) < 0){ iunlock(ctlr); ether->ctlr = nil; return -1; } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } iunlock(ctlr); return 0; } diff --git a/pc/wavelan.c b/pc/wavelan.c index 293715c1a031651244316c03b934962be3c8bab9..2fc657f054c45455475e3f58e787d9df7173be3d 100644 --- a/pc/wavelan.c +++ b/pc/wavelan.c @@ -1,3 +1,22 @@ +/* + Lucent Wavelan IEEE 802.11 pcmcia. + There is almost no documentation for the card. + the driver is done using both the FreeBSD, Linux and + original Plan 9 drivers as `documentation'. + + Has been used with the card plugged in during all up time. + no cards removals/insertions yet. + + For known BUGS see the comments below. Besides, + the driver keeps interrupts disabled for just too + long. When it gets robust, locks should be revisited. + + BUGS: check endian, alignment and mem/io issues; + multicast; + receive watchdog interrupts. + TODO: automatic power management; + improve locking. + */ #include "u.h" #include "../port/lib.h" #include "mem.h" @@ -5,164 +24,1052 @@ #include "fns.h" #include "io.h" #include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" +#include "wavelan.h" -extern Dev rootdevtab; -extern Dev consdevtab; -extern Dev envdevtab; -extern Dev pipedevtab; -extern Dev procdevtab; -extern Dev mntdevtab; -extern Dev srvdevtab; -extern Dev dupdevtab; -extern Dev rtcdevtab; -extern Dev ssldevtab; -extern Dev mntstatsdevtab; -extern Dev etherdevtab; -extern Dev ipdevtab; -extern Dev drawdevtab; -extern Dev mousedevtab; -extern Dev vgadevtab; -extern Dev scsidevtab; -extern Dev cddevtab; -extern Dev sddevtab; -extern Dev atadevtab; -extern Dev floppydevtab; -extern Dev audiodevtab; -extern Dev i82365devtab; -extern Dev lptdevtab; -extern Dev ns16552devtab; -extern Dev lmldevtab; -Dev* devtab[]={ - &rootdevtab, - &consdevtab, - &envdevtab, - &pipedevtab, - &procdevtab, - &mntdevtab, - &srvdevtab, - &dupdevtab, - &rtcdevtab, - &ssldevtab, - &mntstatsdevtab, - ðerdevtab, - &ipdevtab, - &drawdevtab, - &mousedevtab, - &vgadevtab, - &scsidevtab, - &cddevtab, - &sddevtab, - &atadevtab, - &floppydevtab, - &audiodevtab, - &i82365devtab, - &lptdevtab, - &ns16552devtab, - &lmldevtab, - nil, -}; +/* + * When we're using a PCI device and memory-mapped I/O, + * the registers are spaced out as though each takes 32 bits, + * even though they are only 16-bit registers. Thus, + * ctlr->mmb[reg] is the right way to access register reg, + * even though a priori you'd expect to use ctlr->mmb[reg/2]. + */ +void +csr_outs(Ctlr *ctlr, int reg, ushort arg) +{ + if(ctlr->mmb) + ctlr->mmb[reg] = arg; + else + outs(ctlr->iob+reg, arg); +} -extern void ether2000link(void); -extern void ether2114xlink(void); -extern void ether589link(void); -extern void ether79c970link(void); -extern void ether8003link(void); -extern void ether82557link(void); -extern void etherelnk3link(void); -extern void etherwavelanlink(void); -extern void ethermediumlink(void); -void links(void){ - ether2000link(); - ether2114xlink(); - ether589link(); - ether79c970link(); - ether8003link(); - ether82557link(); - etherelnk3link(); - etherwavelanlink(); - ethermediumlink(); -} - -extern PCArch archgeneric; -extern PCArch archmp; -PCArch* knownarch[] = { - &archgeneric, - &archmp, - nil, -}; +ushort +csr_ins(Ctlr *ctlr, int reg) +{ + if(ctlr->mmb) + return ctlr->mmb[reg]; + else + return ins(ctlr->iob+reg); +} -extern SCSIdev scsibuslogicdev; -SCSIdev* scsidev[] = { - &scsibuslogicdev, - nil, -}; +static void +csr_ack(Ctlr *ctlr, int ev) +{ + csr_outs(ctlr, WR_EvAck, ev); +} -#define Image IMAGE -#include -#include -#include -#include "screen.h" -extern VGAdev vgaark2000pvdev; -extern VGAdev vgaclgd542xdev; -extern VGAdev vgact65545dev; -extern VGAdev vgamach64xxdev; -extern VGAdev vgamga2164wdev; -extern VGAdev vgas3dev; -VGAdev* vgadev[] = { - &vgaark2000pvdev, - &vgaclgd542xdev, - &vgact65545dev, - &vgamach64xxdev, - &vgamga2164wdev, - &vgas3dev, - nil, -}; +static void +csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; -extern VGAcur vgaark2000pvcur; -extern VGAcur vgabt485cur; -extern VGAcur vgaclgd542xcur; -extern VGAcur vgact65545cur; -extern VGAcur vgamach64xxcur; -extern VGAcur vgamga2164wcur; -extern VGAcur vgargb524cur; -extern VGAcur vgas3cur; -extern VGAcur vgatvp3020cur; -extern VGAcur vgatvp3026cur; -VGAcur* vgacur[] = { - &vgaark2000pvcur, - &vgabt485cur, - &vgaclgd542xcur, - &vgact65545cur, - &vgamach64xxcur, - &vgamga2164wcur, - &vgargb524cur, - &vgas3cur, - &vgatvp3020cur, - &vgatvp3026cur, - nil, -}; + if(ctlr->mmb){ + rp = &ctlr->mmb[reg]; + wp = dat; + while(ndat-- > 0) + *wp++ = *rp; + }else + inss(ctlr->iob+reg, dat, ndat); +} + +static void +csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; + + if(ctlr->mmb){ + rp = dat; + wp = &ctlr->mmb[reg]; + while(ndat-- > 0) + *wp = *rp++; + }else + outss(ctlr->iob+reg, dat, ndat); +} + +// w_... routines do not ilock the Ctlr and should +// be called locked. + +void +w_intdis(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, 0); + csr_ack(ctlr, 0xffff); +} + +static void +w_intena(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, WEvs); +} + +int +w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) +{ + int i, rc; + + for(i=0; ictlrno, cmd, csr_ins(ctlr, WR_Cmd)); + return -1; + } + + csr_outs(ctlr, WR_Parm0, arg); + csr_outs(ctlr, WR_Cmd, cmd); + + for(i=0; ictlrno, cmd, i); + if(i == IniTmOut){ + print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); + return -1; + } + } + rc = csr_ins(ctlr, WR_Sts); + csr_ack(ctlr, WCmdEv); + + if((rc&WCmdMsk) != (cmd&WCmdMsk)){ + print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + if(rc&WResSts){ + /* + * Don't print; this happens on every WCmdAccWr for some reason. + */ + if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + return 0; +} + +static int +w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan) +{ + int i, rc; + static ushort sel[] = { WR_Sel0, WR_Sel1 }; + static ushort off[] = { WR_Off0, WR_Off1 }; + + if (chan != 0 && chan != 1) + panic("wavelan: bad chan\n"); + csr_outs(ctlr, sel[chan], id); + csr_outs(ctlr, off[chan], offset); + for (i=0; itype)){ + DEBUG("wavelan: access read failed\n"); + return -1; + } + if (w_seek(ctlr,ltv->type,0,1)){ + DEBUG("wavelan: seek failed\n"); + return -1; + } + len = csr_ins(ctlr, WR_Data1); + if (len > ltv->len) + return -1; + ltv->len = len; + if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ + USED(code); + DEBUG("wavelan: type %x != code %x\n",ltv->type,code); + return -1; + } + if(ltv->len > 0) + csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); + + return 0; +} + +static void +w_outltv(Ctlr* ctlr, Wltv* ltv) +{ + if(w_seek(ctlr,ltv->type, 0, 1)) + return; + csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); + w_cmd(ctlr, WCmdAccWr, ltv->type); +} + +void +ltv_outs(Ctlr* ctlr, int type, ushort val) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = val; + w_outltv(ctlr, <v); +} + +int +ltv_ins(Ctlr* ctlr, int type) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = 0; + if(w_inltv(ctlr, <v)) + return -1; + return ltv.val; +} + +static void +ltv_outstr(Ctlr* ctlr, int type, char* val) +{ + Wltv ltv; + int len; + + len = strlen(val); + if(len > sizeof(ltv.s)) + len = sizeof(ltv.s); + memset(<v, 0, sizeof(ltv)); + ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; + ltv.type = type; + +// This should be ltv.slen = len; according to Axel Belinfante + ltv.slen = len; + + strncpy(ltv.s, val, len); + w_outltv(ctlr, <v); +} + +static char Unkname[] = "who knows"; +static char Nilname[] = "card does not tell"; + +static char* +ltv_inname(Ctlr* ctlr, int type) +{ + static Wltv ltv; + int len; + + memset(<v,0,sizeof(ltv)); + ltv.len = WNameLen/2+2; + ltv.type = type; + if (w_inltv(ctlr, <v)) + return Unkname; + len = ltv.slen; + if(len == 0 || ltv.s[0] == 0) + return Nilname; + if(len >= sizeof ltv.s) + len = sizeof ltv.s - 1; + ltv.s[len] = '\0'; + return ltv.s; +} + +static int +w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + if (w_seek(ctlr, type, off, 1)){ + DEBUG("wavelan: w_read: seek failed"); + return 0; + } + csr_inss(ctlr, WR_Data1, buf, len/2); + + return len; +} + +static int +w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + int tries; + + for (tries=0; tries < WTmOut; tries++){ + if (w_seek(ctlr, type, off, 0)){ + DEBUG("wavelan: w_write: seek failed\n"); + return 0; + } + + csr_outss(ctlr, WR_Data0, buf, len/2); + + csr_outs(ctlr, WR_Data0, 0xdead); + csr_outs(ctlr, WR_Data0, 0xbeef); + if (w_seek(ctlr, type, off + len, 0)){ + DEBUG("wavelan: write seek failed\n"); + return 0; + } + if (csr_ins(ctlr, WR_Data0) == 0xdead) + if (csr_ins(ctlr, WR_Data0) == 0xbeef) + return len; + DEBUG("wavelan: Hermes bug byte.\n"); + return 0; + } + DEBUG("wavelan: tx timeout\n"); + return 0; +} + +static int +w_alloc(Ctlr* ctlr, int len) +{ + int rc; + int i,j; + + if (w_cmd(ctlr, WCmdMalloc, len)==0) + for (i = 0; ictlr; + + if (!ctlr) + return -1; + + w_intdis(ctlr); + w_cmd(ctlr, WCmdDis, 0); + w_intdis(ctlr); + if(w_cmd(ctlr, WCmdIni, 0)) + return -1; + w_intdis(ctlr); + + ltv_outs(ctlr, WTyp_Tick, 8); + ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); + ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); + ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); + ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); + ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); + ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); + ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); + ltv_outs(ctlr, WTyp_PM, ctlr->pmena); + if (*ctlr->netname) + ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); + if (*ctlr->wantname) + ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); + ltv_outs(ctlr, WTyp_Chan, ctlr->chan); + if (*ctlr->nodename) + ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); + ltv.len = 4; + ltv.type = WTyp_Mac; + memmove(ltv.addr, ether->ea, Eaddrlen); + w_outltv(ctlr, <v); + + ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); + + if (ctlr->hascrypt){ + ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); + ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); + w_outltv(ctlr, &ctlr->keys); + ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); + } + + // BUG: set multicast addresses + + if (w_cmd(ctlr, WCmdEna, 0)){ + DEBUG("wavelan: Enable failed"); + return -1; + } + ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + if (ctlr->txdid == -1 || ctlr->txmid == -1) + DEBUG("wavelan: alloc failed"); + ctlr->txbusy = 0; + w_intena(ctlr); + return 0; +} + +static void +w_rxdone(Ether* ether) +{ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + int len, sp; + WFrame f; + Block* bp=0; + Etherpkt* ep; + + sp = csr_ins(ctlr, WR_RXId); + len = w_read(ctlr, sp, 0, &f, sizeof(f)); + if (len == 0){ + DEBUG("wavelan: read frame error\n"); + goto rxerror; + } + if (f.sts&WF_Err){ + goto rxerror; + } + switch(f.sts){ + case WF_1042: + case WF_Tunnel: + case WF_WMP: + len = f.dlen + WSnapHdrLen; + bp = iallocb(ETHERHDRSIZE + len + 2); + if (!bp) + goto rxerror; + ep = (Etherpkt*) bp->wp; + memmove(ep->d, f.addr1, Eaddrlen); + memmove(ep->s, f.addr2, Eaddrlen); + memmove(ep->type,&f.type,2); + bp->wp += ETHERHDRSIZE; + if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ + DEBUG("wavelan: read 802.11 error\n"); + goto rxerror; + } + bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); + break; + default: + len = ETHERHDRSIZE + f.dlen + 2; + bp = iallocb(len); + if (!bp) + goto rxerror; + if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ + DEBUG("wavelan: read 800.3 error\n"); + goto rxerror; + } + bp->wp += len; + } + + ctlr->nrx++; + etheriq(ether,bp,1); + ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; + ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; + return; + +rxerror: + freeb(bp); + ctlr->nrxerr++; +} + +static void +w_txstart(Ether* ether) +{ + Etherpkt *pkt; + Ctlr *ctlr; + Block *bp; + int len, off; + + if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy) + return; + + if((bp = qget(ether->oq)) == nil) + return; + pkt = (Etherpkt*)bp->rp; + + // + // If the packet header type field is > 1500 it is an IP or + // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. + // + memset(&ctlr->txf, 0, sizeof(ctlr->txf)); + if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ + ctlr->txf.framectl = WF_Data; + memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); + memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); + memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); + memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); + memmove(&ctlr->txf.type, pkt->type, 2); + bp->rp += ETHERHDRSIZE; + len = BLEN(bp); + off = WF_802_11_Off; + ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; + hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); + hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); + hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); + } + else{ + len = BLEN(bp); + off = WF_802_3_Off; + ctlr->txf.dlen = len; + } + w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); + w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); + + if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ + DEBUG("wavelan: transmit failed\n"); + ctlr->ntxerr++; + } + else{ + ctlr->txbusy = 1; + ctlr->txtmout = 2; + } + freeb(bp); +} + +static void +w_txdone(Ctlr* ctlr, int sts) +{ + ctlr->txbusy = 0; + ctlr->txtmout = 0; + if (sts & WTxErrEv) + ctlr->ntxerr++; + else + ctlr->ntx++; +} + +static int +w_stats(Ctlr* ctlr) +{ + int i, rc, sp; + Wltv ltv; + ulong* p = (ulong*)&ctlr->WStats; + ulong* pend = (ulong*)&ctlr->end; + + sp = csr_ins(ctlr, WR_InfoId); + ltv.len = ltv.type = 0; + w_read(ctlr, sp, 0, <v, 4); + if (ltv.type == WTyp_Stats){ + ltv.len--; + for (i = 0; i < ltv.len && p < pend; i++){ + rc = csr_ins(ctlr, WR_Data1); + if (rc > 0xf000) + rc = ~rc & 0xffff; + p[i] += rc; + } + return 0; + } + return -1; +} + +static void +w_intr(Ether *ether) +{ + int rc, txid; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if (ctlr->attached == 0){ + csr_ack(ctlr, 0xffff); + csr_outs(ctlr, WR_IntEna, 0); + return; + } + + rc = csr_ins(ctlr, WR_EvSts); + csr_ack(ctlr, ~WEvs); // Not interested in them + if (rc & WRXEv){ + w_rxdone(ether); + csr_ack(ctlr, WRXEv); + } + if (rc & WTXEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTXEv); + } + if (rc & WAllocEv){ + ctlr->nalloc++; + txid = csr_ins(ctlr, WR_Alloc); + csr_ack(ctlr, WAllocEv); + if (txid == ctlr->txdid){ + if ((rc & WTXEv) == 0) + w_txdone(ctlr, rc); + } + } + if (rc & WInfoEv){ + ctlr->ninfo++; + w_stats(ctlr); + csr_ack(ctlr, WInfoEv); + } + if (rc & WTxErrEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTxErrEv); + } + if (rc & WIDropEv){ + ctlr->nidrop++; + csr_ack(ctlr, WIDropEv); + } + w_txstart(ether); +} + +// Watcher to ensure that the card still works properly and +// to request WStats updates once a minute. +// BUG: it runs much more often, see the comment below. + +static void +w_timer(void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*)ether->ctlr; + + for(;;){ + tsleep(&ctlr->timer, return0, 0, 50); + ctlr = (Ctlr*)ether->ctlr; + if (ctlr == 0) + break; + if (ctlr->attached == 0) + continue; + ctlr->ticks++; + + ilock(ctlr); + + // Seems that the card gets frames BUT does + // not send the interrupt; this is a problem because + // I suspect it runs out of receive buffers and + // stops receiving until a transmit watchdog + // reenables the card. + // The problem is serious because it leads to + // poor rtts. + // This can be seen clearly by commenting out + // the next if and doing a ping: it will stop + // receiving (although the icmp replies are being + // issued from the remote) after a few seconds. + // Of course this `bug' could be because I'm reading + // the card frames in the wrong way; due to the + // lack of documentation I cannot know. + + if (csr_ins(ctlr, WR_EvSts)&WEvs){ + ctlr->tickintr++; + w_intr(ether); + } + + if ((ctlr->ticks % 10) == 0) { + if (ctlr->txtmout && --ctlr->txtmout == 0){ + ctlr->nwatchdogs++; + w_txdone(ctlr, WTxErrEv); + if (w_enable(ether)){ + DEBUG("wavelan: wdog enable failed\n"); + } + w_txstart(ether); + } + if ((ctlr->ticks % 120) == 0) + if (ctlr->txbusy == 0) + w_cmd(ctlr, WCmdAskStats, WTyp_Stats); + } + iunlock(ctlr); + } + pexit("terminated",0); +} + +void +w_multicast(void*, uchar*, int) +{ + // BUG: to be added. +} + +void +w_attach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + int rc; + + if (ether->ctlr == 0) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if (ctlr->attached == 0){ + ilock(ctlr); + rc = w_enable(ether); + iunlock(ctlr); + if(rc == 0){ + ctlr->attached = 1; + kproc(name, w_timer, ether); + } else + print("#l%d: enable failed\n",ether->ctlrno); + } +} + +#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) +#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) + +long +w_ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr = (Ctlr*) ether->ctlr; + char *k, *p; + int i, l, txid; + + ether->oerrs = ctlr->ntxerr; + ether->crcs = ctlr->nrxfcserr; + ether->frames = 0; + ether->buffs = ctlr->nrxdropnobuf; + ether->overflows = 0; + + // + // Offset must be zero or there's a possibility the + // new data won't match the previous read. + // + if(n == 0 || offset != 0) + return 0; + + p = malloc(READSTR); + l = 0; + + PRINTSTAT("Signal: %d\n", ctlr->signal-149); + PRINTSTAT("Noise: %d\n", ctlr->noise-149); + PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); + PRINTSTAT("Interrupts: %lud\n", ctlr->nints); + PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); + PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); + PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); + PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); + PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); + PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); + PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); + PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); + PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); + PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); + PRINTSTAT("Ticks: %ud\n", ctlr->ticks); + PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); + k = ((ctlr->attached) ? "attached" : "not attached"); + PRINTSTAT("Card %s", k); + k = ((ctlr->txbusy)? ", txbusy" : ""); + PRINTSTAT("%s\n", k); + + if (ctlr->hascrypt){ + PRINTSTR("Keys: "); + for (i = 0; i < WNKeys; i++){ + if (ctlr->keys.keys[i].len == 0) + PRINTSTR("none "); + else if (SEEKEYS == 0) + PRINTSTR("set "); + else + PRINTSTAT("%s ", ctlr->keys.keys[i].dat); + } + PRINTSTR("\n"); + } + + // real card stats + ilock(ctlr); + PRINTSTR("\nCard stats: \n"); + PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); + PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); + i = ltv_ins(ctlr, WTyp_Ptype); + PRINTSTAT("Port type: %d\n", i); + PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); + PRINTSTAT("Current Transmit rate: %d\n", + ltv_ins(ctlr, WTyp_CurTxRate)); + PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); + PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); + PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); + if(i == 3) + PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); + else { + Wltv ltv; + PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); + ltv.type = WTyp_BaseID; + ltv.len = 4; + if (w_inltv(ctlr, <v)) + print("#l%d: unable to read base station mac addr\n", ether->ctlrno); + l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); + } + PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); + PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); + if (ltv_ins(ctlr, WTyp_HasCrypt) == 0) + PRINTSTR("WEP: not supported\n"); + else { + if (ltv_ins(ctlr, WTyp_Crypt) == 0) + PRINTSTR("WEP: disabled\n"); + else{ + PRINTSTR("WEP: enabled\n"); + k = ((ctlr->xclear)? "excluded": "included"); + PRINTSTAT("Clear packets: %s\n", k); + txid = ltv_ins(ctlr, WTyp_TxKey); + PRINTSTAT("Transmit key id: %d\n", txid); + } + } + iunlock(ctlr); + + PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); + PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); + PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); + PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); + PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); + PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); + PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); + PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); + PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); + PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); + PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); + PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); + PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); + PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); + PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); + PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); + PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); + PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); + PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); + PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); + PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); + USED(l); + n = readstr(offset, a, n, p); + free(p); + return n; +} +#undef PRINTSTR +#undef PRINTSTAT + +int +w_option(Ctlr* ctlr, char* buf, long n) +{ + char *p; + int i, r; + WKey *key; + Cmdbuf *cb; + + r = 0; + + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "essid") == 0){ + if (cistrcmp(cb->f[1],"default") == 0) + p = ""; + else + p = cb->f[1]; + if(ctlr->ptype == 3){ + memset(ctlr->netname, 0, sizeof(ctlr->netname)); + strncpy(ctlr->netname, p, WNameLen); + } + else{ + memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); + strncpy(ctlr->wantname, p, WNameLen); + } + } + else if(cistrcmp(cb->f[0], "station") == 0){ + memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); + strncpy(ctlr->nodename, cb->f[1], WNameLen); + } + else if(cistrcmp(cb->f[0], "channel") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= 16) + ctlr->chan = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "mode") == 0){ + if(cistrcmp(cb->f[1], "managed") == 0) + ctlr->ptype = WPTypeManaged; + else if(cistrcmp(cb->f[1], "wds") == 0) + ctlr->ptype = WPTypeWDS; + else if(cistrcmp(cb->f[1], "adhoc") == 0) + ctlr->ptype = WPTypeAdHoc; + else if((i = atoi(cb->f[1])) >= 1 && i <= 3) + ctlr->ptype = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "ibss") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->createibss = 1; + else + ctlr->createibss = 0; + } + else if(cistrcmp(cb->f[0], "crypt") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->crypt = 0; + else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) + ctlr->crypt = 1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->xclear = 0; + else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) + ctlr->xclear = 1; + else + r = -1; + } + else if(cistrncmp(cb->f[0], "key", 3) == 0){ + if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ + ctlr->txkey = i-1; + key = &ctlr->keys.keys[ctlr->txkey]; + key->len = strlen(cb->f[1]); + if (key->len > WKeyLen) + key->len = WKeyLen; + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, cb->f[1], key->len); + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "txkey") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) + ctlr->txkey = i-1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "pm") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->pmena = 0; + else if(cistrcmp(cb->f[1], "on") == 0){ + ctlr->pmena = 1; + if(cb->nf == 3){ + i = atoi(cb->f[2]); + // check range here? what are the units? + ctlr->pmwait = i; + } + } + else + r = -1; + } + else + r = -2; + free(cb); + + return r; +} + +long +w_ctl(Ether* ether, void* buf, long n) +{ + Ctlr *ctlr; + + if((ctlr = ether->ctlr) == nil) + error(Enonexist); + if(ctlr->attached == 0) + error(Eshutdown); + + ilock(ctlr); + if(w_option(ctlr, buf, n)){ + iunlock(ctlr); + error(Ebadctl); + } + if(ctlr->txbusy) + w_txdone(ctlr, WTxErrEv); + w_enable(ether); + w_txstart(ether); + iunlock(ctlr); + + return n; +} + +void +w_transmit(Ether* ether) +{ + Ctlr* ctlr = ether->ctlr; + + if (ctlr == 0) + return; + + ilock(ctlr); + ctlr->ntxrq++; + w_txstart(ether); + iunlock(ctlr); +} + +void +w_promiscuous(void* arg, int on) +{ + Ether* ether = (Ether*)arg; + Ctlr* ctlr = ether->ctlr; + + if (ctlr == nil) + error("card not found"); + if (ctlr->attached == 0) + error("card not attached"); + ilock(ctlr); + ltv_outs(ctlr, WTyp_Prom, (on?1:0)); + iunlock(ctlr); +} + +void +w_interrupt(Ureg* ,void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if (ctlr == 0) + return; + ilock(ctlr); + ctlr->nints++; + w_intr(ether); + iunlock(ctlr); +} + +int +wavelanreset(Ether* ether, Ctlr *ctlr) +{ + Wltv ltv; + + w_intdis(ctlr); + if (w_cmd(ctlr,WCmdIni,0)){ + print("#l%d: init failed\n", ether->ctlrno); + return -1; + } + w_intdis(ctlr); + ltv_outs(ctlr, WTyp_Tick, 8); + + ctlr->chan = 0; + ctlr->ptype = WDfltPType; + ctlr->txkey = 0; + ctlr->createibss = 0; + ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; + ctlr->keys.type = WTyp_Keys; + if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) + ctlr->crypt = 1; + *ctlr->netname = *ctlr->wantname = 0; + strcpy(ctlr->nodename, "Plan 9 STA"); + + ctlr->netname[WNameLen-1] = 0; + ctlr->wantname[WNameLen-1] = 0; + ctlr->nodename[WNameLen-1] =0; + + ltv.type = WTyp_Mac; + ltv.len = 4; + if (w_inltv(ctlr, <v)){ + print("#l%d: unable to read mac addr\n", + ether->ctlrno); + return -1; + } + memmove(ether->ea, ltv.addr, Eaddrlen); + + if (ctlr->chan == 0) + ctlr->chan = ltv_ins(ctlr, WTyp_Chan); + ctlr->apdensity = WDfltApDens; + ctlr->rtsthres = WDfltRtsThres; + ctlr->txrate = WDfltTxRate; + ctlr->maxlen = WMaxLen; + ctlr->pmena = 0; + ctlr->pmwait = 100; + ctlr->signal = 1; + ctlr->noise = 1; + + // link to ether + ether->ctlr = ctlr; + ether->mbps = 10; + ether->attach = w_attach; + ether->interrupt = w_interrupt; + ether->transmit = w_transmit; + ether->ifstat = w_ifstat; + ether->ctl = w_ctl; + ether->promiscuous = w_promiscuous; + ether->multicast = w_multicast; + ether->arg = ether; + +// DEBUG("#l%d: irq %ld port %lx type %s", +// ether->ctlrno, ether->irq, ether->port, ether->type); +// DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", +// ether->ea[0], ether->ea[1], ether->ea[2], +// ether->ea[3], ether->ea[4], ether->ea[5]); + + return 0; +} -#include "../ip/ip.h" -extern void ilinit(Fs*); -extern void tcpinit(Fs*); -extern void udpinit(Fs*); -extern void rudpinit(Fs*); -extern void ipifcinit(Fs*); -extern void icmpinit(Fs*); -extern void greinit(Fs*); -extern void ipmuxinit(Fs*); -void (*ipprotoinit[])(Fs*) = { - ilinit, - tcpinit, - udpinit, - rudpinit, - ipifcinit, - icmpinit, - greinit, - ipmuxinit, +char* wavenames[] = { + "WaveLAN/IEEE", + "TrueMobile 1150", + "Instant Wireless ; Network PC CARD", nil, }; - int cpuserver = 0; -char* conffile = "wavelan"; -ulong kerndate = KERNDATE; diff --git a/pc/wavelan.h b/pc/wavelan.h new file mode 100644 index 0000000000000000000000000000000000000000..58a8069fac4e507482c7120ef2638f4bc804b0a4 --- /dev/null +++ b/pc/wavelan.h @@ -0,0 +1,304 @@ +#define DEBUG if(1){}else print + +#define SEEKEYS 0 + +// Lucent's Length-Type-Value records to talk to the wavelan. +// most operational parameters are read/set using this. +enum +{ + WTyp_Stats = 0xf100, + WTyp_Ptype = 0xfc00, + WTyp_Mac = 0xfc01, + WTyp_WantName = 0xfc02, + WTyp_Chan = 0xfc03, + WTyp_NetName = 0xfc04, + WTyp_ApDens = 0xfc06, + WTyp_MaxLen = 0xfc07, + WTyp_PM = 0xfc09, + WTyp_PMWait = 0xfc0c, + WTyp_NodeName = 0xfc0e, + WTyp_Crypt = 0xfc20, + WTyp_XClear = 0xfc22, + WTyp_CreateIBSS = 0xfc81, + WTyp_RtsThres = 0xfc83, + WTyp_TxRate = 0xfc84, + WTx1Mbps = 0x0, + WTx2Mbps = 0x1, + WTxAuto = 0x3, + WTyp_Prom = 0xfc85, + WTyp_Keys = 0xfcb0, + WTyp_TxKey = 0xfcb1, + WTyp_StationID = 0xfd20, + WTyp_CurName = 0xfd41, + WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station + WTyp_CurTxRate = 0xfd44, // Current TX rate + WTyp_HasCrypt = 0xfd4f, + WTyp_Tick = 0xfce0, +}; + +// Controller +enum +{ + WDfltIRQ = 3, // default irq + WDfltIOB = 0x180, // default IO base + + WIOLen = 0x40, // Hermes IO length + + WTmOut = 65536, // Cmd time out + + WPTypeManaged = 1, + WPTypeWDS = 2, + WPTypeAdHoc = 3, + WDfltPType = WPTypeManaged, + + WDfltApDens = 1, + WDfltRtsThres = 2347, // == disabled + WDfltTxRate = WTxAuto, // 2Mbps + + WMaxLen = 2304, + WNameLen = 32, + + WNKeys = 4, + WKeyLen = 14, + WMinKeyLen = 5, + + // Wavelan hermes registers + WR_Cmd = 0x00, + WCmdIni = 0x0000, + WCmdEna = 0x0001, + WCmdDis = 0x0002, + WCmdTx = 0x000b, + WCmdMalloc = 0x000a, + WCmdAskStats = 0x0011, + WCmdMsk = 0x003f, + WCmdAccRd = 0x0021, + WCmdReclaim = 0x0100, + WCmdAccWr = 0x0121, + WCmdBusy = 0x8000, + WR_Parm0 = 0x02, + WR_Parm1 = 0x04, + WR_Parm2 = 0x06, + WR_Sts = 0x08, + WR_InfoId = 0x10, + WR_Sel0 = 0x18, + WR_Sel1 = 0x1a, + WR_Off0 = 0x1c, + WR_Off1 = 0x1e, + WBusyOff = 0x8000, + WErrOff = 0x4000, + WResSts = 0x7f00, + WR_RXId = 0x20, + WR_Alloc = 0x22, + WR_EvSts = 0x30, + WR_IntEna = 0x32, + WCmdEv = 0x0010, + WRXEv = 0x0001, + WTXEv = 0x0002, + WTxErrEv = 0x0004, + WAllocEv = 0x0008, + WInfoEv = 0x0080, + WIDropEv = 0x2000, + WTickEv = 0x8000, + WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, + + WR_EvAck = 0x34, + WR_Data0 = 0x36, + WR_Data1 = 0x38, + + WR_PciCor = 0x26, + WR_PciHcr = 0x2E, + + // Frame stuff + + WF_Err = 0x0003, + WF_1042 = 0x2000, + WF_Tunnel = 0x4000, + WF_WMP = 0x6000, + + WF_Data = 0x0008, + + WSnapK1 = 0xaa, + WSnapK2 = 0x00, + WSnapCtlr = 0x03, + WSnap0 = (WSnapK1|(WSnapK1<<8)), + WSnap1 = (WSnapK2|(WSnapCtlr<<8)), + WSnapHdrLen = 6, + + WF_802_11_Off = 0x44, + WF_802_3_Off = 0x2e, + +}; + +typedef struct Ctlr Ctlr; +typedef struct Wltv Wltv; +typedef struct WFrame WFrame; +typedef struct Stats Stats; +typedef struct WStats WStats; +typedef struct WKey WKey; + +struct WStats +{ + ulong ntxuframes; // unicast frames + ulong ntxmframes; // multicast frames + ulong ntxfrags; // fragments + ulong ntxubytes; // unicast bytes + ulong ntxmbytes; // multicast bytes + ulong ntxdeferred; // deferred transmits + ulong ntxsretries; // single retries + ulong ntxmultiretries; // multiple retries + ulong ntxretrylimit; + ulong ntxdiscards; + ulong nrxuframes; // unicast frames + ulong nrxmframes; // multicast frames + ulong nrxfrags; // fragments + ulong nrxubytes; // unicast bytes + ulong nrxmbytes; // multicast bytes + ulong nrxfcserr; + ulong nrxdropnobuf; + ulong nrxdropnosa; + ulong nrxcantdecrypt; + ulong nrxmsgfrag; + ulong nrxmsgbadfrag; + ulong end; +}; + +struct WFrame +{ + ushort sts; + ushort rsvd0; + ushort rsvd1; + ushort qinfo; + ushort rsvd2; + ushort rsvd3; + ushort txctl; + ushort framectl; + ushort id; + uchar addr1[Eaddrlen]; + uchar addr2[Eaddrlen]; + uchar addr3[Eaddrlen]; + ushort seqctl; + uchar addr4[Eaddrlen]; + ushort dlen; + uchar dstaddr[Eaddrlen]; + uchar srcaddr[Eaddrlen]; + ushort len; + ushort dat[3]; + ushort type; +}; + +struct WKey +{ + ushort len; + char dat[WKeyLen]; +}; + +struct Wltv +{ + ushort len; + ushort type; + union + { + struct { + ushort val; + ushort pad; + }; + struct { + uchar addr[8]; + }; + struct { + ushort slen; + char s[WNameLen]; + }; + struct { + char name[WNameLen]; + }; + struct { + WKey keys[WNKeys]; + }; + }; +}; + +// What the driver thinks. Not what the card thinks. +struct Stats +{ + ulong nints; + ulong ndoubleint; + ulong nrx; + ulong ntx; + ulong ntxrq; + ulong nrxerr; + ulong ntxerr; + ulong nalloc; // allocation (reclaim) events + ulong ninfo; + ulong nidrop; + ulong nwatchdogs; // transmit time outs, actually + int ticks; + int tickintr; + int signal; + int noise; +}; + +struct Ctlr +{ + Lock; + Rendez timer; + + int attached; + int slot; + int iob; + int createibss; + int ptype; + int apdensity; + int rtsthres; + int txbusy; + int txrate; + int txdid; + int txmid; + int txtmout; + int maxlen; + int chan; + int pmena; + int pmwait; + + char netname[WNameLen]; + char wantname[WNameLen]; + char nodename[WNameLen]; + WFrame txf; + uchar txbuf[1536]; + + int hascrypt; // card has encryption + int crypt; // encryption off/on + int txkey; // transmit key + Wltv keys; // default keys + int xclear; // exclude clear packets off/on + + int ctlrno; + + ushort *mmb; + /* for PCI-based devices */ + Ctlr *next; + int active; + Pcidev *pcidev; + + Stats; + WStats; +}; + +extern char* wavenames[]; + +void csr_outs(Ctlr*, int, ushort); +ushort csr_ins(Ctlr*, int); +void w_intdis(Ctlr*); +int w_cmd(Ctlr *, ushort, ushort); +void ltv_outs(Ctlr*, int, ushort); +int ltv_ins(Ctlr*, int); +int w_option(Ctlr*, char*, long); +int w_inltv(Ctlr*, Wltv*); +void w_attach(Ether*); +void w_interrupt(Ureg*,void*); +void w_transmit(Ether*); +long w_ifstat(Ether*, void*, long, ulong); +long w_ctl(Ether*, void*, long); +void w_promiscuous(void*, int); +void w_multicast(void*, uchar*, int); +int wavelanreset(Ether*, Ctlr*);