13ff48bf5SDavid du Colombier /*
23ff48bf5SDavid du Colombier Lucent Wavelan IEEE 802.11 pcmcia.
33ff48bf5SDavid du Colombier There is almost no documentation for the card.
43ff48bf5SDavid du Colombier the driver is done using both the FreeBSD, Linux and
53ff48bf5SDavid du Colombier original Plan 9 drivers as `documentation'.
63ff48bf5SDavid du Colombier
73ff48bf5SDavid du Colombier Has been used with the card plugged in during all up time.
83ff48bf5SDavid du Colombier no cards removals/insertions yet.
93ff48bf5SDavid du Colombier
103ff48bf5SDavid du Colombier For known BUGS see the comments below. Besides,
113ff48bf5SDavid du Colombier the driver keeps interrupts disabled for just too
123ff48bf5SDavid du Colombier long. When it gets robust, locks should be revisited.
133ff48bf5SDavid du Colombier
143ff48bf5SDavid du Colombier BUGS: check endian, alignment and mem/io issues;
153ff48bf5SDavid du Colombier receive watchdog interrupts.
163ff48bf5SDavid du Colombier TODO: automatic power management;
175fb521f9SDavid du Colombier multicast filtering;
183ff48bf5SDavid du Colombier improve locking.
193ff48bf5SDavid du Colombier */
203ff48bf5SDavid du Colombier #include "u.h"
213ff48bf5SDavid du Colombier #include "../port/lib.h"
223ff48bf5SDavid du Colombier #include "mem.h"
233ff48bf5SDavid du Colombier #include "dat.h"
243ff48bf5SDavid du Colombier #include "fns.h"
253ff48bf5SDavid du Colombier #include "io.h"
263ff48bf5SDavid du Colombier #include "../port/error.h"
273ff48bf5SDavid du Colombier #include "../port/netif.h"
283ff48bf5SDavid du Colombier #include "etherif.h"
293ff48bf5SDavid du Colombier #include "wavelan.h"
303ff48bf5SDavid du Colombier
3115763c87SDavid du Colombier enum
3215763c87SDavid du Colombier {
3315763c87SDavid du Colombier MSperTick= 50, /* ms between ticks of kproc */
3415763c87SDavid du Colombier };
3515763c87SDavid du Colombier
363ff48bf5SDavid du Colombier /*
373ff48bf5SDavid du Colombier * When we're using a PCI device and memory-mapped I/O,
383ff48bf5SDavid du Colombier * the registers are spaced out as though each takes 32 bits,
393ff48bf5SDavid du Colombier * even though they are only 16-bit registers. Thus,
403ff48bf5SDavid du Colombier * ctlr->mmb[reg] is the right way to access register reg,
413ff48bf5SDavid du Colombier * even though a priori you'd expect to use ctlr->mmb[reg/2].
423ff48bf5SDavid du Colombier */
433ff48bf5SDavid du Colombier void
csr_outs(Ctlr * ctlr,int reg,ushort arg)443ff48bf5SDavid du Colombier csr_outs(Ctlr *ctlr, int reg, ushort arg)
453ff48bf5SDavid du Colombier {
463ff48bf5SDavid du Colombier if(ctlr->mmb)
473ff48bf5SDavid du Colombier ctlr->mmb[reg] = arg;
483ff48bf5SDavid du Colombier else
493ff48bf5SDavid du Colombier outs(ctlr->iob+reg, arg);
503ff48bf5SDavid du Colombier }
513ff48bf5SDavid du Colombier
523ff48bf5SDavid du Colombier ushort
csr_ins(Ctlr * ctlr,int reg)533ff48bf5SDavid du Colombier csr_ins(Ctlr *ctlr, int reg)
543ff48bf5SDavid du Colombier {
553ff48bf5SDavid du Colombier if(ctlr->mmb)
563ff48bf5SDavid du Colombier return ctlr->mmb[reg];
573ff48bf5SDavid du Colombier else
583ff48bf5SDavid du Colombier return ins(ctlr->iob+reg);
593ff48bf5SDavid du Colombier }
603ff48bf5SDavid du Colombier
613ff48bf5SDavid du Colombier static void
csr_ack(Ctlr * ctlr,int ev)623ff48bf5SDavid du Colombier csr_ack(Ctlr *ctlr, int ev)
633ff48bf5SDavid du Colombier {
643ff48bf5SDavid du Colombier csr_outs(ctlr, WR_EvAck, ev);
653ff48bf5SDavid du Colombier }
663ff48bf5SDavid du Colombier
673ff48bf5SDavid du Colombier static void
csr_inss(Ctlr * ctlr,int reg,void * dat,int ndat)683ff48bf5SDavid du Colombier csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
693ff48bf5SDavid du Colombier {
703ff48bf5SDavid du Colombier ushort *rp, *wp;
713ff48bf5SDavid du Colombier
723ff48bf5SDavid du Colombier if(ctlr->mmb){
733ff48bf5SDavid du Colombier rp = &ctlr->mmb[reg];
743ff48bf5SDavid du Colombier wp = dat;
753ff48bf5SDavid du Colombier while(ndat-- > 0)
763ff48bf5SDavid du Colombier *wp++ = *rp;
773ff48bf5SDavid du Colombier }else
783ff48bf5SDavid du Colombier inss(ctlr->iob+reg, dat, ndat);
793ff48bf5SDavid du Colombier }
803ff48bf5SDavid du Colombier
813ff48bf5SDavid du Colombier static void
csr_outss(Ctlr * ctlr,int reg,void * dat,int ndat)823ff48bf5SDavid du Colombier csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
833ff48bf5SDavid du Colombier {
843ff48bf5SDavid du Colombier ushort *rp, *wp;
853ff48bf5SDavid du Colombier
863ff48bf5SDavid du Colombier if(ctlr->mmb){
873ff48bf5SDavid du Colombier rp = dat;
883ff48bf5SDavid du Colombier wp = &ctlr->mmb[reg];
893ff48bf5SDavid du Colombier while(ndat-- > 0)
903ff48bf5SDavid du Colombier *wp = *rp++;
913ff48bf5SDavid du Colombier }else
923ff48bf5SDavid du Colombier outss(ctlr->iob+reg, dat, ndat);
933ff48bf5SDavid du Colombier }
943ff48bf5SDavid du Colombier
953ff48bf5SDavid du Colombier // w_... routines do not ilock the Ctlr and should
963ff48bf5SDavid du Colombier // be called locked.
973ff48bf5SDavid du Colombier
983ff48bf5SDavid du Colombier void
w_intdis(Ctlr * ctlr)993ff48bf5SDavid du Colombier w_intdis(Ctlr* ctlr)
1003ff48bf5SDavid du Colombier {
1013ff48bf5SDavid du Colombier csr_outs(ctlr, WR_IntEna, 0);
1023ff48bf5SDavid du Colombier csr_ack(ctlr, 0xffff);
1033ff48bf5SDavid du Colombier }
1043ff48bf5SDavid du Colombier
1053ff48bf5SDavid du Colombier static void
w_intena(Ctlr * ctlr)1063ff48bf5SDavid du Colombier w_intena(Ctlr* ctlr)
1073ff48bf5SDavid du Colombier {
1083ff48bf5SDavid du Colombier csr_outs(ctlr, WR_IntEna, WEvs);
1093ff48bf5SDavid du Colombier }
1103ff48bf5SDavid du Colombier
1113ff48bf5SDavid du Colombier int
w_cmd(Ctlr * ctlr,ushort cmd,ushort arg)1123ff48bf5SDavid du Colombier w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
1133ff48bf5SDavid du Colombier {
1143ff48bf5SDavid du Colombier int i, rc;
1153ff48bf5SDavid du Colombier
1163ff48bf5SDavid du Colombier for(i=0; i<WTmOut; i++)
1173ff48bf5SDavid du Colombier if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
1183ff48bf5SDavid du Colombier break;
1193ff48bf5SDavid du Colombier if(i==WTmOut){
1203ff48bf5SDavid du Colombier print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
1213ff48bf5SDavid du Colombier return -1;
1223ff48bf5SDavid du Colombier }
1233ff48bf5SDavid du Colombier
1243ff48bf5SDavid du Colombier csr_outs(ctlr, WR_Parm0, arg);
1253ff48bf5SDavid du Colombier csr_outs(ctlr, WR_Cmd, cmd);
1263ff48bf5SDavid du Colombier
1273ff48bf5SDavid du Colombier for(i=0; i<WTmOut; i++)
1283ff48bf5SDavid du Colombier if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
1293ff48bf5SDavid du Colombier break;
1303ff48bf5SDavid du Colombier if(i==WTmOut){
1313ff48bf5SDavid du Colombier /*
1323ff48bf5SDavid du Colombier * WCmdIni can take a really long time.
1333ff48bf5SDavid du Colombier */
1343ff48bf5SDavid du Colombier enum { IniTmOut = 2000 };
1353ff48bf5SDavid du Colombier for(i=0; i<IniTmOut; i++){
1363ff48bf5SDavid du Colombier if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
1373ff48bf5SDavid du Colombier break;
1383ff48bf5SDavid du Colombier microdelay(100);
1393ff48bf5SDavid du Colombier }
1403ff48bf5SDavid du Colombier if(i < IniTmOut)
1413ff48bf5SDavid du Colombier if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
1423ff48bf5SDavid du Colombier if(i == IniTmOut){
1433ff48bf5SDavid du Colombier print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
1443ff48bf5SDavid du Colombier return -1;
1453ff48bf5SDavid du Colombier }
1463ff48bf5SDavid du Colombier }
1473ff48bf5SDavid du Colombier rc = csr_ins(ctlr, WR_Sts);
1483ff48bf5SDavid du Colombier csr_ack(ctlr, WCmdEv);
1493ff48bf5SDavid du Colombier
1503ff48bf5SDavid du Colombier if((rc&WCmdMsk) != (cmd&WCmdMsk)){
1513ff48bf5SDavid du Colombier print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
1523ff48bf5SDavid du Colombier return -1;
1533ff48bf5SDavid du Colombier }
1543ff48bf5SDavid du Colombier if(rc&WResSts){
1553ff48bf5SDavid du Colombier /*
1563ff48bf5SDavid du Colombier * Don't print; this happens on every WCmdAccWr for some reason.
1573ff48bf5SDavid du Colombier */
1583ff48bf5SDavid du Colombier if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
1593ff48bf5SDavid du Colombier return -1;
1603ff48bf5SDavid du Colombier }
1613ff48bf5SDavid du Colombier return 0;
1623ff48bf5SDavid du Colombier }
1633ff48bf5SDavid du Colombier
1643ff48bf5SDavid du Colombier static int
w_seek(Ctlr * ctlr,ushort id,ushort offset,int chan)1653ff48bf5SDavid du Colombier w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
1663ff48bf5SDavid du Colombier {
1673ff48bf5SDavid du Colombier int i, rc;
1683ff48bf5SDavid du Colombier static ushort sel[] = { WR_Sel0, WR_Sel1 };
1693ff48bf5SDavid du Colombier static ushort off[] = { WR_Off0, WR_Off1 };
1703ff48bf5SDavid du Colombier
1713ff48bf5SDavid du Colombier if(chan != 0 && chan != 1)
1723ff48bf5SDavid du Colombier panic("wavelan: bad chan\n");
1733ff48bf5SDavid du Colombier csr_outs(ctlr, sel[chan], id);
1743ff48bf5SDavid du Colombier csr_outs(ctlr, off[chan], offset);
1753ff48bf5SDavid du Colombier for (i=0; i<WTmOut; i++){
1763ff48bf5SDavid du Colombier rc = csr_ins(ctlr, off[chan]);
1773ff48bf5SDavid du Colombier if((rc & (WBusyOff|WErrOff)) == 0)
1783ff48bf5SDavid du Colombier return 0;
1793ff48bf5SDavid du Colombier }
1803ff48bf5SDavid du Colombier return -1;
1813ff48bf5SDavid du Colombier }
1823ff48bf5SDavid du Colombier
1833ff48bf5SDavid du Colombier int
w_inltv(Ctlr * ctlr,Wltv * ltv)1843ff48bf5SDavid du Colombier w_inltv(Ctlr* ctlr, Wltv* ltv)
1853ff48bf5SDavid du Colombier {
1863ff48bf5SDavid du Colombier int len;
1873ff48bf5SDavid du Colombier ushort code;
1883ff48bf5SDavid du Colombier
1893ff48bf5SDavid du Colombier if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
1903ff48bf5SDavid du Colombier DEBUG("wavelan: access read failed\n");
1913ff48bf5SDavid du Colombier return -1;
1923ff48bf5SDavid du Colombier }
1933ff48bf5SDavid du Colombier if(w_seek(ctlr,ltv->type,0,1)){
1943ff48bf5SDavid du Colombier DEBUG("wavelan: seek failed\n");
1953ff48bf5SDavid du Colombier return -1;
1963ff48bf5SDavid du Colombier }
1973ff48bf5SDavid du Colombier len = csr_ins(ctlr, WR_Data1);
1983ff48bf5SDavid du Colombier if(len > ltv->len)
1993ff48bf5SDavid du Colombier return -1;
2003ff48bf5SDavid du Colombier ltv->len = len;
2013ff48bf5SDavid du Colombier if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
2023ff48bf5SDavid du Colombier USED(code);
2033ff48bf5SDavid du Colombier DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
2043ff48bf5SDavid du Colombier return -1;
2053ff48bf5SDavid du Colombier }
2063ff48bf5SDavid du Colombier if(ltv->len > 0)
2073ff48bf5SDavid du Colombier csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1);
2083ff48bf5SDavid du Colombier
2093ff48bf5SDavid du Colombier return 0;
2103ff48bf5SDavid du Colombier }
2113ff48bf5SDavid du Colombier
2123ff48bf5SDavid du Colombier static void
w_outltv(Ctlr * ctlr,Wltv * ltv)2133ff48bf5SDavid du Colombier w_outltv(Ctlr* ctlr, Wltv* ltv)
2143ff48bf5SDavid du Colombier {
2153ff48bf5SDavid du Colombier if(w_seek(ctlr,ltv->type, 0, 1))
2163ff48bf5SDavid du Colombier return;
2173ff48bf5SDavid du Colombier csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
2183ff48bf5SDavid du Colombier w_cmd(ctlr, WCmdAccWr, ltv->type);
2193ff48bf5SDavid du Colombier }
2203ff48bf5SDavid du Colombier
2213ff48bf5SDavid du Colombier void
ltv_outs(Ctlr * ctlr,int type,ushort val)2223ff48bf5SDavid du Colombier ltv_outs(Ctlr* ctlr, int type, ushort val)
2233ff48bf5SDavid du Colombier {
2243ff48bf5SDavid du Colombier Wltv ltv;
2253ff48bf5SDavid du Colombier
2263ff48bf5SDavid du Colombier ltv.len = 2;
2273ff48bf5SDavid du Colombier ltv.type = type;
2283ff48bf5SDavid du Colombier ltv.val = val;
2293ff48bf5SDavid du Colombier w_outltv(ctlr, <v);
2303ff48bf5SDavid du Colombier }
2313ff48bf5SDavid du Colombier
2323ff48bf5SDavid du Colombier int
ltv_ins(Ctlr * ctlr,int type)2333ff48bf5SDavid du Colombier ltv_ins(Ctlr* ctlr, int type)
2343ff48bf5SDavid du Colombier {
2353ff48bf5SDavid du Colombier Wltv ltv;
2363ff48bf5SDavid du Colombier
2373ff48bf5SDavid du Colombier ltv.len = 2;
2383ff48bf5SDavid du Colombier ltv.type = type;
2393ff48bf5SDavid du Colombier ltv.val = 0;
2403ff48bf5SDavid du Colombier if(w_inltv(ctlr, <v))
2413ff48bf5SDavid du Colombier return -1;
2423ff48bf5SDavid du Colombier return ltv.val;
2433ff48bf5SDavid du Colombier }
2443ff48bf5SDavid du Colombier
2453ff48bf5SDavid du Colombier static void
ltv_outstr(Ctlr * ctlr,int type,char * val)2463ff48bf5SDavid du Colombier ltv_outstr(Ctlr* ctlr, int type, char* val)
2473ff48bf5SDavid du Colombier {
2483ff48bf5SDavid du Colombier Wltv ltv;
2493ff48bf5SDavid du Colombier int len;
2503ff48bf5SDavid du Colombier
2513ff48bf5SDavid du Colombier len = strlen(val);
2523ff48bf5SDavid du Colombier if(len > sizeof(ltv.s))
2533ff48bf5SDavid du Colombier len = sizeof(ltv.s);
2543ff48bf5SDavid du Colombier memset(<v, 0, sizeof(ltv));
2553ff48bf5SDavid du Colombier ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
2563ff48bf5SDavid du Colombier ltv.type = type;
2573ff48bf5SDavid du Colombier
2583ff48bf5SDavid du Colombier // This should be ltv.slen = len; according to Axel Belinfante
2593ff48bf5SDavid du Colombier ltv.slen = len;
2603ff48bf5SDavid du Colombier
2613ff48bf5SDavid du Colombier strncpy(ltv.s, val, len);
2623ff48bf5SDavid du Colombier w_outltv(ctlr, <v);
2633ff48bf5SDavid du Colombier }
2643ff48bf5SDavid du Colombier
2653ff48bf5SDavid du Colombier static char Unkname[] = "who knows";
2663ff48bf5SDavid du Colombier static char Nilname[] = "card does not tell";
2673ff48bf5SDavid du Colombier
2683ff48bf5SDavid du Colombier static char*
ltv_inname(Ctlr * ctlr,int type)2693ff48bf5SDavid du Colombier ltv_inname(Ctlr* ctlr, int type)
2703ff48bf5SDavid du Colombier {
2713ff48bf5SDavid du Colombier static Wltv ltv;
2723ff48bf5SDavid du Colombier int len;
2733ff48bf5SDavid du Colombier
2743ff48bf5SDavid du Colombier memset(<v,0,sizeof(ltv));
2753ff48bf5SDavid du Colombier ltv.len = WNameLen/2+2;
2763ff48bf5SDavid du Colombier ltv.type = type;
2773ff48bf5SDavid du Colombier if(w_inltv(ctlr, <v))
2783ff48bf5SDavid du Colombier return Unkname;
2793ff48bf5SDavid du Colombier len = ltv.slen;
2803ff48bf5SDavid du Colombier if(len == 0 || ltv.s[0] == 0)
2813ff48bf5SDavid du Colombier return Nilname;
2823ff48bf5SDavid du Colombier if(len >= sizeof ltv.s)
2833ff48bf5SDavid du Colombier len = sizeof ltv.s - 1;
2843ff48bf5SDavid du Colombier ltv.s[len] = '\0';
2853ff48bf5SDavid du Colombier return ltv.s;
2863ff48bf5SDavid du Colombier }
2873ff48bf5SDavid du Colombier
2883ff48bf5SDavid du Colombier static int
w_read(Ctlr * ctlr,int type,int off,void * buf,ulong len)2893ff48bf5SDavid du Colombier w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
2903ff48bf5SDavid du Colombier {
2913ff48bf5SDavid du Colombier if(w_seek(ctlr, type, off, 1)){
2923ff48bf5SDavid du Colombier DEBUG("wavelan: w_read: seek failed");
2933ff48bf5SDavid du Colombier return 0;
2943ff48bf5SDavid du Colombier }
2953ff48bf5SDavid du Colombier csr_inss(ctlr, WR_Data1, buf, len/2);
2963ff48bf5SDavid du Colombier
2973ff48bf5SDavid du Colombier return len;
2983ff48bf5SDavid du Colombier }
2993ff48bf5SDavid du Colombier
3003ff48bf5SDavid du Colombier static int
w_write(Ctlr * ctlr,int type,int off,void * buf,ulong len)3013ff48bf5SDavid du Colombier w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
3023ff48bf5SDavid du Colombier {
3033ff48bf5SDavid du Colombier if(w_seek(ctlr, type, off, 0)){
3043ff48bf5SDavid du Colombier DEBUG("wavelan: w_write: seek failed\n");
3053ff48bf5SDavid du Colombier return 0;
3063ff48bf5SDavid du Colombier }
3073ff48bf5SDavid du Colombier
3083ff48bf5SDavid du Colombier csr_outss(ctlr, WR_Data0, buf, len/2);
3093ff48bf5SDavid du Colombier csr_outs(ctlr, WR_Data0, 0xdead);
3103ff48bf5SDavid du Colombier csr_outs(ctlr, WR_Data0, 0xbeef);
3113ff48bf5SDavid du Colombier if(w_seek(ctlr, type, off + len, 0)){
3123ff48bf5SDavid du Colombier DEBUG("wavelan: write seek failed\n");
3133ff48bf5SDavid du Colombier return 0;
3143ff48bf5SDavid du Colombier }
3159acf0835SDavid du Colombier if(csr_ins(ctlr, WR_Data0) == 0xdead && csr_ins(ctlr, WR_Data0) == 0xbeef)
3163ff48bf5SDavid du Colombier return len;
3179acf0835SDavid du Colombier
3183ff48bf5SDavid du Colombier DEBUG("wavelan: Hermes bug byte.\n");
3193ff48bf5SDavid du Colombier return 0;
3203ff48bf5SDavid du Colombier }
3213ff48bf5SDavid du Colombier
3223ff48bf5SDavid du Colombier static int
w_alloc(Ctlr * ctlr,int len)3233ff48bf5SDavid du Colombier w_alloc(Ctlr* ctlr, int len)
3243ff48bf5SDavid du Colombier {
3253ff48bf5SDavid du Colombier int rc;
3263ff48bf5SDavid du Colombier int i,j;
3273ff48bf5SDavid du Colombier
3283ff48bf5SDavid du Colombier if(w_cmd(ctlr, WCmdMalloc, len)==0)
3293ff48bf5SDavid du Colombier for (i = 0; i<WTmOut; i++)
3303ff48bf5SDavid du Colombier if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
3313ff48bf5SDavid du Colombier csr_ack(ctlr, WAllocEv);
3323ff48bf5SDavid du Colombier rc=csr_ins(ctlr, WR_Alloc);
3333ff48bf5SDavid du Colombier if(w_seek(ctlr, rc, 0, 0))
3343ff48bf5SDavid du Colombier return -1;
3353ff48bf5SDavid du Colombier len = len/2;
3363ff48bf5SDavid du Colombier for (j=0; j<len; j++)
3373ff48bf5SDavid du Colombier csr_outs(ctlr, WR_Data0, 0);
3383ff48bf5SDavid du Colombier return rc;
3393ff48bf5SDavid du Colombier }
3403ff48bf5SDavid du Colombier return -1;
3413ff48bf5SDavid du Colombier }
3423ff48bf5SDavid du Colombier
3433ff48bf5SDavid du Colombier static int
w_enable(Ether * ether)3443ff48bf5SDavid du Colombier w_enable(Ether* ether)
3453ff48bf5SDavid du Colombier {
3463ff48bf5SDavid du Colombier Wltv ltv;
3473ff48bf5SDavid du Colombier Ctlr* ctlr = (Ctlr*) ether->ctlr;
3483ff48bf5SDavid du Colombier
3493ff48bf5SDavid du Colombier if(!ctlr)
3503ff48bf5SDavid du Colombier return -1;
3513ff48bf5SDavid du Colombier
3523ff48bf5SDavid du Colombier w_intdis(ctlr);
3533ff48bf5SDavid du Colombier w_cmd(ctlr, WCmdDis, 0);
3543ff48bf5SDavid du Colombier w_intdis(ctlr);
3553ff48bf5SDavid du Colombier if(w_cmd(ctlr, WCmdIni, 0))
3563ff48bf5SDavid du Colombier return -1;
3573ff48bf5SDavid du Colombier w_intdis(ctlr);
3583ff48bf5SDavid du Colombier
3593ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Tick, 8);
3603ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
3613ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
3623ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
3633ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
3643ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
3653ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
3663ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
3673ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
3683ff48bf5SDavid du Colombier if(*ctlr->netname)
3693ff48bf5SDavid du Colombier ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
3703ff48bf5SDavid du Colombier if(*ctlr->wantname)
3713ff48bf5SDavid du Colombier ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
3723ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
3733ff48bf5SDavid du Colombier if(*ctlr->nodename)
3743ff48bf5SDavid du Colombier ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
3753ff48bf5SDavid du Colombier ltv.len = 4;
3763ff48bf5SDavid du Colombier ltv.type = WTyp_Mac;
3773ff48bf5SDavid du Colombier memmove(ltv.addr, ether->ea, Eaddrlen);
3783ff48bf5SDavid du Colombier w_outltv(ctlr, <v);
3793ff48bf5SDavid du Colombier
3803ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
3813ff48bf5SDavid du Colombier
38215763c87SDavid du Colombier if(ctlr->hascrypt && ctlr->crypt){
3833ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
3843ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
3853ff48bf5SDavid du Colombier w_outltv(ctlr, &ctlr->keys);
3863ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
3873ff48bf5SDavid du Colombier }
3883ff48bf5SDavid du Colombier
3893ff48bf5SDavid du Colombier // BUG: set multicast addresses
3903ff48bf5SDavid du Colombier
3913ff48bf5SDavid du Colombier if(w_cmd(ctlr, WCmdEna, 0)){
3923ff48bf5SDavid du Colombier DEBUG("wavelan: Enable failed");
3933ff48bf5SDavid du Colombier return -1;
3943ff48bf5SDavid du Colombier }
3953ff48bf5SDavid du Colombier ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
3963ff48bf5SDavid du Colombier ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
3973ff48bf5SDavid du Colombier if(ctlr->txdid == -1 || ctlr->txmid == -1)
3983ff48bf5SDavid du Colombier DEBUG("wavelan: alloc failed");
3993ff48bf5SDavid du Colombier ctlr->txbusy = 0;
4003ff48bf5SDavid du Colombier w_intena(ctlr);
4013ff48bf5SDavid du Colombier return 0;
4023ff48bf5SDavid du Colombier }
4033ff48bf5SDavid du Colombier
4043ff48bf5SDavid du Colombier static void
w_rxdone(Ether * ether)4053ff48bf5SDavid du Colombier w_rxdone(Ether* ether)
4063ff48bf5SDavid du Colombier {
4073ff48bf5SDavid du Colombier Ctlr* ctlr = (Ctlr*) ether->ctlr;
4083ff48bf5SDavid du Colombier int len, sp;
4093ff48bf5SDavid du Colombier WFrame f;
4103ff48bf5SDavid du Colombier Block* bp=0;
4113ff48bf5SDavid du Colombier Etherpkt* ep;
4123ff48bf5SDavid du Colombier
4133ff48bf5SDavid du Colombier sp = csr_ins(ctlr, WR_RXId);
4143ff48bf5SDavid du Colombier len = w_read(ctlr, sp, 0, &f, sizeof(f));
4153ff48bf5SDavid du Colombier if(len == 0){
4163ff48bf5SDavid du Colombier DEBUG("wavelan: read frame error\n");
4173ff48bf5SDavid du Colombier goto rxerror;
4183ff48bf5SDavid du Colombier }
4193ff48bf5SDavid du Colombier if(f.sts&WF_Err){
4203ff48bf5SDavid du Colombier goto rxerror;
4213ff48bf5SDavid du Colombier }
4223ff48bf5SDavid du Colombier switch(f.sts){
4233ff48bf5SDavid du Colombier case WF_1042:
4243ff48bf5SDavid du Colombier case WF_Tunnel:
4253ff48bf5SDavid du Colombier case WF_WMP:
4263ff48bf5SDavid du Colombier len = f.dlen + WSnapHdrLen;
4273ff48bf5SDavid du Colombier bp = iallocb(ETHERHDRSIZE + len + 2);
4283ff48bf5SDavid du Colombier if(!bp)
4293ff48bf5SDavid du Colombier goto rxerror;
4303ff48bf5SDavid du Colombier ep = (Etherpkt*) bp->wp;
4313ff48bf5SDavid du Colombier memmove(ep->d, f.addr1, Eaddrlen);
4323ff48bf5SDavid du Colombier memmove(ep->s, f.addr2, Eaddrlen);
4333ff48bf5SDavid du Colombier memmove(ep->type,&f.type,2);
4343ff48bf5SDavid du Colombier bp->wp += ETHERHDRSIZE;
4353ff48bf5SDavid du Colombier if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
4363ff48bf5SDavid du Colombier DEBUG("wavelan: read 802.11 error\n");
4373ff48bf5SDavid du Colombier goto rxerror;
4383ff48bf5SDavid du Colombier }
4393ff48bf5SDavid du Colombier bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
4403ff48bf5SDavid du Colombier break;
4413ff48bf5SDavid du Colombier default:
4423ff48bf5SDavid du Colombier len = ETHERHDRSIZE + f.dlen + 2;
4433ff48bf5SDavid du Colombier bp = iallocb(len);
4443ff48bf5SDavid du Colombier if(!bp)
4453ff48bf5SDavid du Colombier goto rxerror;
4463ff48bf5SDavid du Colombier if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
4473ff48bf5SDavid du Colombier DEBUG("wavelan: read 800.3 error\n");
4483ff48bf5SDavid du Colombier goto rxerror;
4493ff48bf5SDavid du Colombier }
4503ff48bf5SDavid du Colombier bp->wp += len;
4513ff48bf5SDavid du Colombier }
4523ff48bf5SDavid du Colombier
4533ff48bf5SDavid du Colombier ctlr->nrx++;
4543ff48bf5SDavid du Colombier etheriq(ether,bp,1);
4553ff48bf5SDavid du Colombier ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
4563ff48bf5SDavid du Colombier ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
4573ff48bf5SDavid du Colombier return;
4583ff48bf5SDavid du Colombier
4593ff48bf5SDavid du Colombier rxerror:
4603ff48bf5SDavid du Colombier freeb(bp);
4613ff48bf5SDavid du Colombier ctlr->nrxerr++;
4623ff48bf5SDavid du Colombier }
4633ff48bf5SDavid du Colombier
4643ff48bf5SDavid du Colombier static void
w_txstart(Ether * ether)4653ff48bf5SDavid du Colombier w_txstart(Ether* ether)
4663ff48bf5SDavid du Colombier {
4673ff48bf5SDavid du Colombier Etherpkt *pkt;
4683ff48bf5SDavid du Colombier Ctlr *ctlr;
4693ff48bf5SDavid du Colombier Block *bp;
4703ff48bf5SDavid du Colombier int len, off;
4713ff48bf5SDavid du Colombier
4723ff48bf5SDavid du Colombier if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
4733ff48bf5SDavid du Colombier return;
4743ff48bf5SDavid du Colombier
4753ff48bf5SDavid du Colombier if((bp = qget(ether->oq)) == nil)
4763ff48bf5SDavid du Colombier return;
4773ff48bf5SDavid du Colombier pkt = (Etherpkt*)bp->rp;
4783ff48bf5SDavid du Colombier
4793ff48bf5SDavid du Colombier //
4803ff48bf5SDavid du Colombier // If the packet header type field is > 1500 it is an IP or
4813ff48bf5SDavid du Colombier // ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
4823ff48bf5SDavid du Colombier //
4833ff48bf5SDavid du Colombier memset(&ctlr->txf, 0, sizeof(ctlr->txf));
4843ff48bf5SDavid du Colombier if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
4853ff48bf5SDavid du Colombier ctlr->txf.framectl = WF_Data;
4863ff48bf5SDavid du Colombier memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
4873ff48bf5SDavid du Colombier memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
4883ff48bf5SDavid du Colombier memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
4893ff48bf5SDavid du Colombier memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
4903ff48bf5SDavid du Colombier memmove(&ctlr->txf.type, pkt->type, 2);
4913ff48bf5SDavid du Colombier bp->rp += ETHERHDRSIZE;
4923ff48bf5SDavid du Colombier len = BLEN(bp);
4933ff48bf5SDavid du Colombier off = WF_802_11_Off;
4943ff48bf5SDavid du Colombier ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
4953ff48bf5SDavid du Colombier hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
4963ff48bf5SDavid du Colombier hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
4973ff48bf5SDavid du Colombier hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
4983ff48bf5SDavid du Colombier }
4993ff48bf5SDavid du Colombier else{
5003ff48bf5SDavid du Colombier len = BLEN(bp);
5013ff48bf5SDavid du Colombier off = WF_802_3_Off;
5023ff48bf5SDavid du Colombier ctlr->txf.dlen = len;
5033ff48bf5SDavid du Colombier }
5043ff48bf5SDavid du Colombier w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
5053ff48bf5SDavid du Colombier w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
5063ff48bf5SDavid du Colombier
5073ff48bf5SDavid du Colombier if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
5083ff48bf5SDavid du Colombier DEBUG("wavelan: transmit failed\n");
5093ff48bf5SDavid du Colombier ctlr->ntxerr++;
5103ff48bf5SDavid du Colombier }
5113ff48bf5SDavid du Colombier else{
5123ff48bf5SDavid du Colombier ctlr->txbusy = 1;
5133ff48bf5SDavid du Colombier ctlr->txtmout = 2;
5143ff48bf5SDavid du Colombier }
5153ff48bf5SDavid du Colombier freeb(bp);
5163ff48bf5SDavid du Colombier }
5173ff48bf5SDavid du Colombier
5183ff48bf5SDavid du Colombier static void
w_txdone(Ctlr * ctlr,int sts)5193ff48bf5SDavid du Colombier w_txdone(Ctlr* ctlr, int sts)
5203ff48bf5SDavid du Colombier {
5213ff48bf5SDavid du Colombier ctlr->txbusy = 0;
5223ff48bf5SDavid du Colombier ctlr->txtmout = 0;
5233ff48bf5SDavid du Colombier if(sts & WTxErrEv)
5243ff48bf5SDavid du Colombier ctlr->ntxerr++;
5253ff48bf5SDavid du Colombier else
5263ff48bf5SDavid du Colombier ctlr->ntx++;
5273ff48bf5SDavid du Colombier }
5283ff48bf5SDavid du Colombier
52915763c87SDavid du Colombier /* save the stats info in the ctlr struct */
53015763c87SDavid du Colombier static void
w_stats(Ctlr * ctlr,int len)53115763c87SDavid du Colombier w_stats(Ctlr* ctlr, int len)
5323ff48bf5SDavid du Colombier {
53315763c87SDavid du Colombier int i, rc;
5343ff48bf5SDavid du Colombier ulong* p = (ulong*)&ctlr->WStats;
5353ff48bf5SDavid du Colombier ulong* pend = (ulong*)&ctlr->end;
5363ff48bf5SDavid du Colombier
53715763c87SDavid du Colombier for (i = 0; i < len && p < pend; i++){
5383ff48bf5SDavid du Colombier rc = csr_ins(ctlr, WR_Data1);
5393ff48bf5SDavid du Colombier if(rc > 0xf000)
5403ff48bf5SDavid du Colombier rc = ~rc & 0xffff;
5413ff48bf5SDavid du Colombier p[i] += rc;
5423ff48bf5SDavid du Colombier }
54315763c87SDavid du Colombier }
54415763c87SDavid du Colombier
54515763c87SDavid du Colombier /* send the base station scan info to any readers */
54615763c87SDavid du Colombier static void
w_scaninfo(Ether * ether,Ctlr * ctlr,int len)54715763c87SDavid du Colombier w_scaninfo(Ether* ether, Ctlr *ctlr, int len)
54815763c87SDavid du Colombier {
54915763c87SDavid du Colombier int i, j;
55015763c87SDavid du Colombier Netfile **ep, *f, **fp;
55115763c87SDavid du Colombier Block *bp;
55215763c87SDavid du Colombier WScan *wsp;
553fb7f0c93SDavid du Colombier ushort *scanbuf;
554fb7f0c93SDavid du Colombier
555fb7f0c93SDavid du Colombier scanbuf = malloc(len*2);
556fb7f0c93SDavid du Colombier if(scanbuf == nil)
557fb7f0c93SDavid du Colombier return;
55815763c87SDavid du Colombier
55915763c87SDavid du Colombier for (i = 0; i < len ; i++)
560fb7f0c93SDavid du Colombier scanbuf[i] = csr_ins(ctlr, WR_Data1);
56115763c87SDavid du Colombier
562fb7f0c93SDavid du Colombier /* calculate number of samples */
563fb7f0c93SDavid du Colombier len /= 25;
564fb7f0c93SDavid du Colombier if(len == 0)
565fb7f0c93SDavid du Colombier goto out;
566fb7f0c93SDavid du Colombier
56715763c87SDavid du Colombier i = ether->scan;
56815763c87SDavid du Colombier ep = ðer->f[Ntypes];
56915763c87SDavid du Colombier for(fp = ether->f; fp < ep && i > 0; fp++){
57015763c87SDavid du Colombier f = *fp;
57115763c87SDavid du Colombier if(f == nil || f->scan == 0)
57215763c87SDavid du Colombier continue;
57315763c87SDavid du Colombier
574fb7f0c93SDavid du Colombier bp = iallocb(100*len);
57515763c87SDavid du Colombier if(bp == nil)
57615763c87SDavid du Colombier break;
577fb7f0c93SDavid du Colombier for(j = 0; j < len; j++){
578fb7f0c93SDavid du Colombier wsp = (WScan*)(&scanbuf[j*25]);
57915763c87SDavid du Colombier if(wsp->ssid_len > 32)
58015763c87SDavid du Colombier wsp->ssid_len = 32;
581fb7f0c93SDavid du Colombier bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
58215763c87SDavid du Colombier "ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n",
58315763c87SDavid du Colombier wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal,
58415763c87SDavid du Colombier wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":"");
58515763c87SDavid du Colombier }
58615763c87SDavid du Colombier qpass(f->in, bp);
58715763c87SDavid du Colombier i--;
58815763c87SDavid du Colombier }
589fb7f0c93SDavid du Colombier out:
590fb7f0c93SDavid du Colombier free(scanbuf);
59115763c87SDavid du Colombier }
59215763c87SDavid du Colombier
59315763c87SDavid du Colombier static int
w_info(Ether * ether,Ctlr * ctlr)59415763c87SDavid du Colombier w_info(Ether *ether, Ctlr* ctlr)
59515763c87SDavid du Colombier {
59615763c87SDavid du Colombier int sp;
59715763c87SDavid du Colombier Wltv ltv;
59815763c87SDavid du Colombier
59915763c87SDavid du Colombier sp = csr_ins(ctlr, WR_InfoId);
60015763c87SDavid du Colombier ltv.len = ltv.type = 0;
60115763c87SDavid du Colombier w_read(ctlr, sp, 0, <v, 4);
60215763c87SDavid du Colombier ltv.len--;
60315763c87SDavid du Colombier switch(ltv.type){
60415763c87SDavid du Colombier case WTyp_Stats:
60515763c87SDavid du Colombier w_stats(ctlr, ltv.len);
60615763c87SDavid du Colombier return 0;
60715763c87SDavid du Colombier case WTyp_Scan:
60815763c87SDavid du Colombier w_scaninfo(ether, ctlr, ltv.len);
6093ff48bf5SDavid du Colombier return 0;
6103ff48bf5SDavid du Colombier }
6113ff48bf5SDavid du Colombier return -1;
6123ff48bf5SDavid du Colombier }
6133ff48bf5SDavid du Colombier
61415763c87SDavid du Colombier /* set scanning interval */
61515763c87SDavid du Colombier static void
w_scanbs(void * a,uint secs)61615763c87SDavid du Colombier w_scanbs(void *a, uint secs)
61715763c87SDavid du Colombier {
61815763c87SDavid du Colombier Ether *ether = a;
61915763c87SDavid du Colombier Ctlr* ctlr = (Ctlr*) ether->ctlr;
62015763c87SDavid du Colombier
62115763c87SDavid du Colombier ctlr->scanticks = secs*(1000/MSperTick);
62215763c87SDavid du Colombier }
62315763c87SDavid du Colombier
6243ff48bf5SDavid du Colombier static void
w_intr(Ether * ether)6253ff48bf5SDavid du Colombier w_intr(Ether *ether)
6263ff48bf5SDavid du Colombier {
6273ff48bf5SDavid du Colombier int rc, txid;
6283ff48bf5SDavid du Colombier Ctlr* ctlr = (Ctlr*) ether->ctlr;
6293ff48bf5SDavid du Colombier
6303ff48bf5SDavid du Colombier if((ctlr->state & Power) == 0)
6313ff48bf5SDavid du Colombier return;
6323ff48bf5SDavid du Colombier
6333ff48bf5SDavid du Colombier if((ctlr->state & Attached) == 0){
6343ff48bf5SDavid du Colombier csr_ack(ctlr, 0xffff);
6353ff48bf5SDavid du Colombier csr_outs(ctlr, WR_IntEna, 0);
6363ff48bf5SDavid du Colombier return;
6373ff48bf5SDavid du Colombier }
6383ff48bf5SDavid du Colombier
6393ff48bf5SDavid du Colombier rc = csr_ins(ctlr, WR_EvSts);
6403ff48bf5SDavid du Colombier csr_ack(ctlr, ~WEvs); // Not interested in them
6413ff48bf5SDavid du Colombier if(rc & WRXEv){
6423ff48bf5SDavid du Colombier w_rxdone(ether);
6433ff48bf5SDavid du Colombier csr_ack(ctlr, WRXEv);
6443ff48bf5SDavid du Colombier }
6453ff48bf5SDavid du Colombier if(rc & WTXEv){
6463ff48bf5SDavid du Colombier w_txdone(ctlr, rc);
6473ff48bf5SDavid du Colombier csr_ack(ctlr, WTXEv);
6483ff48bf5SDavid du Colombier }
6493ff48bf5SDavid du Colombier if(rc & WAllocEv){
6503ff48bf5SDavid du Colombier ctlr->nalloc++;
6513ff48bf5SDavid du Colombier txid = csr_ins(ctlr, WR_Alloc);
6523ff48bf5SDavid du Colombier csr_ack(ctlr, WAllocEv);
6533ff48bf5SDavid du Colombier if(txid == ctlr->txdid){
6543ff48bf5SDavid du Colombier if((rc & WTXEv) == 0)
6553ff48bf5SDavid du Colombier w_txdone(ctlr, rc);
6563ff48bf5SDavid du Colombier }
6573ff48bf5SDavid du Colombier }
6583ff48bf5SDavid du Colombier if(rc & WInfoEv){
6593ff48bf5SDavid du Colombier ctlr->ninfo++;
66015763c87SDavid du Colombier w_info(ether, ctlr);
6613ff48bf5SDavid du Colombier csr_ack(ctlr, WInfoEv);
6623ff48bf5SDavid du Colombier }
6633ff48bf5SDavid du Colombier if(rc & WTxErrEv){
6643ff48bf5SDavid du Colombier w_txdone(ctlr, rc);
6653ff48bf5SDavid du Colombier csr_ack(ctlr, WTxErrEv);
6663ff48bf5SDavid du Colombier }
6673ff48bf5SDavid du Colombier if(rc & WIDropEv){
6683ff48bf5SDavid du Colombier ctlr->nidrop++;
6693ff48bf5SDavid du Colombier csr_ack(ctlr, WIDropEv);
6703ff48bf5SDavid du Colombier }
6713ff48bf5SDavid du Colombier w_txstart(ether);
6723ff48bf5SDavid du Colombier }
6733ff48bf5SDavid du Colombier
6743ff48bf5SDavid du Colombier // Watcher to ensure that the card still works properly and
6753ff48bf5SDavid du Colombier // to request WStats updates once a minute.
6763ff48bf5SDavid du Colombier // BUG: it runs much more often, see the comment below.
6773ff48bf5SDavid du Colombier
6783ff48bf5SDavid du Colombier static void
w_timer(void * arg)6793ff48bf5SDavid du Colombier w_timer(void* arg)
6803ff48bf5SDavid du Colombier {
6813ff48bf5SDavid du Colombier Ether* ether = (Ether*) arg;
6823ff48bf5SDavid du Colombier Ctlr* ctlr = (Ctlr*)ether->ctlr;
6833ff48bf5SDavid du Colombier
6843ff48bf5SDavid du Colombier ctlr->timerproc = up;
6853ff48bf5SDavid du Colombier for(;;){
686dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, MSperTick);
6873ff48bf5SDavid du Colombier ctlr = (Ctlr*)ether->ctlr;
6883ff48bf5SDavid du Colombier if(ctlr == 0)
6893ff48bf5SDavid du Colombier break;
6903ff48bf5SDavid du Colombier if((ctlr->state & (Attached|Power)) != (Attached|Power))
6913ff48bf5SDavid du Colombier continue;
6923ff48bf5SDavid du Colombier ctlr->ticks++;
6933ff48bf5SDavid du Colombier
6943ff48bf5SDavid du Colombier ilock(ctlr);
6953ff48bf5SDavid du Colombier
6963ff48bf5SDavid du Colombier // Seems that the card gets frames BUT does
6973ff48bf5SDavid du Colombier // not send the interrupt; this is a problem because
6983ff48bf5SDavid du Colombier // I suspect it runs out of receive buffers and
6993ff48bf5SDavid du Colombier // stops receiving until a transmit watchdog
7003ff48bf5SDavid du Colombier // reenables the card.
7013ff48bf5SDavid du Colombier // The problem is serious because it leads to
7023ff48bf5SDavid du Colombier // poor rtts.
7033ff48bf5SDavid du Colombier // This can be seen clearly by commenting out
7043ff48bf5SDavid du Colombier // the next if and doing a ping: it will stop
7053ff48bf5SDavid du Colombier // receiving (although the icmp replies are being
7063ff48bf5SDavid du Colombier // issued from the remote) after a few seconds.
7073ff48bf5SDavid du Colombier // Of course this `bug' could be because I'm reading
7083ff48bf5SDavid du Colombier // the card frames in the wrong way; due to the
7093ff48bf5SDavid du Colombier // lack of documentation I cannot know.
7103ff48bf5SDavid du Colombier
7113ff48bf5SDavid du Colombier if(csr_ins(ctlr, WR_EvSts)&WEvs){
7123ff48bf5SDavid du Colombier ctlr->tickintr++;
7133ff48bf5SDavid du Colombier w_intr(ether);
7143ff48bf5SDavid du Colombier }
7153ff48bf5SDavid du Colombier
7163ff48bf5SDavid du Colombier if((ctlr->ticks % 10) == 0) {
7173ff48bf5SDavid du Colombier if(ctlr->txtmout && --ctlr->txtmout == 0){
7183ff48bf5SDavid du Colombier ctlr->nwatchdogs++;
7193ff48bf5SDavid du Colombier w_txdone(ctlr, WTxErrEv);
7203ff48bf5SDavid du Colombier if(w_enable(ether)){
7213ff48bf5SDavid du Colombier DEBUG("wavelan: wdog enable failed\n");
7223ff48bf5SDavid du Colombier }
7233ff48bf5SDavid du Colombier w_txstart(ether);
7243ff48bf5SDavid du Colombier }
7253ff48bf5SDavid du Colombier if((ctlr->ticks % 120) == 0)
7263ff48bf5SDavid du Colombier if(ctlr->txbusy == 0)
72715763c87SDavid du Colombier w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
72815763c87SDavid du Colombier if(ctlr->scanticks > 0)
72915763c87SDavid du Colombier if((ctlr->ticks % ctlr->scanticks) == 0)
73015763c87SDavid du Colombier if(ctlr->txbusy == 0)
73115763c87SDavid du Colombier w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
7323ff48bf5SDavid du Colombier }
7333ff48bf5SDavid du Colombier iunlock(ctlr);
7343ff48bf5SDavid du Colombier }
7353ff48bf5SDavid du Colombier pexit("terminated", 0);
7363ff48bf5SDavid du Colombier }
7373ff48bf5SDavid du Colombier
7383ff48bf5SDavid du Colombier void
w_multicast(void * ether,uchar *,int add)7395fb521f9SDavid du Colombier w_multicast(void *ether, uchar*, int add)
7403ff48bf5SDavid du Colombier {
7415fb521f9SDavid du Colombier /* BUG: use controller's multicast filter */
7425fb521f9SDavid du Colombier if (add)
7435fb521f9SDavid du Colombier w_promiscuous(ether, 1);
7443ff48bf5SDavid du Colombier }
7453ff48bf5SDavid du Colombier
7463ff48bf5SDavid du Colombier void
w_attach(Ether * ether)7473ff48bf5SDavid du Colombier w_attach(Ether* ether)
7483ff48bf5SDavid du Colombier {
7493ff48bf5SDavid du Colombier Ctlr* ctlr;
7503ff48bf5SDavid du Colombier char name[64];
7513ff48bf5SDavid du Colombier int rc;
7523ff48bf5SDavid du Colombier
7533ff48bf5SDavid du Colombier if(ether->ctlr == 0)
7543ff48bf5SDavid du Colombier return;
7553ff48bf5SDavid du Colombier
7563ff48bf5SDavid du Colombier snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
7573ff48bf5SDavid du Colombier ctlr = (Ctlr*) ether->ctlr;
7583ff48bf5SDavid du Colombier if((ctlr->state & Attached) == 0){
7593ff48bf5SDavid du Colombier ilock(ctlr);
7603ff48bf5SDavid du Colombier rc = w_enable(ether);
7613ff48bf5SDavid du Colombier iunlock(ctlr);
7623ff48bf5SDavid du Colombier if(rc == 0){
7633ff48bf5SDavid du Colombier ctlr->state |= Attached;
7643ff48bf5SDavid du Colombier kproc(name, w_timer, ether);
7653ff48bf5SDavid du Colombier } else
7663ff48bf5SDavid du Colombier print("#l%d: enable failed\n",ether->ctlrno);
7673ff48bf5SDavid du Colombier }
7683ff48bf5SDavid du Colombier }
7693ff48bf5SDavid du Colombier
7703ff48bf5SDavid du Colombier void
w_detach(Ether * ether)7713ff48bf5SDavid du Colombier w_detach(Ether* ether)
7723ff48bf5SDavid du Colombier {
7733ff48bf5SDavid du Colombier Ctlr* ctlr;
7743ff48bf5SDavid du Colombier char name[64];
7753ff48bf5SDavid du Colombier
7763ff48bf5SDavid du Colombier if(ether->ctlr == nil)
7773ff48bf5SDavid du Colombier return;
7783ff48bf5SDavid du Colombier
7793ff48bf5SDavid du Colombier snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
7803ff48bf5SDavid du Colombier ctlr = (Ctlr*) ether->ctlr;
7813ff48bf5SDavid du Colombier if(ctlr->state & Attached){
7823ff48bf5SDavid du Colombier ilock(ctlr);
7833ff48bf5SDavid du Colombier w_intdis(ctlr);
7843ff48bf5SDavid du Colombier if(ctlr->timerproc){
7853ff48bf5SDavid du Colombier if(!postnote(ctlr->timerproc, 1, "kill", NExit))
7863ff48bf5SDavid du Colombier print("timerproc note not posted\n");
7873ff48bf5SDavid du Colombier print("w_detach, killing 0x%p\n", ctlr->timerproc);
7883ff48bf5SDavid du Colombier }
7893ff48bf5SDavid du Colombier ctlr->state &= ~Attached;
7903ff48bf5SDavid du Colombier iunlock(ctlr);
7913ff48bf5SDavid du Colombier }
7923ff48bf5SDavid du Colombier ether->ctlr = nil;
7933ff48bf5SDavid du Colombier }
7943ff48bf5SDavid du Colombier
7953ff48bf5SDavid du Colombier void
w_power(Ether * ether,int on)7963ff48bf5SDavid du Colombier w_power(Ether* ether, int on)
7973ff48bf5SDavid du Colombier {
7983ff48bf5SDavid du Colombier Ctlr *ctlr;
7993ff48bf5SDavid du Colombier
8003ff48bf5SDavid du Colombier ctlr = (Ctlr*) ether->ctlr;
8013ff48bf5SDavid du Colombier ilock(ctlr);
8023ff48bf5SDavid du Colombier iprint("w_power %d\n", on);
8033ff48bf5SDavid du Colombier if(on){
8043ff48bf5SDavid du Colombier if((ctlr->state & Power) == 0){
8053ff48bf5SDavid du Colombier if (wavelanreset(ether, ctlr) < 0){
8063ff48bf5SDavid du Colombier iprint("w_power: reset failed\n");
8073ff48bf5SDavid du Colombier iunlock(ctlr);
8083ff48bf5SDavid du Colombier w_detach(ether);
8093ff48bf5SDavid du Colombier free(ctlr);
8103ff48bf5SDavid du Colombier return;
8113ff48bf5SDavid du Colombier }
8123ff48bf5SDavid du Colombier if(ctlr->state & Attached)
8133ff48bf5SDavid du Colombier w_enable(ether);
8143ff48bf5SDavid du Colombier ctlr->state |= Power;
8153ff48bf5SDavid du Colombier }
8163ff48bf5SDavid du Colombier }else{
8173ff48bf5SDavid du Colombier if(ctlr->state & Power){
8183ff48bf5SDavid du Colombier if(ctlr->state & Attached)
8193ff48bf5SDavid du Colombier w_intdis(ctlr);
8203ff48bf5SDavid du Colombier ctlr->state &= ~Power;
8213ff48bf5SDavid du Colombier }
8223ff48bf5SDavid du Colombier }
8233ff48bf5SDavid du Colombier iunlock(ctlr);
8243ff48bf5SDavid du Colombier }
8253ff48bf5SDavid du Colombier
8263ff48bf5SDavid du Colombier #define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val))
8273ff48bf5SDavid du Colombier #define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt))
8283ff48bf5SDavid du Colombier
8293ff48bf5SDavid du Colombier long
w_ifstat(Ether * ether,void * a,long n,ulong offset)8303ff48bf5SDavid du Colombier w_ifstat(Ether* ether, void* a, long n, ulong offset)
8313ff48bf5SDavid du Colombier {
8323ff48bf5SDavid du Colombier Ctlr *ctlr = (Ctlr*) ether->ctlr;
8333ff48bf5SDavid du Colombier char *k, *p;
8343ff48bf5SDavid du Colombier int i, l, txid;
8353ff48bf5SDavid du Colombier
8363ff48bf5SDavid du Colombier ether->oerrs = ctlr->ntxerr;
8373ff48bf5SDavid du Colombier ether->crcs = ctlr->nrxfcserr;
8383ff48bf5SDavid du Colombier ether->frames = 0;
8393ff48bf5SDavid du Colombier ether->buffs = ctlr->nrxdropnobuf;
8403ff48bf5SDavid du Colombier ether->overflows = 0;
8413ff48bf5SDavid du Colombier
8423ff48bf5SDavid du Colombier //
8433ff48bf5SDavid du Colombier // Offset must be zero or there's a possibility the
8443ff48bf5SDavid du Colombier // new data won't match the previous read.
8453ff48bf5SDavid du Colombier //
8463ff48bf5SDavid du Colombier if(n == 0 || offset != 0)
8473ff48bf5SDavid du Colombier return 0;
8483ff48bf5SDavid du Colombier
8493ff48bf5SDavid du Colombier p = malloc(READSTR);
850*aa72973aSDavid du Colombier if(p == nil)
851*aa72973aSDavid du Colombier error(Enomem);
8523ff48bf5SDavid du Colombier l = 0;
8533ff48bf5SDavid du Colombier
8543ff48bf5SDavid du Colombier PRINTSTAT("Signal: %d\n", ctlr->signal-149);
8553ff48bf5SDavid du Colombier PRINTSTAT("Noise: %d\n", ctlr->noise-149);
8563ff48bf5SDavid du Colombier PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
8573ff48bf5SDavid du Colombier PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
8583ff48bf5SDavid du Colombier PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
8593ff48bf5SDavid du Colombier PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
8603ff48bf5SDavid du Colombier PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
8613ff48bf5SDavid du Colombier PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
8623ff48bf5SDavid du Colombier PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
8633ff48bf5SDavid du Colombier PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
8643ff48bf5SDavid du Colombier PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
8653ff48bf5SDavid du Colombier PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
8663ff48bf5SDavid du Colombier PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
8673ff48bf5SDavid du Colombier PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
8683ff48bf5SDavid du Colombier PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
8693ff48bf5SDavid du Colombier PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
8703ff48bf5SDavid du Colombier k = ((ctlr->state & Attached) ? "attached" : "not attached");
8713ff48bf5SDavid du Colombier PRINTSTAT("Card %s", k);
8723ff48bf5SDavid du Colombier k = ((ctlr->state & Power) ? "on" : "off");
87315763c87SDavid du Colombier PRINTSTAT(", power %s", k);
8743ff48bf5SDavid du Colombier k = ((ctlr->txbusy)? ", txbusy" : "");
8753ff48bf5SDavid du Colombier PRINTSTAT("%s\n", k);
8763ff48bf5SDavid du Colombier
8773ff48bf5SDavid du Colombier if(ctlr->hascrypt){
8783ff48bf5SDavid du Colombier PRINTSTR("Keys: ");
8793ff48bf5SDavid du Colombier for (i = 0; i < WNKeys; i++){
8803ff48bf5SDavid du Colombier if(ctlr->keys.keys[i].len == 0)
8813ff48bf5SDavid du Colombier PRINTSTR("none ");
8823ff48bf5SDavid du Colombier else if(SEEKEYS == 0)
8833ff48bf5SDavid du Colombier PRINTSTR("set ");
8843ff48bf5SDavid du Colombier else
8853ff48bf5SDavid du Colombier PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
8863ff48bf5SDavid du Colombier }
8873ff48bf5SDavid du Colombier PRINTSTR("\n");
8883ff48bf5SDavid du Colombier }
8893ff48bf5SDavid du Colombier
8903ff48bf5SDavid du Colombier // real card stats
8913ff48bf5SDavid du Colombier ilock(ctlr);
8923ff48bf5SDavid du Colombier PRINTSTR("\nCard stats: \n");
8933ff48bf5SDavid du Colombier PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
8943ff48bf5SDavid du Colombier PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
8953ff48bf5SDavid du Colombier i = ltv_ins(ctlr, WTyp_Ptype);
8963ff48bf5SDavid du Colombier PRINTSTAT("Port type: %d\n", i);
8973ff48bf5SDavid du Colombier PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
8983ff48bf5SDavid du Colombier PRINTSTAT("Current Transmit rate: %d\n",
8993ff48bf5SDavid du Colombier ltv_ins(ctlr, WTyp_CurTxRate));
9003ff48bf5SDavid du Colombier PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
9013ff48bf5SDavid du Colombier PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
9023ff48bf5SDavid du Colombier PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
90315763c87SDavid du Colombier if(i == WPTypeAdHoc)
9043ff48bf5SDavid du Colombier PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
9053ff48bf5SDavid du Colombier else {
9063ff48bf5SDavid du Colombier Wltv ltv;
9073ff48bf5SDavid du Colombier PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
9083ff48bf5SDavid du Colombier ltv.type = WTyp_BaseID;
9093ff48bf5SDavid du Colombier ltv.len = 4;
9103ff48bf5SDavid du Colombier if(w_inltv(ctlr, <v))
9113ff48bf5SDavid du Colombier print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
9123ff48bf5SDavid du Colombier l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
9133ff48bf5SDavid du Colombier ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
9143ff48bf5SDavid du Colombier }
9153ff48bf5SDavid du Colombier PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
9163ff48bf5SDavid du Colombier PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
9173ff48bf5SDavid du Colombier if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
9183ff48bf5SDavid du Colombier PRINTSTR("WEP: not supported\n");
9193ff48bf5SDavid du Colombier else {
9203ff48bf5SDavid du Colombier if(ltv_ins(ctlr, WTyp_Crypt) == 0)
9213ff48bf5SDavid du Colombier PRINTSTR("WEP: disabled\n");
9223ff48bf5SDavid du Colombier else{
9233ff48bf5SDavid du Colombier PRINTSTR("WEP: enabled\n");
9243ff48bf5SDavid du Colombier k = ((ctlr->xclear)? "excluded": "included");
9253ff48bf5SDavid du Colombier PRINTSTAT("Clear packets: %s\n", k);
9263ff48bf5SDavid du Colombier txid = ltv_ins(ctlr, WTyp_TxKey);
9273ff48bf5SDavid du Colombier PRINTSTAT("Transmit key id: %d\n", txid);
9283ff48bf5SDavid du Colombier }
9293ff48bf5SDavid du Colombier }
9303ff48bf5SDavid du Colombier iunlock(ctlr);
9313ff48bf5SDavid du Colombier
9323ff48bf5SDavid du Colombier PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
9333ff48bf5SDavid du Colombier PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
9343ff48bf5SDavid du Colombier PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
9353ff48bf5SDavid du Colombier PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
9363ff48bf5SDavid du Colombier PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
9373ff48bf5SDavid du Colombier PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
9383ff48bf5SDavid du Colombier PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
9393ff48bf5SDavid du Colombier PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
9403ff48bf5SDavid du Colombier PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
9413ff48bf5SDavid du Colombier PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
9423ff48bf5SDavid du Colombier PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
9433ff48bf5SDavid du Colombier PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
9443ff48bf5SDavid du Colombier PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
9453ff48bf5SDavid du Colombier PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
9463ff48bf5SDavid du Colombier PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
9473ff48bf5SDavid du Colombier PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
9483ff48bf5SDavid du Colombier PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
9493ff48bf5SDavid du Colombier PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
9503ff48bf5SDavid du Colombier PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
9513ff48bf5SDavid du Colombier PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
9523ff48bf5SDavid du Colombier PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
9533ff48bf5SDavid du Colombier USED(l);
9543ff48bf5SDavid du Colombier n = readstr(offset, a, n, p);
9553ff48bf5SDavid du Colombier free(p);
9563ff48bf5SDavid du Colombier return n;
9573ff48bf5SDavid du Colombier }
9583ff48bf5SDavid du Colombier #undef PRINTSTR
9593ff48bf5SDavid du Colombier #undef PRINTSTAT
9603ff48bf5SDavid du Colombier
9617c881178SDavid du Colombier static int
parsekey(WKey * key,char * a)9627c881178SDavid du Colombier parsekey(WKey* key, char* a)
9637c881178SDavid du Colombier {
9647c881178SDavid du Colombier int i, k, len, n;
9657c881178SDavid du Colombier char buf[WMaxKeyLen];
9667c881178SDavid du Colombier
9677c881178SDavid du Colombier len = strlen(a);
9687c881178SDavid du Colombier if(len == WMinKeyLen || len == WMaxKeyLen){
9697c881178SDavid du Colombier memset(key->dat, 0, sizeof(key->dat));
9707c881178SDavid du Colombier memmove(key->dat, a, len);
9717c881178SDavid du Colombier key->len = len;
9727c881178SDavid du Colombier
9737c881178SDavid du Colombier return 0;
9747c881178SDavid du Colombier }
9757c881178SDavid du Colombier else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
9767c881178SDavid du Colombier k = 0;
9777c881178SDavid du Colombier for(i = 0; i < len; i++){
9787c881178SDavid du Colombier if(*a >= '0' && *a <= '9')
9797c881178SDavid du Colombier n = *a++ - '0';
9807c881178SDavid du Colombier else if(*a >= 'a' && *a <= 'f')
9817c881178SDavid du Colombier n = *a++ - 'a' + 10;
9827c881178SDavid du Colombier else if(*a >= 'A' && *a <= 'F')
9837c881178SDavid du Colombier n = *a++ - 'A' + 10;
9847c881178SDavid du Colombier else
9857c881178SDavid du Colombier return -1;
9867c881178SDavid du Colombier
9877c881178SDavid du Colombier if(i & 1){
9887c881178SDavid du Colombier buf[k] |= n;
9897c881178SDavid du Colombier k++;
9907c881178SDavid du Colombier }
9917c881178SDavid du Colombier else
9927c881178SDavid du Colombier buf[k] = n<<4;
9937c881178SDavid du Colombier }
9947c881178SDavid du Colombier
9957c881178SDavid du Colombier memset(key->dat, 0, sizeof(key->dat));
9967c881178SDavid du Colombier memmove(key->dat, buf, k);
9977c881178SDavid du Colombier key->len = k;
9987c881178SDavid du Colombier
9997c881178SDavid du Colombier return 0;
10007c881178SDavid du Colombier }
10017c881178SDavid du Colombier
10027c881178SDavid du Colombier return -1;
10037c881178SDavid du Colombier }
10047c881178SDavid du Colombier
10053ff48bf5SDavid du Colombier int
w_option(Ctlr * ctlr,char * buf,long n)10063ff48bf5SDavid du Colombier w_option(Ctlr* ctlr, char* buf, long n)
10073ff48bf5SDavid du Colombier {
10083ff48bf5SDavid du Colombier char *p;
10093ff48bf5SDavid du Colombier int i, r;
10103ff48bf5SDavid du Colombier Cmdbuf *cb;
10113ff48bf5SDavid du Colombier
10123ff48bf5SDavid du Colombier r = 0;
10133ff48bf5SDavid du Colombier
10143ff48bf5SDavid du Colombier cb = parsecmd(buf, n);
10153ff48bf5SDavid du Colombier if(cb->nf < 2)
10163ff48bf5SDavid du Colombier r = -1;
10173ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "essid") == 0){
10183ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1],"default") == 0)
10193ff48bf5SDavid du Colombier p = "";
10203ff48bf5SDavid du Colombier else
10213ff48bf5SDavid du Colombier p = cb->f[1];
102215763c87SDavid du Colombier if(ctlr->ptype == WPTypeAdHoc){
10233ff48bf5SDavid du Colombier memset(ctlr->netname, 0, sizeof(ctlr->netname));
10243ff48bf5SDavid du Colombier strncpy(ctlr->netname, p, WNameLen);
10253ff48bf5SDavid du Colombier }
10263ff48bf5SDavid du Colombier else{
10273ff48bf5SDavid du Colombier memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
10283ff48bf5SDavid du Colombier strncpy(ctlr->wantname, p, WNameLen);
10293ff48bf5SDavid du Colombier }
10303ff48bf5SDavid du Colombier }
10313ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "station") == 0){
10323ff48bf5SDavid du Colombier memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
10333ff48bf5SDavid du Colombier strncpy(ctlr->nodename, cb->f[1], WNameLen);
10343ff48bf5SDavid du Colombier }
10353ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "channel") == 0){
10363ff48bf5SDavid du Colombier if((i = atoi(cb->f[1])) >= 1 && i <= 16)
10373ff48bf5SDavid du Colombier ctlr->chan = i;
10383ff48bf5SDavid du Colombier else
10393ff48bf5SDavid du Colombier r = -1;
10403ff48bf5SDavid du Colombier }
10413ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "mode") == 0){
10423ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1], "managed") == 0)
10433ff48bf5SDavid du Colombier ctlr->ptype = WPTypeManaged;
10443ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[1], "wds") == 0)
10453ff48bf5SDavid du Colombier ctlr->ptype = WPTypeWDS;
10463ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[1], "adhoc") == 0)
10473ff48bf5SDavid du Colombier ctlr->ptype = WPTypeAdHoc;
104807a38badSDavid du Colombier else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
10493ff48bf5SDavid du Colombier ctlr->ptype = i;
10503ff48bf5SDavid du Colombier else
10513ff48bf5SDavid du Colombier r = -1;
10523ff48bf5SDavid du Colombier }
10533ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "ibss") == 0){
10543ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1], "on") == 0)
10553ff48bf5SDavid du Colombier ctlr->createibss = 1;
10563ff48bf5SDavid du Colombier else
10573ff48bf5SDavid du Colombier ctlr->createibss = 0;
10583ff48bf5SDavid du Colombier }
10593ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "crypt") == 0){
10603ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1], "off") == 0)
10613ff48bf5SDavid du Colombier ctlr->crypt = 0;
10623ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
10633ff48bf5SDavid du Colombier ctlr->crypt = 1;
10643ff48bf5SDavid du Colombier else
10653ff48bf5SDavid du Colombier r = -1;
10663ff48bf5SDavid du Colombier }
10673ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "clear") == 0){
10683ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1], "on") == 0)
10693ff48bf5SDavid du Colombier ctlr->xclear = 0;
10703ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
10713ff48bf5SDavid du Colombier ctlr->xclear = 1;
10723ff48bf5SDavid du Colombier else
10733ff48bf5SDavid du Colombier r = -1;
10743ff48bf5SDavid du Colombier }
10753ff48bf5SDavid du Colombier else if(cistrncmp(cb->f[0], "key", 3) == 0){
10763ff48bf5SDavid du Colombier if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
10773ff48bf5SDavid du Colombier ctlr->txkey = i-1;
10787c881178SDavid du Colombier if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1]))
10797c881178SDavid du Colombier r = -1;
10803ff48bf5SDavid du Colombier }
10813ff48bf5SDavid du Colombier else
10823ff48bf5SDavid du Colombier r = -1;
10833ff48bf5SDavid du Colombier }
10843ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "txkey") == 0){
10853ff48bf5SDavid du Colombier if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
10863ff48bf5SDavid du Colombier ctlr->txkey = i-1;
10873ff48bf5SDavid du Colombier else
10883ff48bf5SDavid du Colombier r = -1;
10893ff48bf5SDavid du Colombier }
10903ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[0], "pm") == 0){
10913ff48bf5SDavid du Colombier if(cistrcmp(cb->f[1], "off") == 0)
10923ff48bf5SDavid du Colombier ctlr->pmena = 0;
10933ff48bf5SDavid du Colombier else if(cistrcmp(cb->f[1], "on") == 0){
10943ff48bf5SDavid du Colombier ctlr->pmena = 1;
10953ff48bf5SDavid du Colombier if(cb->nf == 3){
10963ff48bf5SDavid du Colombier i = atoi(cb->f[2]);
10973ff48bf5SDavid du Colombier // check range here? what are the units?
10983ff48bf5SDavid du Colombier ctlr->pmwait = i;
10993ff48bf5SDavid du Colombier }
11003ff48bf5SDavid du Colombier }
11013ff48bf5SDavid du Colombier else
11023ff48bf5SDavid du Colombier r = -1;
11033ff48bf5SDavid du Colombier }
11043ff48bf5SDavid du Colombier else
11053ff48bf5SDavid du Colombier r = -2;
11063ff48bf5SDavid du Colombier free(cb);
11073ff48bf5SDavid du Colombier
11083ff48bf5SDavid du Colombier return r;
11093ff48bf5SDavid du Colombier }
11103ff48bf5SDavid du Colombier
11113ff48bf5SDavid du Colombier long
w_ctl(Ether * ether,void * buf,long n)11123ff48bf5SDavid du Colombier w_ctl(Ether* ether, void* buf, long n)
11133ff48bf5SDavid du Colombier {
11143ff48bf5SDavid du Colombier Ctlr *ctlr;
11153ff48bf5SDavid du Colombier
11163ff48bf5SDavid du Colombier if((ctlr = ether->ctlr) == nil)
11173ff48bf5SDavid du Colombier error(Enonexist);
11183ff48bf5SDavid du Colombier if((ctlr->state & Attached) == 0)
11193ff48bf5SDavid du Colombier error(Eshutdown);
11203ff48bf5SDavid du Colombier
11213ff48bf5SDavid du Colombier ilock(ctlr);
11223ff48bf5SDavid du Colombier if(w_option(ctlr, buf, n)){
11233ff48bf5SDavid du Colombier iunlock(ctlr);
11243ff48bf5SDavid du Colombier error(Ebadctl);
11253ff48bf5SDavid du Colombier }
11263ff48bf5SDavid du Colombier if(ctlr->txbusy)
11273ff48bf5SDavid du Colombier w_txdone(ctlr, WTxErrEv);
11283ff48bf5SDavid du Colombier w_enable(ether);
11293ff48bf5SDavid du Colombier w_txstart(ether);
11303ff48bf5SDavid du Colombier iunlock(ctlr);
11313ff48bf5SDavid du Colombier
11323ff48bf5SDavid du Colombier return n;
11333ff48bf5SDavid du Colombier }
11343ff48bf5SDavid du Colombier
11353ff48bf5SDavid du Colombier void
w_transmit(Ether * ether)11363ff48bf5SDavid du Colombier w_transmit(Ether* ether)
11373ff48bf5SDavid du Colombier {
11383ff48bf5SDavid du Colombier Ctlr* ctlr = ether->ctlr;
11393ff48bf5SDavid du Colombier
11403ff48bf5SDavid du Colombier if(ctlr == 0)
11413ff48bf5SDavid du Colombier return;
11423ff48bf5SDavid du Colombier
11433ff48bf5SDavid du Colombier ilock(ctlr);
11443ff48bf5SDavid du Colombier ctlr->ntxrq++;
11453ff48bf5SDavid du Colombier w_txstart(ether);
11463ff48bf5SDavid du Colombier iunlock(ctlr);
11473ff48bf5SDavid du Colombier }
11483ff48bf5SDavid du Colombier
11493ff48bf5SDavid du Colombier void
w_promiscuous(void * arg,int on)11503ff48bf5SDavid du Colombier w_promiscuous(void* arg, int on)
11513ff48bf5SDavid du Colombier {
11523ff48bf5SDavid du Colombier Ether* ether = (Ether*)arg;
11533ff48bf5SDavid du Colombier Ctlr* ctlr = ether->ctlr;
11543ff48bf5SDavid du Colombier
11553ff48bf5SDavid du Colombier if(ctlr == nil)
11563ff48bf5SDavid du Colombier error("card not found");
11573ff48bf5SDavid du Colombier if((ctlr->state & Attached) == 0)
11583ff48bf5SDavid du Colombier error("card not attached");
11593ff48bf5SDavid du Colombier ilock(ctlr);
11603ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Prom, (on?1:0));
11613ff48bf5SDavid du Colombier iunlock(ctlr);
11623ff48bf5SDavid du Colombier }
11633ff48bf5SDavid du Colombier
11643ff48bf5SDavid du Colombier void
w_interrupt(Ureg *,void * arg)11653ff48bf5SDavid du Colombier w_interrupt(Ureg* ,void* arg)
11663ff48bf5SDavid du Colombier {
11673ff48bf5SDavid du Colombier Ether* ether = (Ether*) arg;
11683ff48bf5SDavid du Colombier Ctlr* ctlr = (Ctlr*) ether->ctlr;
11693ff48bf5SDavid du Colombier
11703ff48bf5SDavid du Colombier if(ctlr == 0)
11713ff48bf5SDavid du Colombier return;
11723ff48bf5SDavid du Colombier ilock(ctlr);
11733ff48bf5SDavid du Colombier ctlr->nints++;
11743ff48bf5SDavid du Colombier w_intr(ether);
11753ff48bf5SDavid du Colombier iunlock(ctlr);
11763ff48bf5SDavid du Colombier }
11773ff48bf5SDavid du Colombier
117898bee55eSDavid du Colombier static void
w_shutdown(Ether * ether)117998bee55eSDavid du Colombier w_shutdown(Ether* ether)
118098bee55eSDavid du Colombier {
118198bee55eSDavid du Colombier Ctlr *ctlr;
118298bee55eSDavid du Colombier
118398bee55eSDavid du Colombier ctlr = ether->ctlr;
118498bee55eSDavid du Colombier w_intdis(ctlr);
118598bee55eSDavid du Colombier if(w_cmd(ctlr,WCmdIni,0))
118698bee55eSDavid du Colombier iprint("#l%d: init failed\n", ether->ctlrno);
118798bee55eSDavid du Colombier w_intdis(ctlr);
118898bee55eSDavid du Colombier }
118998bee55eSDavid du Colombier
11903ff48bf5SDavid du Colombier int
wavelanreset(Ether * ether,Ctlr * ctlr)11913ff48bf5SDavid du Colombier wavelanreset(Ether* ether, Ctlr *ctlr)
11923ff48bf5SDavid du Colombier {
11933ff48bf5SDavid du Colombier Wltv ltv;
11943ff48bf5SDavid du Colombier
11953ff48bf5SDavid du Colombier iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
11963ff48bf5SDavid du Colombier w_intdis(ctlr);
11973ff48bf5SDavid du Colombier if(w_cmd(ctlr,WCmdIni,0)){
11983ff48bf5SDavid du Colombier iprint("#l%d: init failed\n", ether->ctlrno);
11993ff48bf5SDavid du Colombier return -1;
12003ff48bf5SDavid du Colombier }
12013ff48bf5SDavid du Colombier w_intdis(ctlr);
12023ff48bf5SDavid du Colombier ltv_outs(ctlr, WTyp_Tick, 8);
12033ff48bf5SDavid du Colombier
12043ff48bf5SDavid du Colombier ctlr->chan = 0;
12053ff48bf5SDavid du Colombier ctlr->ptype = WDfltPType;
12063ff48bf5SDavid du Colombier ctlr->txkey = 0;
12073ff48bf5SDavid du Colombier ctlr->createibss = 0;
12083ff48bf5SDavid du Colombier ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
12093ff48bf5SDavid du Colombier ctlr->keys.type = WTyp_Keys;
12103ff48bf5SDavid du Colombier if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
12113ff48bf5SDavid du Colombier ctlr->crypt = 1;
12123ff48bf5SDavid du Colombier *ctlr->netname = *ctlr->wantname = 0;
12133ff48bf5SDavid du Colombier strcpy(ctlr->nodename, "Plan 9 STA");
12143ff48bf5SDavid du Colombier
12153ff48bf5SDavid du Colombier ctlr->netname[WNameLen-1] = 0;
12163ff48bf5SDavid du Colombier ctlr->wantname[WNameLen-1] = 0;
12173ff48bf5SDavid du Colombier ctlr->nodename[WNameLen-1] =0;
12183ff48bf5SDavid du Colombier
12193ff48bf5SDavid du Colombier ltv.type = WTyp_Mac;
12203ff48bf5SDavid du Colombier ltv.len = 4;
12213ff48bf5SDavid du Colombier if(w_inltv(ctlr, <v)){
12223ff48bf5SDavid du Colombier iprint("#l%d: unable to read mac addr\n",
12233ff48bf5SDavid du Colombier ether->ctlrno);
12243ff48bf5SDavid du Colombier return -1;
12253ff48bf5SDavid du Colombier }
12263ff48bf5SDavid du Colombier memmove(ether->ea, ltv.addr, Eaddrlen);
12273ff48bf5SDavid du Colombier
12283ff48bf5SDavid du Colombier if(ctlr->chan == 0)
12293ff48bf5SDavid du Colombier ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
12303ff48bf5SDavid du Colombier ctlr->apdensity = WDfltApDens;
12313ff48bf5SDavid du Colombier ctlr->rtsthres = WDfltRtsThres;
12323ff48bf5SDavid du Colombier ctlr->txrate = WDfltTxRate;
12333ff48bf5SDavid du Colombier ctlr->maxlen = WMaxLen;
12343ff48bf5SDavid du Colombier ctlr->pmena = 0;
12353ff48bf5SDavid du Colombier ctlr->pmwait = 100;
12363ff48bf5SDavid du Colombier ctlr->signal = 1;
12373ff48bf5SDavid du Colombier ctlr->noise = 1;
12383ff48bf5SDavid du Colombier ctlr->state |= Power;
12393ff48bf5SDavid du Colombier
12403ff48bf5SDavid du Colombier // free old Ctlr struct if resetting after suspend
12413ff48bf5SDavid du Colombier if(ether->ctlr && ether->ctlr != ctlr)
12423ff48bf5SDavid du Colombier free(ether->ctlr);
12433ff48bf5SDavid du Colombier
12443ff48bf5SDavid du Colombier // link to ether
12453ff48bf5SDavid du Colombier ether->ctlr = ctlr;
12463ff48bf5SDavid du Colombier ether->mbps = 10;
12473ff48bf5SDavid du Colombier ether->attach = w_attach;
12483ff48bf5SDavid du Colombier ether->detach = w_detach;
12493ff48bf5SDavid du Colombier ether->interrupt = w_interrupt;
12503ff48bf5SDavid du Colombier ether->transmit = w_transmit;
12513ff48bf5SDavid du Colombier ether->ifstat = w_ifstat;
12523ff48bf5SDavid du Colombier ether->ctl = w_ctl;
12533ff48bf5SDavid du Colombier ether->power = w_power;
12543ff48bf5SDavid du Colombier ether->promiscuous = w_promiscuous;
12553ff48bf5SDavid du Colombier ether->multicast = w_multicast;
125698bee55eSDavid du Colombier ether->shutdown = w_shutdown;
125715763c87SDavid du Colombier ether->scanbs = w_scanbs;
12583ff48bf5SDavid du Colombier ether->arg = ether;
12593ff48bf5SDavid du Colombier
1260fb7f0c93SDavid du Colombier DEBUG("#l%d: irq %d port %lx type %s",
12613ff48bf5SDavid du Colombier ether->ctlrno, ether->irq, ether->port, ether->type);
12626063170eSDavid du Colombier DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n",
12633ff48bf5SDavid du Colombier ether->ea[0], ether->ea[1], ether->ea[2],
12643ff48bf5SDavid du Colombier ether->ea[3], ether->ea[4], ether->ea[5]);
12653ff48bf5SDavid du Colombier
12663ff48bf5SDavid du Colombier return 0;
12673ff48bf5SDavid du Colombier }
12683ff48bf5SDavid du Colombier
12693ff48bf5SDavid du Colombier char* wavenames[] = {
12703ff48bf5SDavid du Colombier "WaveLAN/IEEE",
12713ff48bf5SDavid du Colombier "TrueMobile 1150",
12723ff48bf5SDavid du Colombier "Instant Wireless ; Network PC CARD",
1273d9306527SDavid du Colombier "Instant Wireless Network PC Card",
12743ff48bf5SDavid du Colombier "Avaya Wireless PC Card",
12753ff48bf5SDavid du Colombier "AirLancer MC-11",
1276e059317eSDavid du Colombier "INTERSIL;HFA384x/IEEE;Version 01.02;",
12773ff48bf5SDavid du Colombier nil,
12783ff48bf5SDavid du Colombier };
1279