/* Lucent Wavelan IEEE 802.11 pcmcia. There is almost no documentation for the card. the driver is done using both the freebsd and the linux driver 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. WEP is not tested (see BUGs too). */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" #define DEBUG if(1)print #define SEEKEYS 1 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; }; // 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_Tick = 0xfce0, WTyp_RtsThres= 0xfc83, WTyp_TxRate = 0xfc84, WTx1Mbps = 0x0, WTx2Mbps = 0x1, WTxAuto = 0x3, WTyp_Prom = 0xfc85, WTyp_Keys = 0xfcb0, WTyp_TxKey = 0xfcb1, WTyp_HasCrypt= 0xfd4f, }; // Controller enum { WDfltIRQ = 3, // default irq WDfltIOB = 0x100, // 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, WCmdMalloc = 0x000a, WCmdAskStats= 0x0011, WCmdMsk = 0x003f, WCmdAccRd = 0x0021, WCmdAccWr = 0x0121, WCmdTxFree = 0x000b|0x0100, 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, // 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, }; #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)) struct WKey { ushort len; char dat[WKeyLen]; }; struct Wltv { ushort len; ushort type; union { struct { ushort val; ushort pad; }; struct { uchar addr[8]; }; struct { char s[17*2]; }; struct { char name[WNameLen]; }; struct { WKey key[WNKeys]; }; }; }; // What the driver thinks. Not what the card thinks. struct Stats { ulong nints; ulong nrx; ulong ntx; ulong ntxrq; ulong nrxerr; ulong ntxerr; ulong nalloc; // allocation (reclaim) events ulong ninfo; ulong nidrop; ulong nwatchdogs; // transmits time outs, actually }; struct Ctlr { Lock; Rendez timer; int attached; int slot; int iob; 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 txlen; int keyid; // 0 if not using WEP int xclear; WKey key[WNKeys]; Stats; WStats; }; // 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; int 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,l->type,0,1)){ DEBUG("wavelan: seek failed\n"); return -1; } len = csr_ins(ctlr, WR_Data1); if (len > l->len) return -1; l->len = len; if ((code=csr_ins(ctlr, WR_Data1)) != l->type){ DEBUG("wavelan: type %x != code %x\n",l->type,code); return -1; } p = &l->val; len--; for (i=0; itype,0,1)) return; csr_outs(ctlr, WR_Data1, l->len); csr_outs(ctlr, WR_Data1, l->type); p = &l->val; len = l->len-1; for (i=0; itype); } static void ltv_outs(Ctlr* ctlr, int type, ushort val) { Wltv l; l.type = type; l.val = val; l.len = 2; w_outltv(ctlr, &l); } static ushort ltv_ins(Ctlr* ctlr, int type) { Wltv l; l.type = type; l.len = 2; l.val = 0; w_inltv(ctlr, &l); return l.val; } static void ltv_outstr(Ctlr* ctlr, int type, char *val) { Wltv l; int len; len = (strlen(val)+1)&~1; memset(&l,0,sizeof(l)); l.type = type; l.len = (len/2)+2; l.val = len; // l.s[0] and l.s[1] strncpy(l.s+2,val,strlen(val)); w_outltv(ctlr, &l); } static char Unkname[] = "who knows"; static char Nilname[] = "card does not tell"; static char* ltv_inname(Ctlr* ctlr, int type) { static Wltv l; memset(&l,0,sizeof(l)); l.type = type; l.len = WNameLen/2+2; if (w_inltv(ctlr, &l)) return Unkname; if (l.name[2] == 0) return Nilname; return l.name+2; } static char* ltv_inkey(Ctlr* ctlr, int id) { static char k[WKeyLen+1]; Wltv l; int len; int i; id--; memset(&l, 0, sizeof(l)); l.type = WTyp_Keys; l.len = sizeof(l.key)/2 + 2; // BUG? if (w_inltv(ctlr, &l)) return Unkname; if (id <0 || id >=WNKeys){ DEBUG("wavelan: ltv_inkey: bad key id\n"); return Unkname; } for (i=0 ; i WKeyLen) len = WKeyLen; memset(k, 0, sizeof(k)); memmove(k, l.key[id].dat, len); k[WKeyLen] = 0; return k; } static int w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) { ushort *p = (ushort*)buf; int i, n; n=0; if (w_seek(ctlr, type, off, 1) == 0){ len /= 2; for (i = 0; i < len; i++){ p[i] = csr_ins(ctlr, WR_Data1); n += 2; } } else DEBUG("wavelan: w_read: seek failed"); return n; } static int w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) { ushort *p = (ushort*)buf; ulong l = len / 2; int i,tries; for (tries=0; tries < WTmOut; tries++){ if (w_seek(ctlr, type, off, 0)){ DEBUG("wavelan: w_write: seek failed\n"); return 0; } for (i = 0; i < l; i++) csr_outs(ctlr, WR_Data0, p[i]); 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; ushort wep; 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_RtsThres, ctlr->rtsthres); ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); ltv_outs(ctlr, WTyp_PM, ctlr->pmena); ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); 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); l.type = WTyp_Mac; l.len = 4; memmove(l.addr, ether->ea, Eaddrlen); w_outltv(ctlr, &l); ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); wep = ltv_ins(ctlr, WTyp_HasCrypt); iprint("wep %d\n", wep); if (wep) if (ctlr->keyid == 0) ltv_outs(ctlr, WTyp_Crypt, 0); else { // BUG? // I think it's ok, but don't know if // the card needs a WEPing access point // just to admit the keys. ltv_outs(ctlr, WTyp_TxKey, ctlr->keyid); memset(&l, 0, sizeof(l)); l.len = sizeof(ctlr->key[0])*WNKeys/2 + 1; l.type= WTyp_Keys; memmove(l.key, &ctlr->key[0], sizeof(l.key[0])*WNKeys); w_outltv(ctlr, &l); ltv_outs(ctlr, WTyp_Crypt, wep); 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; ushort sp; WFrame f; Block* bp=0; ulong l; Etherpkt* ep; sp = csr_ins(ctlr, WR_RXId); l = w_read(ctlr, sp, 0, &f, sizeof(f)); if (l == 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: l = f.dlen + WSnapHdrLen; bp = iallocb(ETHERHDRSIZE + l + 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, l+2) == 0){ DEBUG("wavelan: read 802.11 error\n"); goto rxerror; } bp->wp += l+2; bp = trimblock(bp, 0, f.dlen+ETHERHDRSIZE); break; default: l = ETHERHDRSIZE + f.dlen + 2; bp = iallocb(l); if (!bp) goto rxerror; if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, l) == 0){ DEBUG("wavelan: read 800.3 error\n"); goto rxerror; } bp->wp += l; } ctlr->nrx++; etheriq(ether,bp,1); return; rxerror: freeb(bp); ctlr->nrxerr++; } static int w_txstart(Ether* ether, int again) { Etherpkt* ep; Ctlr* ctlr = (Ctlr*) ether->ctlr; Block* bp; int txid; if (ctlr == 0 || ctlr->attached == 0 ) return -1; if (ctlr->txbusy && again==0) return -1; txid = ctlr->txdid; if (again){ bp = 0; // a watchdog reenabled the card. goto retry; // must retry a previously failed tx. } bp = qget(ether->oq); if (bp == 0) return 0; ep = (Etherpkt*) bp->rp; ctlr->txbusy = 1; // BUG: only IP/ARP/RARP seem to be ok for 802.3 // Other packets should be just copied to the board. // The driver is not doing so, though. // Besides, the Block should be used instead of txbuf, // to save a memory copy. memset(ctlr->txbuf,0,sizeof(ctlr->txbuf)); memset(&ctlr->txf,0,sizeof(ctlr->txf)); ctlr->txf.framectl = WF_Data; memmove(ctlr->txf.addr1, ep->d, Eaddrlen); memmove(ctlr->txf.addr2, ep->s, Eaddrlen); memmove(ctlr->txf.dstaddr, ep->d, Eaddrlen); memmove(ctlr->txf.srcaddr, ep->s, Eaddrlen); memmove(&ctlr->txf.type,ep->type,2); ctlr->txlen = BLEN(bp); ctlr->txf.dlen = ctlr->txlen - WSnapHdrLen; hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); hnputs((uchar*)&ctlr->txf.len, ctlr->txlen - WSnapHdrLen); if (ctlr->txlen - ETHERHDRSIZE > 1536){ print("wavelan: txbuf overflow"); freeb(bp); return -1; } memmove(ctlr->txbuf, bp->rp+sizeof(ETHERHDRSIZE)+10, ctlr->txlen - ETHERHDRSIZE ); retry: w_write(ctlr, txid, 0, &ctlr->txf, sizeof(ctlr->txf)); w_write(ctlr, txid, WF_802_11_Off, ctlr->txbuf, ctlr->txlen - ETHERHDRSIZE + 2); if (w_cmd(ctlr, WCmdTxFree, txid)){ DEBUG("wavelan: transmit failed\n"); ctlr->txbusy=0; // added ctlr->ntxerr++; freeb(bp); return -1; } ctlr->txtmout = 2; freeb(bp); return 0; } static void w_txdone(Ctlr* ctlr, int sts) { int b = ctlr->txbusy; ctlr->txbusy = 0; ctlr->txtmout= 0; if (sts & WTxErrEv){ ctlr->ntxerr++; if (sts&1) // it was a watchdog, stay busy to retry. ctlr->txbusy = b; } else ctlr->ntx++; } static int w_stats(Ctlr* ctlr) { int sp,i; ushort rc; Wltv l; ulong* p = (ulong*)&ctlr->WStats; ulong* pend= (ulong*)&ctlr->end; sp = csr_ins(ctlr, WR_InfoId); l.type = l.len = 0; w_read(ctlr, sp, 0, &l, 4); if (l.type == WTyp_Stats){ l.len--; for (i = 0; i < l.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, i; Ctlr* ctlr = (Ctlr*) ether->ctlr; if (ctlr->attached == 0){ csr_ack(ctlr, 0xffff); csr_outs(ctlr, WR_IntEna, 0); return; } for(i=0; i<7; i++){ 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,0); } } // 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; int tick=0; for(;;){ tsleep(&ctlr->timer, return0, 0, 50); ctlr = (Ctlr*)ether->ctlr; if (ctlr == 0) break; if (ctlr->attached == 0) continue; tick++; 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) w_intr(ether); if (tick % 10 == 0) { if (ctlr->txtmout && --ctlr->txtmout == 0){ ctlr->nwatchdogs++; w_txdone(ctlr, WTxErrEv|1); // 1: keep it busy if (w_enable(ether)){ DEBUG("wavelan: wdog enable failed\n"); } if (ctlr->txbusy) w_txstart(ether,1); } if (tick % 120 == 0) if (ctlr->txbusy == 0) w_cmd(ctlr, WCmdAskStats, WTyp_Stats); } iunlock(&ctlr->Lock); } pexit("terminated",0); } static void* emalloc(ulong size) { void *r=malloc(size); if (!r) error(Enomem); memset(r,0,size); return r; } static void multicast(void*, uchar*, int) { // BUG: to be added. } static void attach(Ether* ether) { Ctlr* ctlr; char name[NAMELEN]; int rc; if (ether->ctlr == 0) return; snprint(name, NAMELEN, "#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* p; int l, i; int txid; char* k; if (n == 0 || ctlr == 0){ return 0; } p = malloc(READSTR); l = 0; 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); k = ((ctlr->attached) ? "attached" : "not attached"); PRINTSTAT("Card %s", k); k = ((ctlr->txbusy)? ", txbusy" : ""); PRINTSTAT("%s\n", k); if (ctlr->keyid){ PRINTSTR("Keys: "); for (i = 0; i < WNKeys; i++) if (ctlr->key[i].len == 0) PRINTSTR("none "); else { if (SEEKEYS == 0) PRINTSTR("set "); else { PRINTSTAT("%s ", ctlr->key[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)); PRINTSTAT("Port type: %d\n", ltv_ins(ctlr, WTyp_Ptype)); PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); 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)); PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); 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"); i = ltv_ins(ctlr, WTyp_XClear); k = ((i) ? "excluded" : "included"); PRINTSTAT("Clear packets: %s\n", k); txid = ltv_ins(ctlr, WTyp_TxKey); PRINTSTAT("Transmit key id: %d\n", txid); if (txid >= 1 && txid <= WNKeys){ k = ltv_inkey(ctlr, txid); if (SEEKEYS && k != nil) PRINTSTAT("Transmit key: %s\n", k); } } } 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 #define min(a,b) (((a)<(b))?(a):(b)) static long ctl(Ether* ether, void* buf, long n) { int i; Ctlr *ctlr; Cmdbuf *cb; char *nessid; WKey *kp; char k[64]; char *kn= &k[3]; int key; if((ctlr = ether->ctlr) == nil) error(Enonexist); if(ctlr->attached == 0) error(Eshutdown); cb = parsecmd(buf, n); if(cb->nf < 2) error(Ebadctl); ilock(&ctlr->Lock); if(waserror()){ iunlock(&ctlr->Lock); free(cb); nexterror(); } if(strcmp(cb->f[0], "essid") == 0){ if (strcmp(cb->f[1],"default") == 0) nessid = ""; else nessid = cb->f[1]; if (ctlr->ptype == 3){ memset(ctlr->netname, 0, sizeof(ctlr->netname)); strncpy(ctlr->netname, nessid, WNameLen); } else { memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); strncpy(ctlr->wantname, nessid, WNameLen); } } else if(strcmp(cb->f[0], "station_name") == 0){ memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); strncpy(ctlr->nodename, cb->f[1], WNameLen); } else if(strcmp(cb->f[0], "channel") == 0){ i = atoi(cb->f[1]); if(i < 1 || i > 16 ) error("channel not in [1-16]\n"); ctlr->chan = i; } else if(strcmp(cb->f[0], "ptype") == 0){ i = atoi(cb->f[1]); if(i < 1 || i > 3 ) error("port type not in [1-3]"); ctlr->ptype = i; } else if(strncmp(cb->f[0], "key", 3) == 0){ strncpy(k,cb->f[0],64); if (strcmp(cb->f[1], "off") == 0) ctlr->keyid = 0; else if (strcmp(cb->f[1], "exclude") == 0){ ctlr->xclear = 1; } else if (strcmp(cb->f[1], "include") == 0){ ctlr->xclear = 0; } else { key = atoi(kn); if (key < 1 || key > WNKeys) error("key not in [1-4]"); ctlr->keyid = key; kp = &ctlr->key[key-1]; kp->len = strlen(cb->f[1]); if (kp->len > WKeyLen) kp->len = WKeyLen; memset(kp->dat, 0, sizeof(kp->dat)); strncpy(kp->dat, cb->f[1], kp->len); if (kp->len > WMinKeyLen) kp->len = WKeyLen; else if (kp->len > 0) kp->len = WMinKeyLen; } } else error(Ebadctl); if(ctlr->txbusy) w_txdone(ctlr, WTxErrEv|1); // retry tx later. w_enable(ether); iunlock(&ctlr->Lock); poperror(); free(cb); return n; } static void transmit(Ether* ether) { Ctlr* ctlr = ether->ctlr; if (ctlr == 0) return; ilock(&ctlr->Lock); ctlr->ntxrq++; w_txstart(ether,0); 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 void setopt(Ctlr* ctlr, char* opt, int no) { int i; char k[64]; char *kn = &k[3]; WKey *kp; char *ke; int key; if (strncmp(opt,"essid=",6) == 0){ if (ctlr->ptype == 3) strncpy(ctlr->netname,opt+6,WNameLen); else strncpy(ctlr->wantname,opt+6,WNameLen); iprint("essid %d %s\n", ctlr->ptype, opt+6); } else if (strncmp(opt,"station_name=",13) == 0){ // BUG? // In my kernel, it seems that the max length of // opt is 16, and I get "na" as node name // when I say station_name=nautilus. strncpy(ctlr->nodename, opt+13,WNameLen); iprint("nodename %s\n", ctlr->nodename); } else if (strncmp(opt, "channel=",8) == 0){ i = atoi(opt+8); if(i < 1 || i > 16 ) print("#l%d: channel (%d) not in [1-16]\n",no,i); else ctlr->chan = i; iprint("chan %d\n", i); } else if (strncmp(opt, "ptype=",6) == 0){ i = atoi(opt+6); if(i < 1 || i > 3 ) print("#l%d: port type (%d) not in [1-3]\n",no,i); else ctlr->ptype = i; iprint("ptype %d\n", i); } else if (strncmp(opt, "key", 3) == 0){ if (opt[3] == 0 || opt[4] != '='){ print("#l%d: key option is not keyX=xxxx\n", no); return; } strncpy(k,opt,64); key = atoi(kn); if (key < 1 || key > WNKeys){ print("#l%d: key number (%d) not in [1-%d]\n", no, key, WNKeys); return; } ctlr->keyid = key; kp = &ctlr->key[key-1]; ke = opt+5; while (*ke && *ke != ' ' && *ke != '\n' && *ke != '\t') ke++; kp->len = ke - (opt+5); if (kp->len > WKeyLen) kp->len = WKeyLen; memset(kp->dat, 0, sizeof(kp->dat)); strncpy(kp->dat, opt+5, kp->len); if (kp->len > WMinKeyLen) kp->len = WKeyLen; else if (kp->len > 0) kp->len = WMinKeyLen; iprint("key %d <%s> len %d\n", key, kp->dat, kp->len); } } static int reset(Ether* ether) { Ctlr* ctlr; Wltv ltv; int i; if (ether->ctlr){ print("#l%d: only one card supported\n", ether->ctlrno); return -1; } ether->arg = ctlr = (Ctlr*)emalloc(sizeof(Ctlr)); ilock(&ctlr->Lock); if (ether->port==0) ether->port=WDfltIOB; ctlr->iob = ether->port; if (ether->irq==0) ether->irq=WDfltIRQ; if ((ctlr->slot = pcmspecial("WaveLAN/IEEE", ether))<0){ DEBUG("no wavelan found\n"); goto abort; } DEBUG("#l%d: port=0x%lx irq=%ld\n", ether->ctlrno, ether->port, ether->irq); ctlr->chan = 0; ctlr->ptype= WDfltPType; ctlr->keyid= 0; *ctlr->netname = *ctlr->wantname = *ctlr->nodename = 0; for (i=0; i < ether->nopt; i++) setopt(ctlr, ether->opt[i], ether->ctlrno); ctlr->netname[WNameLen-1] = 0; ctlr->wantname[WNameLen-1] = 0; ctlr->nodename[WNameLen-1] =0; if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ print("#l%d: port 0x%lx in use\n", ether->ctlrno, ether->port); goto abort; } 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); 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; } memmove(ether->ea, ltv.addr, Eaddrlen); DEBUG("#l%d: %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", ether->ctlrno, ether->ea[0], ether->ea[1], ether->ea[2], ether->ea[3], ether->ea[4], ether->ea[5]); 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; // 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]); iunlock(&ctlr->Lock); return 0; abort: iunlock(&ctlr->Lock); free(ctlr); ether->ctlr = nil; return -1; } void etherwavelanlink(void) { addethercard("wavelan", reset); }