📄 wavelan.c
字号:
/* 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"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h"#include "etherif.h"#include "wavelan.h"enum{ MSperTick= 50, /* ms between ticks of kproc */};/* * 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]. */voidcsr_outs(Ctlr *ctlr, int reg, ushort arg){ if(ctlr->mmb) ctlr->mmb[reg] = arg; else outs(ctlr->iob+reg, arg);}ushortcsr_ins(Ctlr *ctlr, int reg){ if(ctlr->mmb) return ctlr->mmb[reg]; else return ins(ctlr->iob+reg);}static voidcsr_ack(Ctlr *ctlr, int ev){ csr_outs(ctlr, WR_EvAck, ev);}static voidcsr_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 voidcsr_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.voidw_intdis(Ctlr* ctlr){ csr_outs(ctlr, WR_IntEna, 0); csr_ack(ctlr, 0xffff);}static voidw_intena(Ctlr* ctlr){ csr_outs(ctlr, WR_IntEna, WEvs);}intw_cmd(Ctlr *ctlr, ushort cmd, ushort arg){ int i, rc; for(i=0; i<WTmOut; i++) if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0) break; if(i==WTmOut){ print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd)); return -1; } csr_outs(ctlr, WR_Parm0, arg); csr_outs(ctlr, WR_Cmd, cmd); for(i=0; i<WTmOut; i++) if(csr_ins(ctlr, WR_EvSts)&WCmdEv) break; if(i==WTmOut){ /* * WCmdIni can take a really long time. */ enum { IniTmOut = 2000 }; for(i=0; i<IniTmOut; i++){ if(csr_ins(ctlr, WR_EvSts)&WCmdEv) break; microdelay(100); } if(i < IniTmOut) if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, 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 intw_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; i<WTmOut; i++){ rc = csr_ins(ctlr, off[chan]); if((rc & (WBusyOff|WErrOff)) == 0) return 0; } return -1;}intw_inltv(Ctlr* ctlr, Wltv* ltv){ int len; ushort code; if(w_cmd(ctlr, WCmdAccRd, ltv->type)){ 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 voidw_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);}voidltv_outs(Ctlr* ctlr, int type, ushort val){ Wltv ltv; ltv.len = 2; ltv.type = type; ltv.val = val; w_outltv(ctlr, <v);}intltv_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 voidltv_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 intw_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 intw_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 intw_alloc(Ctlr* ctlr, int len){ int rc; int i,j; if(w_cmd(ctlr, WCmdMalloc, len)==0) for (i = 0; i<WTmOut; i++) if(csr_ins(ctlr, WR_EvSts) & WAllocEv){ csr_ack(ctlr, WAllocEv); rc=csr_ins(ctlr, WR_Alloc); if(w_seek(ctlr, rc, 0, 0)) return -1; len = len/2; for (j=0; j<len; j++) csr_outs(ctlr, WR_Data0, 0); return rc; } return -1;}static intw_enable(Ether* ether){ Wltv ltv; Ctlr* ctlr = (Ctlr*) ether->ctlr; 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 && ctlr->crypt){ 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 voidw_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 voidw_txstart(Ether* ether){ Etherpkt *pkt; Ctlr *ctlr; Block *bp; int len, off; if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || 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 voidw_txdone(Ctlr* ctlr, int sts){ ctlr->txbusy = 0; ctlr->txtmout = 0; if(sts & WTxErrEv) ctlr->ntxerr++; else ctlr->ntx++;}/* save the stats info in the ctlr struct */static voidw_stats(Ctlr* ctlr, int len){ int i, rc; ulong* p = (ulong*)&ctlr->WStats; ulong* pend = (ulong*)&ctlr->end; for (i = 0; i < len && p < pend; i++){ rc = csr_ins(ctlr, WR_Data1); if(rc > 0xf000) rc = ~rc & 0xffff; p[i] += rc; }}/* send the base station scan info to any readers */static voidw_scaninfo(Ether* ether, Ctlr *ctlr, int len){ int i, j; Netfile **ep, *f, **fp; Block *bp; WScan *wsp; ushort *scanbuf; scanbuf = malloc(len*2); if(scanbuf == nil) return; for (i = 0; i < len ; i++) scanbuf[i] = csr_ins(ctlr, WR_Data1); /* calculate number of samples */ len /= 25; if(len == 0) goto out; i = ether->scan; ep = ðer->f[Ntypes]; for(fp = ether->f; fp < ep && i > 0; fp++){ f = *fp; if(f == nil || f->scan == 0) continue; bp = iallocb(100*len); if(bp == nil) break; for(j = 0; j < len; j++){ wsp = (WScan*)(&scanbuf[j*25]); if(wsp->ssid_len > 32) wsp->ssid_len = 32; bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, "ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n", wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal, wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":""); } qpass(f->in, bp); i--; }out: free(scanbuf);}static intw_info(Ether *ether, Ctlr* ctlr){ int sp; Wltv ltv; sp = csr_ins(ctlr, WR_InfoId); ltv.len = ltv.type = 0; w_read(ctlr, sp, 0, <v, 4); ltv.len--; switch(ltv.type){ case WTyp_Stats: w_stats(ctlr, ltv.len); return 0; case WTyp_Scan: w_scaninfo(ether, ctlr, ltv.len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -