wavelan.c 27 KB

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