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