]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/wavelan.c
perms
[plan9front.git] / sys / src / 9 / pc / wavelan.c
1 /*
2         Lucent Wavelan IEEE 802.11 pcmcia.
3         There is almost no documentation for the card.
4         the driver is done using both the FreeBSD, Linux and
5         original Plan 9 drivers as `documentation'.
6
7         Has been used with the card plugged in during all up time.
8         no cards removals/insertions yet.
9
10         For known BUGS see the comments below. Besides,
11         the driver keeps interrupts disabled for just too
12         long. When it gets robust, locks should be revisited.
13
14         BUGS: check endian, alignment and mem/io issues;
15               receive watchdog interrupts.
16         TODO: automatic power management;
17               multicast filtering;
18               improve locking.
19  */
20 #include "u.h"
21 #include "../port/lib.h"
22 #include "mem.h"
23 #include "dat.h"
24 #include "fns.h"
25 #include "io.h"
26 #include "../port/error.h"
27 #include "../port/netif.h"
28 #include "etherif.h"
29 #include "wavelan.h"
30
31 enum
32 {
33         MSperTick=      50,     /* ms between ticks of kproc */
34 };
35
36 /*
37  * When we're using a PCI device and memory-mapped I/O, 
38  * the registers are spaced out as though each takes 32 bits,
39  * even though they are only 16-bit registers.  Thus, 
40  * ctlr->mmb[reg] is the right way to access register reg,
41  * even though a priori you'd expect to use ctlr->mmb[reg/2].
42  */
43 void
44 csr_outs(Ctlr *ctlr, int reg, ushort arg)
45 {
46         if(ctlr->mmb)
47                 ctlr->mmb[reg] = arg;
48         else
49                 outs(ctlr->iob+reg, arg);
50 }
51
52 ushort
53 csr_ins(Ctlr *ctlr, int reg)
54 {
55         if(ctlr->mmb)
56                 return ctlr->mmb[reg];
57         else
58                 return ins(ctlr->iob+reg);
59 }
60
61 static void
62 csr_ack(Ctlr *ctlr, int ev)
63 {
64         csr_outs(ctlr, WR_EvAck, ev);
65 }
66
67 static void
68 csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
69 {
70         ushort *rp, *wp;
71
72         if(ctlr->mmb){
73                 rp = &ctlr->mmb[reg];
74                 wp = dat;
75                 while(ndat-- > 0)
76                         *wp++ = *rp;
77         }else
78                 inss(ctlr->iob+reg, dat, ndat);
79 }
80
81 static void
82 csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
83 {
84         ushort *rp, *wp;
85
86         if(ctlr->mmb){
87                 rp = dat;
88                 wp = &ctlr->mmb[reg];
89                 while(ndat-- > 0)
90                         *wp = *rp++;
91         }else
92                 outss(ctlr->iob+reg, dat, ndat);
93 }
94
95 // w_... routines do not ilock the Ctlr and should
96 // be called locked.
97
98 void
99 w_intdis(Ctlr* ctlr)
100 {
101         csr_outs(ctlr, WR_IntEna, 0);
102         csr_ack(ctlr, 0xffff);
103 }
104
105 static void
106 w_intena(Ctlr* ctlr)
107 {
108         csr_outs(ctlr, WR_IntEna, WEvs);
109 }
110
111 int
112 w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
113 {
114         int i, rc;
115
116         for(i=0; i<WTmOut; i++)
117                 if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
118                         break;
119         if(i==WTmOut){
120                 print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
121                 return -1;
122         }
123
124         csr_outs(ctlr, WR_Parm0, arg);
125         csr_outs(ctlr, WR_Cmd, cmd);
126
127         for(i=0; i<WTmOut; i++)
128                 if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
129                         break;
130         if(i==WTmOut){
131                 /*
132                  * WCmdIni can take a really long time.
133                  */
134                 enum { IniTmOut = 2000 };
135                 for(i=0; i<IniTmOut; i++){
136                         if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
137                                 break;
138                         microdelay(100);
139                 }
140                 if(i < IniTmOut)
141                         if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
142                 if(i == IniTmOut){
143                         print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
144                         return -1;
145                 }
146         }
147         rc = csr_ins(ctlr, WR_Sts);
148         csr_ack(ctlr, WCmdEv);
149
150         if((rc&WCmdMsk) != (cmd&WCmdMsk)){
151                 print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
152                 return -1;
153         }
154         if(rc&WResSts){
155                 /*
156                  * Don't print; this happens on every WCmdAccWr for some reason.
157                  */
158                 if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
159                 return -1;
160         }
161         return 0;
162 }
163
164 static int
165 w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
166 {
167         int i, rc;
168         static ushort sel[] = { WR_Sel0, WR_Sel1 };
169         static ushort off[] = { WR_Off0, WR_Off1 };
170
171         if(chan != 0 && chan != 1)
172                 panic("wavelan: bad chan\n");
173         csr_outs(ctlr, sel[chan], id);
174         csr_outs(ctlr, off[chan], offset);
175         for (i=0; i<WTmOut; i++){
176                 rc = csr_ins(ctlr, off[chan]);
177                 if((rc & (WBusyOff|WErrOff)) == 0)
178                         return 0;
179         }
180         return -1;
181 }
182
183 int
184 w_inltv(Ctlr* ctlr, Wltv* ltv)
185 {
186         int len;
187         ushort code;
188
189         if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
190                 DEBUG("wavelan: access read failed\n");
191                 return -1;
192         }
193         if(w_seek(ctlr,ltv->type,0,1)){
194                 DEBUG("wavelan: seek failed\n");
195                 return -1;
196         }
197         len = csr_ins(ctlr, WR_Data1);
198         if(len > ltv->len)
199                 return -1;
200         ltv->len = len;
201         if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
202                 USED(code);
203                 DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
204                 return -1;
205         }
206         if(ltv->len > 0)
207                 csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);
208
209         return 0;
210 }
211
212 static void
213 w_outltv(Ctlr* ctlr, Wltv* ltv)
214 {
215         if(w_seek(ctlr,ltv->type, 0, 1))
216                 return;
217         csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
218         w_cmd(ctlr, WCmdAccWr, ltv->type);
219 }
220
221 void
222 ltv_outs(Ctlr* ctlr, int type, ushort val)
223 {
224         Wltv ltv;
225
226         ltv.len = 2;
227         ltv.type = type;
228         ltv.val = val;
229         w_outltv(ctlr, &ltv);
230 }
231
232 int
233 ltv_ins(Ctlr* ctlr, int type)
234 {
235         Wltv ltv;
236
237         ltv.len = 2;
238         ltv.type = type;
239         ltv.val = 0;
240         if(w_inltv(ctlr, &ltv))
241                 return -1;
242         return ltv.val;
243 }
244
245 static void
246 ltv_outstr(Ctlr* ctlr, int type, char* val)
247 {
248         Wltv ltv;
249         int len;
250
251         len = strlen(val);
252         if(len > sizeof(ltv.s))
253                 len = sizeof(ltv.s);
254         memset(&ltv, 0, sizeof(ltv));
255         ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
256         ltv.type = type;
257
258 //      This should be ltv.slen = len; according to Axel Belinfante
259         ltv.slen = len; 
260
261         strncpy(ltv.s, val, len);
262         w_outltv(ctlr, &ltv);
263 }
264
265 static char Unkname[] = "who knows";
266 static char Nilname[] = "card does not tell";
267
268 static char*
269 ltv_inname(Ctlr* ctlr, int type)
270 {
271         static Wltv ltv;
272         int len;
273
274         memset(&ltv,0,sizeof(ltv));
275         ltv.len = WNameLen/2+2;
276         ltv.type = type;
277         if(w_inltv(ctlr, &ltv))
278                 return Unkname;
279         len = ltv.slen;
280         if(len == 0 || ltv.s[0] == 0)
281                 return Nilname;
282         if(len >= sizeof ltv.s)
283                 len = sizeof ltv.s - 1;
284         ltv.s[len] = '\0';
285         return ltv.s;
286 }
287
288 static int
289 w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
290 {
291         if(w_seek(ctlr, type, off, 1)){
292                 DEBUG("wavelan: w_read: seek failed");
293                 return 0;
294         }
295         csr_inss(ctlr, WR_Data1, buf, len/2);
296
297         return len;
298 }
299
300 static int
301 w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
302 {
303         if(w_seek(ctlr, type, off, 0)){
304                 DEBUG("wavelan: w_write: seek failed\n");
305                 return 0;
306         }
307
308         csr_outss(ctlr, WR_Data0, buf, len/2);
309         csr_outs(ctlr, WR_Data0, 0xdead);
310         csr_outs(ctlr, WR_Data0, 0xbeef);
311         if(w_seek(ctlr, type, off + len, 0)){
312                 DEBUG("wavelan: write seek failed\n");
313                 return 0;
314         }
315         if(csr_ins(ctlr, WR_Data0) == 0xdead && csr_ins(ctlr, WR_Data0) == 0xbeef)
316                 return len;
317
318         DEBUG("wavelan: Hermes bug byte.\n");
319         return 0;
320 }
321
322 static int
323 w_alloc(Ctlr* ctlr, int len)
324 {
325         int rc;
326         int i,j;
327
328         if(w_cmd(ctlr, WCmdMalloc, len)==0)
329                 for (i = 0; i<WTmOut; i++)
330                         if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
331                                 csr_ack(ctlr, WAllocEv);
332                                 rc=csr_ins(ctlr, WR_Alloc);
333                                 if(w_seek(ctlr, rc, 0, 0))
334                                         return -1;
335                                 len = len/2;
336                                 for (j=0; j<len; j++)
337                                         csr_outs(ctlr, WR_Data0, 0);
338                                 return rc;
339                         }
340         return -1;
341 }
342
343 static int
344 w_enable(Ether* ether)
345 {
346         Wltv ltv;
347         Ctlr* ctlr = (Ctlr*) ether->ctlr;
348
349         if(!ctlr)
350                 return -1;
351
352         w_intdis(ctlr);
353         w_cmd(ctlr, WCmdDis, 0);
354         w_intdis(ctlr);
355         if(w_cmd(ctlr, WCmdIni, 0))
356                 return -1;
357         w_intdis(ctlr);
358
359         ltv_outs(ctlr, WTyp_Tick, 8);
360         ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
361         ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
362         ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
363         ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
364         ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
365         ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
366         ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
367         ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
368         if(*ctlr->netname)
369                 ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
370         if(*ctlr->wantname)
371                 ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
372         ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
373         if(*ctlr->nodename)
374                 ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
375         ltv.len = 4;
376         ltv.type = WTyp_Mac;
377         memmove(ltv.addr, ether->ea, Eaddrlen);
378         w_outltv(ctlr, &ltv);
379
380         ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
381
382         if(ctlr->hascrypt && ctlr->crypt){
383                 ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
384                 ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
385                 w_outltv(ctlr, &ctlr->keys);
386                 ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
387         }
388
389         // BUG: set multicast addresses
390
391         if(w_cmd(ctlr, WCmdEna, 0)){
392                 DEBUG("wavelan: Enable failed");
393                 return -1;
394         }
395         ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
396         ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
397         if(ctlr->txdid == -1 || ctlr->txmid == -1)
398                 DEBUG("wavelan: alloc failed");
399         ctlr->txbusy = 0;
400         w_intena(ctlr);
401         return 0;
402 }
403
404 static void
405 w_rxdone(Ether* ether)
406 {
407         Ctlr* ctlr = (Ctlr*) ether->ctlr;
408         int len, sp;
409         WFrame f;
410         Block* bp=0;
411         Etherpkt* ep;
412
413         sp = csr_ins(ctlr, WR_RXId);
414         len = w_read(ctlr, sp, 0, &f, sizeof(f));
415         if(len == 0){
416                 DEBUG("wavelan: read frame error\n");
417                 goto rxerror;
418         }
419         if(f.sts&WF_Err){
420                 goto rxerror;
421         }
422         switch(f.sts){
423         case WF_1042:
424         case WF_Tunnel:
425         case WF_WMP:
426                 len = f.dlen + WSnapHdrLen;
427                 bp = iallocb(ETHERHDRSIZE + len + 2);
428                 if(!bp)
429                         goto rxerror;
430                 ep = (Etherpkt*) bp->wp;
431                 memmove(ep->d, f.addr1, Eaddrlen);
432                 memmove(ep->s, f.addr2, Eaddrlen);
433                 memmove(ep->type,&f.type,2);
434                 bp->wp += ETHERHDRSIZE;
435                 if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
436                         DEBUG("wavelan: read 802.11 error\n");
437                         goto rxerror;
438                 }
439                 bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
440                 break;
441         default:
442                 len = ETHERHDRSIZE + f.dlen + 2;
443                 bp = iallocb(len);
444                 if(!bp)
445                         goto rxerror;
446                 if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
447                         DEBUG("wavelan: read 800.3 error\n");
448                         goto rxerror;
449                 }
450                 bp->wp += len;
451         }
452
453         ctlr->nrx++;
454         etheriq(ether,bp,1);
455         ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
456         ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
457         return;
458
459 rxerror:
460         freeb(bp);
461         ctlr->nrxerr++;
462 }
463
464 static void
465 w_txstart(Ether* ether)
466 {
467         Etherpkt *pkt;
468         Ctlr *ctlr;
469         Block *bp;
470         int len, off;
471
472         if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
473                 return;
474
475         if((bp = qget(ether->oq)) == nil)
476                 return;
477         pkt = (Etherpkt*)bp->rp;
478
479         //
480         // If the packet header type field is > 1500 it is an IP or
481         // ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
482         //
483         memset(&ctlr->txf, 0, sizeof(ctlr->txf));
484         if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
485                 ctlr->txf.framectl = WF_Data;
486                 memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
487                 memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
488                 memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
489                 memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
490                 memmove(&ctlr->txf.type, pkt->type, 2);
491                 bp->rp += ETHERHDRSIZE;
492                 len = BLEN(bp);
493                 off = WF_802_11_Off;
494                 ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
495                 hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
496                 hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
497                 hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
498         }
499         else{
500                 len = BLEN(bp);
501                 off = WF_802_3_Off;
502                 ctlr->txf.dlen = len;
503         }
504         w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
505         w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
506
507         if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
508                 DEBUG("wavelan: transmit failed\n");
509                 ctlr->ntxerr++;
510         }
511         else{
512                 ctlr->txbusy = 1;
513                 ctlr->txtmout = 2;
514         }
515         freeb(bp);
516 }
517
518 static void
519 w_txdone(Ctlr* ctlr, int sts)
520 {
521         ctlr->txbusy = 0;
522         ctlr->txtmout = 0;
523         if(sts & WTxErrEv)
524                 ctlr->ntxerr++;
525         else
526                 ctlr->ntx++;
527 }
528
529 /* save the stats info in the ctlr struct */
530 static void
531 w_stats(Ctlr* ctlr, int len)
532 {
533         int i, rc;
534         ulong* p = (ulong*)&ctlr->WStats;
535         ulong* pend = (ulong*)&ctlr->end;
536
537         for (i = 0; i < len && p < pend; i++){
538                 rc = csr_ins(ctlr, WR_Data1);
539                 if(rc > 0xf000)
540                         rc = ~rc & 0xffff;
541                 p[i] += rc;
542         }
543 }
544
545 /* send the base station scan info to any readers */
546 static void
547 w_scaninfo(Ether* ether, Ctlr *ctlr, int len)
548 {
549         int i, j;
550         Netfile **ep, *f, **fp;
551         Block *bp;
552         WScan *wsp;
553         ushort *scanbuf;
554
555         scanbuf = malloc(len*2);
556         if(scanbuf == nil)
557                 return;
558         
559         for (i = 0; i < len ; i++)
560                 scanbuf[i] = csr_ins(ctlr, WR_Data1);
561
562         /* calculate number of samples */
563         len /= 25;
564         if(len == 0)
565                 goto out;
566
567         i = ether->scan;
568         ep = &ether->f[Ntypes];
569         for(fp = ether->f; fp < ep && i > 0; fp++){
570                 f = *fp;
571                 if(f == nil || f->scan == 0)
572                         continue;
573
574                 bp = iallocb(100*len);
575                 if(bp == nil)
576                         break;
577                 for(j = 0; j < len; j++){
578                         wsp = (WScan*)(&scanbuf[j*25]);
579                         if(wsp->ssid_len > 32)
580                                 wsp->ssid_len = 32;
581                         bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
582                                 "ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n",
583                                 wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal,
584                                 wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":"");
585                 }
586                 qpass(f->in, bp);
587                 i--;
588         }
589 out:
590         free(scanbuf);
591 }
592
593 static int
594 w_info(Ether *ether, Ctlr* ctlr)
595 {
596         int sp;
597         Wltv ltv;
598
599         sp = csr_ins(ctlr, WR_InfoId);
600         ltv.len = ltv.type = 0;
601         w_read(ctlr, sp, 0, &ltv, 4);
602         ltv.len--;
603         switch(ltv.type){
604         case WTyp_Stats:
605                 w_stats(ctlr, ltv.len);
606                 return 0;
607         case WTyp_Scan:
608                 w_scaninfo(ether, ctlr, ltv.len);
609                 return 0;
610         }
611         return -1;
612 }
613
614 /* set scanning interval */
615 static void
616 w_scanbs(void *a, uint secs)
617 {
618         Ether *ether = a;
619         Ctlr* ctlr = (Ctlr*) ether->ctlr;
620
621         ctlr->scanticks = secs*(1000/MSperTick);
622 }
623
624 static void
625 w_intr(Ether *ether)
626 {
627         int rc, txid;
628         Ctlr* ctlr = (Ctlr*) ether->ctlr;
629
630         if((ctlr->state & Power) == 0)
631                 return;
632
633         if((ctlr->state & Attached) == 0){
634                 csr_ack(ctlr, 0xffff);
635                 csr_outs(ctlr, WR_IntEna, 0);
636                 return;
637         }
638
639         rc = csr_ins(ctlr, WR_EvSts);
640         csr_ack(ctlr, ~WEvs);   // Not interested in them
641         if(rc & WRXEv){
642                 w_rxdone(ether);
643                 csr_ack(ctlr, WRXEv);
644         }
645         if(rc & WTXEv){
646                 w_txdone(ctlr, rc);
647                 csr_ack(ctlr, WTXEv);
648         }
649         if(rc & WAllocEv){
650                 ctlr->nalloc++;
651                 txid = csr_ins(ctlr, WR_Alloc);
652                 csr_ack(ctlr, WAllocEv);
653                 if(txid == ctlr->txdid){
654                         if((rc & WTXEv) == 0)
655                                 w_txdone(ctlr, rc);
656                 }
657         }
658         if(rc & WInfoEv){
659                 ctlr->ninfo++;
660                 w_info(ether, ctlr);
661                 csr_ack(ctlr, WInfoEv);
662         }
663         if(rc & WTxErrEv){
664                 w_txdone(ctlr, rc);
665                 csr_ack(ctlr, WTxErrEv);
666         }
667         if(rc & WIDropEv){
668                 ctlr->nidrop++;
669                 csr_ack(ctlr, WIDropEv);
670         }
671         w_txstart(ether);
672 }
673
674 // Watcher to ensure that the card still works properly and
675 // to request WStats updates once a minute.
676 // BUG: it runs much more often, see the comment below.
677
678 static void
679 w_timer(void* arg)
680 {
681         Ether* ether = (Ether*) arg;
682         Ctlr* ctlr = (Ctlr*)ether->ctlr;
683
684         ctlr->timerproc = up;
685         for(;;){
686                 tsleep(&up->sleep, return0, 0, MSperTick);
687                 ctlr = (Ctlr*)ether->ctlr;
688                 if(ctlr == 0)
689                         break;
690                 if((ctlr->state & (Attached|Power)) != (Attached|Power))
691                         continue;
692                 ctlr->ticks++;
693
694                 ilock(ctlr);
695
696                 // Seems that the card gets frames BUT does
697                 // not send the interrupt; this is a problem because
698                 // I suspect it runs out of receive buffers and
699                 // stops receiving until a transmit watchdog
700                 // reenables the card.
701                 // The problem is serious because it leads to
702                 // poor rtts.
703                 // This can be seen clearly by commenting out
704                 // the next if and doing a ping: it will stop
705                 // receiving (although the icmp replies are being
706                 // issued from the remote) after a few seconds.
707                 // Of course this `bug' could be because I'm reading
708                 // the card frames in the wrong way; due to the
709                 // lack of documentation I cannot know.
710
711                 if(csr_ins(ctlr, WR_EvSts)&WEvs){
712                         ctlr->tickintr++;
713                         w_intr(ether);
714                 }
715
716                 if((ctlr->ticks % 10) == 0) {
717                         if(ctlr->txtmout && --ctlr->txtmout == 0){
718                                 ctlr->nwatchdogs++;
719                                 w_txdone(ctlr, WTxErrEv);
720                                 if(w_enable(ether)){
721                                         DEBUG("wavelan: wdog enable failed\n");
722                                 }
723                                 w_txstart(ether);
724                         }
725                         if((ctlr->ticks % 120) == 0)
726                         if(ctlr->txbusy == 0)
727                                 w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
728                         if(ctlr->scanticks > 0)
729                         if((ctlr->ticks % ctlr->scanticks) == 0)
730                         if(ctlr->txbusy == 0)
731                                 w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
732                 }
733                 iunlock(ctlr);
734         }
735         pexit("terminated", 0);
736 }
737
738 void
739 w_multicast(void *ether, uchar*, int add)
740 {
741         /* BUG: use controller's multicast filter */
742         if (add)
743                 w_promiscuous(ether, 1);
744 }
745
746 void
747 w_attach(Ether* ether)
748 {
749         Ctlr* ctlr;
750         char name[64];
751         int rc;
752
753         if(ether->ctlr == 0)
754                 return;
755
756         snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
757         ctlr = (Ctlr*) ether->ctlr;
758         if((ctlr->state & Attached) == 0){
759                 ilock(ctlr);
760                 rc = w_enable(ether);
761                 iunlock(ctlr);
762                 if(rc == 0){
763                         ctlr->state |= Attached;
764                         kproc(name, w_timer, ether);
765                 } else
766                         print("#l%d: enable failed\n",ether->ctlrno);
767         }
768 }
769
770 void
771 w_detach(Ether* ether)
772 {
773         Ctlr* ctlr;
774         char name[64];
775
776         if(ether->ctlr == nil)
777                 return;
778
779         snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
780         ctlr = (Ctlr*) ether->ctlr;
781         if(ctlr->state & Attached){
782                 ilock(ctlr);
783                 w_intdis(ctlr);
784                 if(ctlr->timerproc){
785                         if(!postnote(ctlr->timerproc, 1, "kill", NExit))
786                                 print("timerproc note not posted\n");
787                         print("w_detach, killing 0x%p\n", ctlr->timerproc);
788                 }
789                 ctlr->state &= ~Attached;
790                 iunlock(ctlr);
791         }
792         ether->ctlr = nil;
793 }
794
795 void
796 w_power(Ether* ether, int on)
797 {
798         Ctlr *ctlr;
799
800         ctlr = (Ctlr*) ether->ctlr;
801         ilock(ctlr);
802 iprint("w_power %d\n", on);
803         if(on){
804                 if((ctlr->state & Power) == 0){
805                         if (wavelanreset(ether, ctlr) < 0){
806                                 iprint("w_power: reset failed\n");
807                                 iunlock(ctlr);
808                                 w_detach(ether);
809                                 free(ctlr);
810                                 return;
811                         }
812                         if(ctlr->state & Attached)
813                                 w_enable(ether);
814                         ctlr->state |= Power;
815                 }
816         }else{
817                 if(ctlr->state & Power){
818                         if(ctlr->state & Attached)
819                                 w_intdis(ctlr);
820                         ctlr->state &= ~Power;
821                 }
822         }
823         iunlock(ctlr);
824 }
825
826 #define PRINTSTAT(fmt,val)      l += snprint(p+l, READSTR-l, (fmt), (val))
827 #define PRINTSTR(fmt)           l += snprint(p+l, READSTR-l, (fmt))
828
829 long
830 w_ifstat(Ether* ether, void* a, long n, ulong offset)
831 {
832         Ctlr *ctlr = (Ctlr*) ether->ctlr;
833         char *k, *p;
834         int i, l, txid;
835
836         ether->oerrs = ctlr->ntxerr;
837         ether->crcs = ctlr->nrxfcserr;
838         ether->frames = 0;
839         ether->buffs = ctlr->nrxdropnobuf;
840         ether->overflows = 0;
841
842         //
843         // Offset must be zero or there's a possibility the
844         // new data won't match the previous read.
845         //
846         if(n == 0 || offset != 0)
847                 return 0;
848
849         p = malloc(READSTR);
850         l = 0;
851
852         PRINTSTAT("Signal: %d\n", ctlr->signal-149);
853         PRINTSTAT("Noise: %d\n", ctlr->noise-149);
854         PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
855         PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
856         PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
857         PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
858         PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
859         PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
860         PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
861         PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
862         PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
863         PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
864         PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
865         PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
866         PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
867         PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
868         k = ((ctlr->state & Attached) ? "attached" : "not attached");
869         PRINTSTAT("Card %s", k);
870         k = ((ctlr->state & Power) ? "on" : "off");
871         PRINTSTAT(", power %s", k);
872         k = ((ctlr->txbusy)? ", txbusy" : "");
873         PRINTSTAT("%s\n", k);
874
875         if(ctlr->hascrypt){
876                 PRINTSTR("Keys: ");
877                 for (i = 0; i < WNKeys; i++){
878                         if(ctlr->keys.keys[i].len == 0)
879                                 PRINTSTR("none ");
880                         else if(SEEKEYS == 0)
881                                 PRINTSTR("set ");
882                         else
883                                 PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
884                 }
885                 PRINTSTR("\n");
886         }
887
888         // real card stats
889         ilock(ctlr);
890         PRINTSTR("\nCard stats: \n");
891         PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
892         PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
893         i = ltv_ins(ctlr, WTyp_Ptype);
894         PRINTSTAT("Port type: %d\n", i);
895         PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
896         PRINTSTAT("Current Transmit rate: %d\n",
897                 ltv_ins(ctlr, WTyp_CurTxRate));
898         PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
899         PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
900         PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
901         if(i == WPTypeAdHoc)
902                 PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
903         else {
904                 Wltv ltv;
905                 PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
906                 ltv.type = WTyp_BaseID;
907                 ltv.len = 4;
908                 if(w_inltv(ctlr, &ltv))
909                         print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
910                 l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
911                         ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
912         }
913         PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
914         PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
915         if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
916                 PRINTSTR("WEP: not supported\n");
917         else {
918                 if(ltv_ins(ctlr, WTyp_Crypt) == 0)
919                         PRINTSTR("WEP: disabled\n");
920                 else{
921                         PRINTSTR("WEP: enabled\n");
922                         k = ((ctlr->xclear)? "excluded": "included");
923                         PRINTSTAT("Clear packets: %s\n", k);
924                         txid = ltv_ins(ctlr, WTyp_TxKey);
925                         PRINTSTAT("Transmit key id: %d\n", txid);
926                 }
927         }
928         iunlock(ctlr);
929
930         PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
931         PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
932         PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
933         PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
934         PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
935         PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
936         PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
937         PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
938         PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
939         PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
940         PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
941         PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
942         PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
943         PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
944         PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
945         PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
946         PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
947         PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
948         PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
949         PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
950         PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
951         USED(l);
952         n = readstr(offset, a, n, p);
953         free(p);
954         return n;
955 }
956 #undef PRINTSTR
957 #undef PRINTSTAT
958
959 static int
960 parsekey(WKey* key, char* a) 
961 {
962         int i, k, len, n;
963         char buf[WMaxKeyLen];
964
965         len = strlen(a);
966         if(len == WMinKeyLen || len == WMaxKeyLen){
967                 memset(key->dat, 0, sizeof(key->dat));
968                 memmove(key->dat, a, len);
969                 key->len = len;
970
971                 return 0;
972         }
973         else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
974                 k = 0;
975                 for(i = 0; i < len; i++){
976                         if(*a >= '0' && *a <= '9')
977                                 n = *a++ - '0';
978                         else if(*a >= 'a' && *a <= 'f')
979                                 n = *a++ - 'a' + 10;
980                         else if(*a >= 'A' && *a <= 'F')
981                                 n = *a++ - 'A' + 10;
982                         else
983                                 return -1;
984         
985                         if(i & 1){
986                                 buf[k] |= n;
987                                 k++;
988                         }
989                         else
990                                 buf[k] = n<<4;
991                 }
992
993                 memset(key->dat, 0, sizeof(key->dat));
994                 memmove(key->dat, buf, k);
995                 key->len = k;
996
997                 return 0;
998         }
999
1000         return -1;
1001 }
1002
1003 int
1004 w_option(Ctlr* ctlr, char* buf, long n)
1005 {
1006         char *p;
1007         int i, r;
1008         Cmdbuf *cb;
1009
1010         r = 0;
1011
1012         cb = parsecmd(buf, n);
1013         if(cb->nf < 2)
1014                 r = -1;
1015         else if(cistrcmp(cb->f[0], "essid") == 0){
1016                 if(cistrcmp(cb->f[1],"default") == 0)
1017                         p = "";
1018                 else
1019                         p = cb->f[1];
1020                 if(ctlr->ptype == WPTypeAdHoc){
1021                         memset(ctlr->netname, 0, sizeof(ctlr->netname));
1022                         strncpy(ctlr->netname, p, WNameLen);
1023                 }
1024                 else{
1025                         memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
1026                         strncpy(ctlr->wantname, p, WNameLen);
1027                 }
1028         }
1029         else if(cistrcmp(cb->f[0], "station") == 0){
1030                 memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
1031                 strncpy(ctlr->nodename, cb->f[1], WNameLen);
1032         }
1033         else if(cistrcmp(cb->f[0], "channel") == 0){
1034                 if((i = atoi(cb->f[1])) >= 1 && i <= 16)
1035                         ctlr->chan = i;
1036                 else
1037                         r = -1;
1038         }
1039         else if(cistrcmp(cb->f[0], "mode") == 0){
1040                 if(cistrcmp(cb->f[1], "managed") == 0)
1041                         ctlr->ptype = WPTypeManaged;
1042                 else if(cistrcmp(cb->f[1], "wds") == 0)
1043                         ctlr->ptype = WPTypeWDS;
1044                 else if(cistrcmp(cb->f[1], "adhoc") == 0)
1045                         ctlr->ptype = WPTypeAdHoc;
1046                 else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
1047                         ctlr->ptype = i;
1048                 else
1049                         r = -1;
1050         }
1051         else if(cistrcmp(cb->f[0], "ibss") == 0){
1052                 if(cistrcmp(cb->f[1], "on") == 0)
1053                         ctlr->createibss = 1;
1054                 else
1055                         ctlr->createibss = 0;
1056         }
1057         else if(cistrcmp(cb->f[0], "crypt") == 0){
1058                 if(cistrcmp(cb->f[1], "off") == 0)
1059                         ctlr->crypt = 0;
1060                 else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
1061                         ctlr->crypt = 1;
1062                 else
1063                         r = -1;
1064         }
1065         else if(cistrcmp(cb->f[0], "clear") == 0){
1066                 if(cistrcmp(cb->f[1], "on") == 0)
1067                         ctlr->xclear = 0;
1068                 else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
1069                         ctlr->xclear = 1;
1070                 else
1071                         r = -1;
1072         }
1073         else if(cistrncmp(cb->f[0], "key", 3) == 0){
1074                 if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
1075                         ctlr->txkey = i-1;
1076                         if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1]))
1077                                 r = -1;
1078                 }
1079                 else
1080                         r = -1;
1081         }
1082         else if(cistrcmp(cb->f[0], "txkey") == 0){
1083                 if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
1084                         ctlr->txkey = i-1;
1085                 else
1086                         r = -1;
1087         }
1088         else if(cistrcmp(cb->f[0], "pm") == 0){
1089                 if(cistrcmp(cb->f[1], "off") == 0)
1090                         ctlr->pmena = 0;
1091                 else if(cistrcmp(cb->f[1], "on") == 0){
1092                         ctlr->pmena = 1;
1093                         if(cb->nf == 3){
1094                                 i = atoi(cb->f[2]);
1095                                 // check range here? what are the units?
1096                                 ctlr->pmwait = i;
1097                         }
1098                 }
1099                 else
1100                         r = -1;
1101         }
1102         else
1103                 r = -2;
1104         free(cb);
1105
1106         return r;
1107 }
1108
1109 long
1110 w_ctl(Ether* ether, void* buf, long n)
1111 {
1112         Ctlr *ctlr;
1113
1114         if((ctlr = ether->ctlr) == nil)
1115                 error(Enonexist);
1116         if((ctlr->state & Attached) == 0)
1117                 error(Eshutdown);
1118
1119         ilock(ctlr);
1120         if(w_option(ctlr, buf, n)){
1121                 iunlock(ctlr);
1122                 error(Ebadctl);
1123         }
1124         if(ctlr->txbusy)
1125                 w_txdone(ctlr, WTxErrEv);
1126         w_enable(ether);
1127         w_txstart(ether);
1128         iunlock(ctlr);
1129
1130         return n;
1131 }
1132
1133 void
1134 w_transmit(Ether* ether)
1135 {
1136         Ctlr* ctlr = ether->ctlr;
1137
1138         if(ctlr == 0)
1139                 return;
1140
1141         ilock(ctlr);
1142         ctlr->ntxrq++;
1143         w_txstart(ether);
1144         iunlock(ctlr);
1145 }
1146
1147 void
1148 w_promiscuous(void* arg, int on)
1149 {
1150         Ether* ether = (Ether*)arg;
1151         Ctlr* ctlr = ether->ctlr;
1152
1153         if(ctlr == nil)
1154                 error("card not found");
1155         if((ctlr->state & Attached) == 0)
1156                 error("card not attached");
1157         ilock(ctlr);
1158         ltv_outs(ctlr, WTyp_Prom, (on?1:0));
1159         iunlock(ctlr);
1160 }
1161
1162 void
1163 w_interrupt(Ureg* ,void* arg)
1164 {
1165         Ether* ether = (Ether*) arg;
1166         Ctlr* ctlr = (Ctlr*) ether->ctlr;
1167
1168         if(ctlr == 0)
1169                 return;
1170         ilock(ctlr);
1171         ctlr->nints++;
1172         w_intr(ether);
1173         iunlock(ctlr);
1174 }
1175
1176 int
1177 wavelanreset(Ether* ether, Ctlr *ctlr)
1178 {
1179         Wltv ltv;
1180
1181         iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
1182         w_intdis(ctlr);
1183         if(w_cmd(ctlr,WCmdIni,0)){
1184                 iprint("#l%d: init failed\n", ether->ctlrno);
1185                 return -1;
1186         }
1187         w_intdis(ctlr);
1188         ltv_outs(ctlr, WTyp_Tick, 8);
1189
1190         ctlr->chan = 0;
1191         ctlr->ptype = WDfltPType;
1192         ctlr->txkey = 0;
1193         ctlr->createibss = 0;
1194         ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
1195         ctlr->keys.type = WTyp_Keys;
1196         if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
1197                 ctlr->crypt = 1;
1198         *ctlr->netname = *ctlr->wantname = 0;
1199         strcpy(ctlr->nodename, "Plan 9 STA");
1200
1201         ctlr->netname[WNameLen-1] = 0;
1202         ctlr->wantname[WNameLen-1] = 0;
1203         ctlr->nodename[WNameLen-1] =0;
1204
1205         ltv.type = WTyp_Mac;
1206         ltv.len = 4;
1207         if(w_inltv(ctlr, &ltv)){
1208                 iprint("#l%d: unable to read mac addr\n",
1209                         ether->ctlrno);
1210                 return -1;
1211         }
1212         memmove(ether->ea, ltv.addr, Eaddrlen);
1213
1214         if(ctlr->chan == 0)
1215                 ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
1216         ctlr->apdensity = WDfltApDens;
1217         ctlr->rtsthres = WDfltRtsThres;
1218         ctlr->txrate = WDfltTxRate;
1219         ctlr->maxlen = WMaxLen;
1220         ctlr->pmena = 0;
1221         ctlr->pmwait = 100;
1222         ctlr->signal = 1;
1223         ctlr->noise = 1;
1224         ctlr->state |= Power;
1225
1226         // free old Ctlr struct if resetting after suspend
1227         if(ether->ctlr && ether->ctlr != ctlr)
1228                 free(ether->ctlr);
1229
1230         // link to ether
1231         ether->ctlr = ctlr;
1232         ether->mbps = 10;
1233         ether->attach = w_attach;
1234         ether->detach = w_detach;
1235         ether->interrupt = w_interrupt;
1236         ether->transmit = w_transmit;
1237         ether->ifstat = w_ifstat;
1238         ether->ctl = w_ctl;
1239         ether->power = w_power;
1240         ether->promiscuous = w_promiscuous;
1241         ether->multicast = w_multicast;
1242         ether->scanbs = w_scanbs;
1243         ether->arg = ether;
1244
1245         DEBUG("#l%d: irq %d port %lx type %s",
1246                 ether->ctlrno, ether->irq, ether->port, ether->type);
1247         DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n",
1248                 ether->ea[0], ether->ea[1], ether->ea[2],
1249                 ether->ea[3], ether->ea[4], ether->ea[5]);
1250
1251         return 0;
1252 }
1253
1254 char* wavenames[] = {
1255         "WaveLAN/IEEE",
1256         "TrueMobile 1150",
1257         "Instant Wireless ; Network PC CARD",
1258         "Instant Wireless Network PC Card",
1259         "Avaya Wireless PC Card",
1260         "AirLancer MC-11",
1261         "INTERSIL;HFA384x/IEEE;Version 01.02;",
1262         nil,
1263 };