]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/wl/if_wl.c
This commit was generated by cvs2svn to compensate for changes in r102840,
[FreeBSD/FreeBSD.git] / sys / dev / wl / if_wl.c
1 /* $FreeBSD$ */
2 /* 
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  * 1. Redistributions of source code must retain all copyright 
7  *    notices, this list of conditions and the following disclaimer.
8  * 2. The names of the authors may not be used to endorse or promote products
9  *    derived from this software without specific prior written permission
10  *
11  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
12  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
13  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
14  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
15  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
16  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
17  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
18  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
19  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
20  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21  * 
22  */
23 /*
24  * if_wl.c - original MACH, then BSDI ISA wavelan driver
25  *      ported to mach by Anders Klemets
26  *      to BSDI by Robert Morris
27  *      to FreeBSD by Jim Binkley
28  *      to FreeBSD 2.2+ by Michael Smith
29  *
30  * 2.2 update:
31  * Changed interface to match 2.1-2.2 differences.
32  * Implement IRQ selection logic in wlprobe()
33  * Implement PSA updating.
34  * Pruned heading comments for relevance.
35  * Ripped out all the 'interface counters' cruft.
36  * Cut the missing-interrupt timer back to 100ms.
37  * 2.2.1 update:
38  * now supports all multicast mode (mrouted will work),
39  *      but unfortunately must do that by going into promiscuous mode
40  * NWID sysctl added so that normally promiscuous mode is NWID-specific
41  *      but can be made NWID-inspecific
42  *                      7/14/97 jrb
43  *
44  * Work done:
45  * Ported to FreeBSD, got promiscuous mode working with bpfs,
46  * and rewired timer routine.  The i82586 will hang occasionally on output 
47  * and the watchdog timer will kick it if so and log an entry.
48  * 2 second timeout there.  Apparently the chip loses an interrupt.
49  * Code borrowed from if_ie.c for watchdog timer.
50  *
51  * The wavelan card is a 2mbit radio modem that emulates ethernet;
52  * i.e., it uses MAC addresses.  This should not be a surprise since
53  * it uses an ethernet controller as a major hw item.
54  * It can broadcast, unicast or apparently multicast in a base cell 
55  * using a omni-directional antennae that is 
56  * about 800 feet around the base cell barring walls and metal.  
57  * With directional antennae, it can be used point to point over a mile
58  * or so apparently (haven't tried that).
59  *
60  * There are ISA and pcmcia versions (not supported by this code).
61  * The ISA card has an Intel 82586 lan controller on it.  It consists
62  * of 2 pieces of hw, the lan controller (intel) and a radio-modem.
63  * The latter has an extra set of controller registers that has nothing
64  * to do with the i82586 and allows setting and monitoring of radio
65  * signal strength, etc.  There is a nvram area called the PSA that
66  * contains a number of setup variables including the IRQ and so-called
67  * NWID or Network ID.  The NWID must be set the same for all radio
68  * cards to communicate (unless you are using the ATT/NCR roaming feature
69  * with their access points.  There is no support for that here. Roaming
70  * involves a link-layer beacon sent out from the access points.  End
71  * stations monitor the signal strength and only use the strongest
72  * access point).  This driver assumes that the base ISA port, IRQ, 
73  * and NWID are first set in nvram via the dos-side "instconf.exe" utility 
74  * supplied with the card. This driver takes the ISA port from 
75  * the kernel configuration setup, and then determines the IRQ either 
76  * from the kernel config (if an explicit IRQ is set) or from the 
77  * PSA on the card if not.
78  * The hw also magically just uses the IRQ set in the nvram.
79  * The NWID is used magically as well by the radio-modem
80  * to determine which packets to keep or throw out.  
81  *
82  * sample config:
83  *
84  * device wl0 at isa? port 0x300 net irq ?
85  *
86  * Ifdefs:
87  * 1. WLDEBUG. (off) - if turned on enables IFF_DEBUG set via ifconfig debug
88  * 2. MULTICAST (on) - turned on and works up to and including mrouted
89  * 3. WLCACHE (off) -  define to turn on a signal strength 
90  * (and other metric) cache that is indexed by sender MAC address.  
91  * Apps can read this out to learn the remote signal strength of a 
92  * sender.  Note that it has a switch so that it only stores 
93  * broadcast/multicast senders but it could be set to store unicast 
94  * too only.  Size is hardwired in if_wl_wavelan.h
95  *
96  * one further note: promiscuous mode is a curious thing.  In this driver,
97  * promiscuous mode apparently CAN catch ALL packets and ignore the NWID
98  * setting.  This is probably more useful in a sense (for snoopers) if
99  * you are interested in all traffic as opposed to if you are interested
100  * in just your own.  There is a driver specific sysctl to turn promiscuous
101  * from just promiscuous to wildly promiscuous...
102  *
103  * This driver also knows how to load the synthesizers in the 2.4 Gz
104  * ISA Half-card, Product number 847647476 (USA/FCC IEEE Channel set).
105  * This product consists of a "mothercard" that contains the 82586,
106  * NVRAM that holds the PSA, and the ISA-buss interface custom ASIC. 
107  * The radio transceiver is a "daughtercard" called the WaveMODEM which
108  * connects to the mothercard through two single-inline connectors: a
109  * 20-pin connector provides DC-power and modem signals, and a 3-pin
110  * connector which exports the antenna connection. The code herein
111  * loads the receive and transmit synthesizers and the corresponding
112  * transmitter output power value from an EEPROM controlled through
113  * additional registers via the MMC. The EEPROM address selected
114  * are those whose values are preset by the DOS utility programs
115  * provided with the product, and this provides compatible operation
116  * with the DOS Packet Driver software. A future modification will
117  * add the necessary functionality to this driver and to the wlconfig
118  * utility to completely replace the DOS Configuration Utilities.
119  * The 2.4 Gz WaveMODEM is described in document number 407-024692/E,
120  * and is available through Lucent Technologies OEM supply channels.
121  * --RAB 1997/06/08.
122  */
123
124 #define MULTICAST  1
125
126 /* 
127  *      Olivetti PC586 Mach Ethernet driver v1.0
128  *      Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
129  *      All rights reserved.
130  *
131  */ 
132
133 /*
134   Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
135 Cupertino, California.
136
137                 All Rights Reserved
138
139   Permission to use, copy, modify, and distribute this software and
140 its documentation for any purpose and without fee is hereby
141 granted, provided that the above copyright notice appears in all
142 copies and that both the copyright notice and this permission notice
143 appear in supporting documentation, and that the name of Olivetti
144 not be used in advertising or publicity pertaining to distribution
145 of the software without specific, written prior permission.
146
147   OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
148 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
149 IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
150 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
151 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
152 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
153 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
154 */
155
156 /*
157   Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
158
159                 All Rights Reserved
160
161 Permission to use, copy, modify, and distribute this software and
162 its documentation for any purpose and without fee is hereby
163 granted, provided that the above copyright notice appears in all
164 copies and that both the copyright notice and this permission notice
165 appear in supporting documentation, and that the name of Intel
166 not be used in advertising or publicity pertaining to distribution
167 of the software without specific, written prior permission.
168
169 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
170 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
171 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
172 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
173 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
174 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
175 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176 */
177
178 /*
179  * NOTE:
180  *              by rvb:
181  *  1.  The best book on the 82586 is:
182  *              LAN Components User's Manual by Intel
183  *      The copy I found was dated 1984.  This really tells you
184  *      what the state machines are doing
185  *  2.  In the current design, we only do one write at a time,
186  *      though the hardware is capable of chaining and possibly
187  *      even batching.  The problem is that we only make one
188  *      transmit buffer available in sram space.
189  */
190
191 #define NWL 4
192 #include "opt_wavelan.h"
193 #include "opt_inet.h"
194
195 #include <sys/param.h>
196 #include <sys/systm.h>
197 #include <sys/kernel.h>
198 #include <sys/sockio.h>
199 #include <sys/mbuf.h>
200 #include <sys/socket.h>
201 #include <sys/syslog.h>
202 #include <sys/bus.h>
203
204 #include <sys/sysctl.h>
205
206 #include <net/ethernet.h>
207 #include <net/if.h>
208 #include <net/if_dl.h>
209
210 #ifdef INET
211 #include <netinet/in.h>
212 #include <netinet/in_systm.h>
213 #include <netinet/ip.h>
214 #include <netinet/if_ether.h>
215 #endif
216
217 #include <net/bpf.h>
218 #include <i386/isa/isa_device.h>
219 #include <i386/isa/ic/if_wl_i82586.h>   /* Definitions for the Intel chip */
220
221 /* was 1000 in original, fed to DELAY(x) */
222 #define DELAYCONST      1000
223 #include <dev/wl/if_wl.h>
224 #include <machine/if_wl_wavelan.h>
225
226 #ifndef COMPAT_OLDISA
227 #error "The wl device requires the old isa compatibility shims"
228 #endif
229
230 static char     t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)];
231
232 struct wl_softc{ 
233     struct      arpcom  wl_ac;                  /* Ethernet common part */
234 #define wl_if   wl_ac.ac_if                     /* network visible interface */
235 #define wl_addr wl_ac.ac_enaddr                 /* hardware address */
236     u_char      psa[0x40];
237     u_char      nwid[2];        /* current radio modem nwid */
238     short       base;
239     short       unit;
240     int         flags;
241     int         tbusy;          /* flag to determine if xmit is busy */
242     u_short     begin_fd;
243     u_short     end_fd;
244     u_short     end_rbd;
245     u_short     hacr;           /* latest host adapter CR command */
246     short       mode;
247     u_char      chan24;         /* 2.4 Gz: channel number/EEPROM Area # */
248     u_short     freq24;         /* 2.4 Gz: resulting frequency  */
249     struct      callout_handle watchdog_ch;
250 #ifdef WLCACHE
251     int         w_sigitems;     /* number of cached entries */
252     /*  array of cache entries */
253     struct w_sigcache w_sigcache[ MAXCACHEITEMS ];            
254     int w_nextcache;            /* next free cache entry */    
255     int w_wrapindex;            /* next "free" cache entry */
256 #endif
257 };
258 static struct wl_softc wl_softc[NWL];
259
260 #define WLSOFTC(unit) ((struct wl_softc *)(&wl_softc[unit]))
261
262 static int      wlprobe(struct isa_device *);
263 static int      wlattach(struct isa_device *);
264
265 struct isa_driver wldriver = {
266         INTR_TYPE_NET,
267         wlprobe,
268         wlattach,
269         "wl",
270         0
271 };
272 COMPAT_ISA_DRIVER(wl, wldriver);
273
274 /*
275  * XXX  The Wavelan appears to be prone to dropping stuff if you talk to
276  * it too fast.  This disgusting hack inserts a delay after each packet
277  * is queued which helps avoid this behaviour on fast systems.
278  */
279 static int      wl_xmit_delay = 250;
280 SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_delay, CTLFLAG_RW, &wl_xmit_delay, 0, "");
281
282 /* 
283  * not XXX, but ZZZ (bizarre).
284  * promiscuous mode can be toggled to ignore NWIDs.  By default,
285  * it does not.  Caution should be exercised about combining
286  * this mode with IFF_ALLMULTI which puts this driver in
287  * promiscuous mode.
288  */
289 static int      wl_ignore_nwid = 0;
290 SYSCTL_INT(_machdep, OID_AUTO, wl_ignore_nwid, CTLFLAG_RW, &wl_ignore_nwid, 0, "");
291
292 /*
293  * Emit diagnostics about transmission problems
294  */
295 static int      xmt_watch = 0;
296 SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_watch, CTLFLAG_RW, &xmt_watch, 0, "");
297
298 /*
299  * Collect SNR statistics
300  */
301 static int      gathersnr = 0;
302 SYSCTL_INT(_machdep, OID_AUTO, wl_gather_snr, CTLFLAG_RW, &gathersnr, 0, "");
303
304 static void     wlstart(struct ifnet *ifp);
305 static void     wlinit(void *xsc);
306 static int      wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
307 static timeout_t wlwatchdog;
308 static ointhand2_t wlintr;
309 static void     wlxmt(int unt, struct mbuf *m);
310 static int      wldiag(int unt); 
311 static int      wlconfig(int unit); 
312 static int      wlcmd(int unit, char *str);
313 static void     wlmmcstat(int unit);
314 static u_short  wlbldru(int unit);
315 static u_short  wlmmcread(u_int base, u_short reg);
316 static void     wlinitmmc(int unit);
317 static int      wlhwrst(int unit);
318 static void     wlrustrt(int unit);
319 static void     wlbldcu(int unit);
320 static int      wlack(int unit);
321 static int      wlread(int unit, u_short fd_p);
322 static void     getsnr(int unit);
323 static void     wlrcv(int unit);
324 static int      wlrequeue(int unit, u_short fd_p);
325 static void     wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, int unit);
326 static void     wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, int unit);
327 #ifdef WLDEBUG
328 static void     wltbd(int unit);
329 #endif
330 static void     wlgetpsa(int base, u_char *buf);
331 static void     wlsetpsa(int unit);
332 static u_short  wlpsacrc(u_char *buf);
333 static void     wldump(int unit);
334 #ifdef WLCACHE
335 static void     wl_cache_store(int, int, struct ether_header *, struct mbuf *);
336 static void     wl_cache_zero(int unit);
337 #endif
338
339 /* array for maping irq numbers to values for the irq parameter register */
340 static int irqvals[16] = { 
341     0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80 
342 };
343 /* mask of valid IRQs */
344 #define WL_IRQS (IRQ3|IRQ4|IRQ5|IRQ7|IRQ10|IRQ11|IRQ12|IRQ15)
345
346 /*
347  * wlprobe:
348  *
349  *      This function "probes" or checks for the WaveLAN board on the bus to
350  *      see if it is there.  As far as I can tell, the best break between this
351  *      routine and the attach code is to simply determine whether the board
352  *      is configured in properly.  Currently my approach to this is to write
353  *      and read a word from the SRAM on the board being probed.  If the word
354  *      comes back properly then we assume the board is there.  The config
355  *      code expects to see a successful return from the probe routine before
356  *      attach will be called.
357  *
358  * input        : address device is mapped to, and unit # being checked
359  * output       : a '1' is returned if the board exists, and a 0 otherwise
360  *
361  */
362 static int
363 wlprobe(struct isa_device *id)
364 {
365     struct wl_softc     *sc = &wl_softc[id->id_unit];   
366     register short      base = id->id_iobase;
367     char                *str = "wl%d: board out of range [0..%d]\n";
368     u_char              inbuf[100];
369     unsigned long       oldpri;
370     int                 irq;
371
372     /* TBD. not true.
373      * regular CMD() will not work, since no softc yet 
374      */
375 #define PCMD(base, hacr) outw((base), (hacr))
376
377     oldpri = splimp();
378     PCMD(base, HACR_RESET);                     /* reset the board */
379     DELAY(DELAYCONST);                          /* >> 4 clocks at 6MHz */
380     PCMD(base, HACR_RESET);                     /* reset the board */
381     DELAY(DELAYCONST);                          /* >> 4 clocks at 6MHz */
382     splx(oldpri);
383
384     /* clear reset command and set PIO#1 in autoincrement mode */
385     PCMD(base, HACR_DEFAULT);
386     PCMD(base, HACR_DEFAULT);
387     outw(PIOR1(base), 0);                       /* go to beginning of RAM */
388     outsw(PIOP1(base), str, strlen(str)/2+1);   /* write string */
389     
390     outw(PIOR1(base), 0);                       /* rewind */
391     insw(PIOP1(base), inbuf, strlen(str)/2+1);  /* read result */
392     
393     if (bcmp(str, inbuf, strlen(str)))
394         return(0);
395
396     sc->chan24 = 0;                             /* 2.4 Gz: config channel */
397     sc->freq24 = 0;                             /* 2.4 Gz: frequency    */
398
399     /* read the PSA from the board into temporary storage */
400     wlgetpsa(base, inbuf);
401     
402     /* We read the IRQ value from the PSA on the board. */
403     for (irq = 15; irq >= 0; irq--)
404         if (irqvals[irq] == inbuf[WLPSA_IRQNO])
405             break;
406     if ((irq == 0) || (irqvals[irq] == 0)){
407         printf("wl%d: PSA corrupt (invalid IRQ value)\n", id->id_unit);
408         id->id_irq = 0;                         /* no interrupt */
409     } else {
410         /*
411          * If the IRQ requested by the PSA is already claimed by another
412          * device, the board won't work, but the user can still access the
413          * driver to change the IRQ.
414          */
415         id->id_irq = (1<<irq);                  /* use IRQ from PSA */
416     }
417     return(16);
418 }
419
420
421 /*
422  * wlattach:
423  *
424  *      This function attaches a WaveLAN board to the "system".  The rest of
425  *      runtime structures are initialized here (this routine is called after
426  *      a successful probe of the board).  Once the ethernet address is read
427  *      and stored, the board's ifnet structure is attached and readied.
428  *
429  * input        : isa_dev structure setup in autoconfig
430  * output       : board structs and ifnet is setup
431  *
432  */
433 static int
434 wlattach(struct isa_device *id)
435 {
436     struct wl_softc     *sc = (struct wl_softc *) &wl_softc[id->id_unit];
437     register short      base = id->id_iobase;
438     int                 i,j;
439     u_char              unit = id->id_unit;
440     register struct ifnet       *ifp = &sc->wl_if;
441
442 #ifdef WLDEBUG
443     printf("wlattach: base %x, unit %d\n", base, unit);
444 #endif
445     id->id_ointr = wlintr;
446     sc->base = base;
447     sc->unit = unit;
448     sc->flags = 0;
449     sc->mode = 0;
450     sc->hacr = HACR_RESET;
451     callout_handle_init(&sc->watchdog_ch);
452     CMD(unit);                          /* reset the board */
453     DELAY(DELAYCONST);                  /* >> 4 clocks at 6MHz */
454         
455     /* clear reset command and set PIO#2 in parameter access mode */
456     sc->hacr = (HACR_DEFAULT & ~HACR_16BITS);
457     CMD(unit);
458
459     /* Read the PSA from the board for our later reference */
460     wlgetpsa(base, sc->psa);
461
462     /* fetch NWID */
463     sc->nwid[0] = sc->psa[WLPSA_NWID];
464     sc->nwid[1] = sc->psa[WLPSA_NWID+1];
465     
466     /* fetch MAC address - decide which one first */
467     if (sc->psa[WLPSA_MACSEL] & 1)
468         j = WLPSA_LOCALMAC;
469     else
470         j = WLPSA_UNIMAC;
471     for (i=0; i < WAVELAN_ADDR_SIZE; ++i)
472         sc->wl_addr[i] = sc->psa[j + i];
473
474     /* enter normal 16 bit mode operation */
475     sc->hacr = HACR_DEFAULT;
476     CMD(unit);
477
478     wlinitmmc(unit);
479     outw(PIOR1(base), OFFSET_SCB + 8);  /* address of scb_crcerrs */
480     outw(PIOP1(base), 0);                       /* clear scb_crcerrs */
481     outw(PIOP1(base), 0);                       /* clear scb_alnerrs */
482     outw(PIOP1(base), 0);                       /* clear scb_rscerrs */
483     outw(PIOP1(base), 0);                       /* clear scb_ovrnerrs */
484
485     bzero(ifp, sizeof(ifp));
486     ifp->if_softc = sc;
487     ifp->if_unit = id->id_unit;
488     ifp->if_mtu = WAVELAN_MTU;
489     ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
490 #ifdef    WLDEBUG
491     ifp->if_flags |= IFF_DEBUG;
492 #endif
493 #if     MULTICAST
494     ifp->if_flags |= IFF_MULTICAST;
495 #endif  /* MULTICAST */
496     ifp->if_name = "wl";
497     ifp->if_unit = unit;
498     ifp->if_init = wlinit;
499     ifp->if_output = ether_output;
500     ifp->if_start = wlstart;
501     ifp->if_ioctl = wlioctl;
502     ifp->if_timer = 0;   /* paranoia */
503     /* no entries
504        ifp->if_watchdog
505        ifp->if_done
506        ifp->if_reset
507        */
508     ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
509
510     bcopy(&sc->wl_addr[0], sc->wl_ac.ac_enaddr, WAVELAN_ADDR_SIZE);
511     printf("%s%d: address %6D, NWID 0x%02x%02x", ifp->if_name, ifp->if_unit,
512            sc->wl_ac.ac_enaddr, ":", sc->nwid[0], sc->nwid[1]);
513     if (sc->freq24) 
514         printf(", Freq %d MHz",sc->freq24);             /* 2.4 Gz       */
515     printf("\n");                                       /* 2.4 Gz       */
516
517
518     if (bootverbose)
519         wldump(unit);
520     return(1);
521 }
522
523 /*
524  * Print out interesting information about the 82596.
525  */
526 static void
527 wldump(int unit)
528 {
529     register struct wl_softc *sp = WLSOFTC(unit);
530     int         base = sp->base;
531     int         i;
532         
533     printf("hasr %04x\n", inw(HASR(base)));
534         
535     printf("scb at %04x:\n ", OFFSET_SCB);
536     outw(PIOR1(base), OFFSET_SCB);
537     for (i = 0; i < 8; i++)
538         printf("%04x ", inw(PIOP1(base)));
539     printf("\n");
540         
541     printf("cu at %04x:\n ", OFFSET_CU);
542     outw(PIOR1(base), OFFSET_CU);
543     for (i = 0; i < 8; i++)
544         printf("%04x ", inw(PIOP1(base)));
545     printf("\n");
546         
547     printf("tbd at %04x:\n ", OFFSET_TBD);
548     outw(PIOR1(base), OFFSET_TBD);
549     for (i = 0; i < 4; i++)
550         printf("%04x ", inw(PIOP1(base)));
551     printf("\n");
552 }
553
554 /* Initialize the Modem Management Controller */
555 static void
556 wlinitmmc(int unit)
557 {
558     register struct wl_softc *sp = WLSOFTC(unit);
559     int         base = sp->base;
560     int         configured;
561     int         mode = sp->mode;
562     int         i;                              /* 2.4 Gz               */
563         
564     /* enter 8 bit operation */
565     sp->hacr = (HACR_DEFAULT & ~HACR_16BITS);
566     CMD(unit);
567
568     configured = sp->psa[WLPSA_CONFIGURED] & 1;
569         
570     /*
571      * Set default modem control parameters.  Taken from NCR document
572      *  407-0024326 Rev. A 
573      */
574     MMC_WRITE(MMC_JABBER_ENABLE, 0x01);
575     MMC_WRITE(MMC_ANTEN_SEL, 0x02);
576     MMC_WRITE(MMC_IFS, 0x20);
577     MMC_WRITE(MMC_MOD_DELAY, 0x04);
578     MMC_WRITE(MMC_JAM_TIME, 0x38);
579     MMC_WRITE(MMC_DECAY_PRM, 0x00);             /* obsolete ? */
580     MMC_WRITE(MMC_DECAY_UPDAT_PRM, 0x00);
581     if (!configured) {
582         MMC_WRITE(MMC_LOOPT_SEL, 0x00);
583         if (sp->psa[WLPSA_COMPATNO] & 1) {
584             MMC_WRITE(MMC_THR_PRE_SET, 0x01);   /* 0x04 for AT and 0x01 for MCA */
585         } else {
586             MMC_WRITE(MMC_THR_PRE_SET, 0x04);   /* 0x04 for AT and 0x01 for MCA */
587         }
588         MMC_WRITE(MMC_QUALITY_THR, 0x03);
589     } else {
590         /* use configuration defaults from parameter storage area */
591         if (sp->psa[WLPSA_NWIDENABLE] & 1) {
592             if ((mode & (MOD_PROM | MOD_ENAL)) && wl_ignore_nwid) {
593                 MMC_WRITE(MMC_LOOPT_SEL, 0x40);
594             } else {
595                 MMC_WRITE(MMC_LOOPT_SEL, 0x00);
596             }
597         } else {
598             MMC_WRITE(MMC_LOOPT_SEL, 0x40);     /* disable network id check */
599         }
600         MMC_WRITE(MMC_THR_PRE_SET, sp->psa[WLPSA_THRESH]);
601         MMC_WRITE(MMC_QUALITY_THR, sp->psa[WLPSA_QUALTHRESH]);
602     }
603     MMC_WRITE(MMC_FREEZE, 0x00);
604     MMC_WRITE(MMC_ENCR_ENABLE, 0x00);
605
606     MMC_WRITE(MMC_NETW_ID_L,sp->nwid[1]);       /* set NWID */
607     MMC_WRITE(MMC_NETW_ID_H,sp->nwid[0]);
608
609     /* enter normal 16 bit mode operation */
610     sp->hacr = HACR_DEFAULT;
611     CMD(unit);
612     CMD(unit);                                  /* virtualpc1 needs this! */
613
614     if (sp->psa[WLPSA_COMPATNO]==               /* 2.4 Gz: half-card ver     */
615                 WLPSA_COMPATNO_WL24B) {         /* 2.4 Gz                    */
616         i=sp->chan24<<4;                        /* 2.4 Gz: position ch #     */
617         MMC_WRITE(MMC_EEADDR,i+0x0f);           /* 2.4 Gz: named ch, wc=16   */
618         MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+   /* 2.4 Gz: Download Synths   */
619                         MMC_EECTRL_EEOP_READ);  /* 2.4 Gz: Read EEPROM       */
620         for (i=0; i<1000; ++i) {                /* 2.4 Gz: wait for download */
621             DELAY(40);                          /* 2.4 Gz             */
622             if ((wlmmcread(base,MMC_EECTRLstat) /* 2.4 Gz: check DWLD and    */
623                 &(MMC_EECTRLstat_DWLD           /* 2.4 Gz:       EEBUSY      */
624                  +MMC_EECTRLstat_EEBUSY))==0)   /* 2.4 Gz:                   */
625                 break;                          /* 2.4 Gz: download finished */
626         }                                       /* 2.4 Gz                    */
627         if (i==1000) printf("wl: synth load failed\n"); /* 2.4 Gz       */
628         MMC_WRITE(MMC_EEADDR,0x61);             /* 2.4 Gz: default pwr, wc=2 */
629         MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+   /* 2.4 Gz: Download Xmit Pwr */
630                         MMC_EECTRL_EEOP_READ);  /* 2.4 Gz: Read EEPROM       */
631         for (i=0; i<1000; ++i) {                /* 2.4 Gz: wait for download */
632             DELAY(40);                          /* 2.4 Gz             */
633             if ((wlmmcread(base,MMC_EECTRLstat) /* 2.4 Gz: check DWLD and    */
634                 &(MMC_EECTRLstat_DWLD           /* 2.4 Gz:       EEBUSY      */
635                  +MMC_EECTRLstat_EEBUSY))==0)   /* 2.4 Gz:                   */
636                 break;                          /* 2.4 Gz: download finished */
637         }                                       /* 2.4 Gz                    */
638         if (i==1000) printf("wl: xmit pwr load failed\n"); /* 2.4 Gz         */
639         MMC_WRITE(MMC_ANALCTRL,                 /* 2.4 Gz: EXT ant+polarity  */
640                         MMC_ANALCTRL_ANTPOL +   /* 2.4 Gz:                   */
641                         MMC_ANALCTRL_EXTANT);   /* 2.4 Gz:                   */
642         i=sp->chan24<<4;                        /* 2.4 Gz: position ch #     */
643         MMC_WRITE(MMC_EEADDR,i);                /* 2.4 Gz: get frequency     */
644         MMC_WRITE(MMC_EECTRL,                   /* 2.4 Gz: EEPROM read      */
645                         MMC_EECTRL_EEOP_READ);  /* 2.4 Gz:                  */
646         DELAY(40);                              /* 2.4 Gz                    */
647         i = wlmmcread(base,MMC_EEDATALrv)       /* 2.4 Gz: freq val          */
648           + (wlmmcread(base,MMC_EEDATAHrv)<<8); /* 2.4 Gz                    */
649         sp->freq24 = (i>>6)+2400;               /* 2.4 Gz: save real freq    */
650     }
651 }
652
653 /*
654  * wlinit:
655  *
656  *      Another routine that interfaces the "if" layer to this driver.  
657  *      Simply resets the structures that are used by "upper layers".  
658  *      As well as calling wlhwrst that does reset the WaveLAN board.
659  *
660  * input        : softc pointer for this interface
661  * output       : structures (if structs) and board are reset
662  *
663  */     
664 static void
665 wlinit(void *xsc)
666 {
667     register struct wl_softc *sc = xsc;
668     struct ifnet        *ifp = &sc->wl_if;
669     int                 stat;
670     u_long              oldpri;
671
672 #ifdef WLDEBUG
673     if (sc->wl_if.if_flags & IFF_DEBUG)
674         printf("wl%d: entered wlinit()\n",sc->unit);
675 #endif
676     if (TAILQ_FIRST(&ifp->if_addrhead) == (struct ifaddr *)0)
677         return;
678     oldpri = splimp();
679     if ((stat = wlhwrst(sc->unit)) == TRUE) {
680         sc->wl_if.if_flags |= IFF_RUNNING;   /* same as DSF_RUNNING */
681         /* 
682          * OACTIVE is used by upper-level routines
683          * and must be set
684          */
685         sc->wl_if.if_flags &= ~IFF_OACTIVE;  /* same as tbusy below */
686                 
687         sc->flags |= DSF_RUNNING;
688         sc->tbusy = 0;
689         untimeout(wlwatchdog, sc, sc->watchdog_ch);
690                 
691         wlstart(ifp);
692     } else {
693         printf("wl%d init(): trouble resetting board.\n", sc->unit);
694     }
695     splx(oldpri);
696 }
697
698 /*
699  * wlhwrst:
700  *
701  *      This routine resets the WaveLAN board that corresponds to the 
702  *      board number passed in.
703  *
704  * input        : board number to do a hardware reset
705  * output       : board is reset
706  *
707  */
708 static int
709 wlhwrst(int unit)
710 {
711     register struct wl_softc    *sc = WLSOFTC(unit);
712
713 #ifdef WLDEBUG
714     if (sc->wl_if.if_flags & IFF_DEBUG)
715         printf("wl%d: entered wlhwrst()\n",unit);
716 #endif
717     sc->hacr = HACR_RESET;
718     CMD(unit);                  /* reset the board */
719         
720     /* clear reset command and set PIO#1 in autoincrement mode */
721     sc->hacr = HACR_DEFAULT;
722     CMD(unit);
723
724 #ifdef  WLDEBUG
725     if (sc->wl_if.if_flags & IFF_DEBUG)
726         wlmmcstat(unit);        /* Display MMC registers */
727 #endif  /* WLDEBUG */
728     wlbldcu(unit);              /* set up command unit structures */
729     
730     if (wldiag(unit) == 0)
731         return(0);
732     
733     if (wlconfig(unit) == 0)
734             return(0);
735     /* 
736      * insert code for loopback test here
737      */
738     wlrustrt(unit);             /* start receive unit */
739
740     /* enable interrupts */
741     sc->hacr = (HACR_DEFAULT | HACR_INTRON);
742     CMD(unit);
743     
744     return(1);
745 }
746
747 /*
748  * wlbldcu:
749  *
750  *      This function builds up the command unit structures.  It inits
751  *      the scp, iscp, scb, cb, tbd, and tbuf.
752  *
753  */
754 static void
755 wlbldcu(int unit)
756 {
757     register struct wl_softc *sc = WLSOFTC(unit);
758     short               base = sc->base;
759     scp_t               scp;
760     iscp_t              iscp;
761     scb_t               scb;
762     ac_t                cb;
763     tbd_t               tbd;
764     int         i;
765
766     bzero(&scp, sizeof(scp));
767     scp.scp_sysbus = 0;
768     scp.scp_iscp = OFFSET_ISCP;
769     scp.scp_iscp_base = 0;
770     outw(PIOR1(base), OFFSET_SCP);
771     outsw(PIOP1(base), &scp, sizeof(scp_t)/2);
772
773     bzero(&iscp, sizeof(iscp));
774     iscp.iscp_busy = 1;
775     iscp.iscp_scb_offset = OFFSET_SCB;
776     iscp.iscp_scb = 0;
777     iscp.iscp_scb_base = 0;
778     outw(PIOR1(base), OFFSET_ISCP);
779     outsw(PIOP1(base), &iscp, sizeof(iscp_t)/2);
780
781     scb.scb_status = 0;
782     scb.scb_command = SCB_RESET;
783     scb.scb_cbl_offset = OFFSET_CU;
784     scb.scb_rfa_offset = OFFSET_RU;
785     scb.scb_crcerrs = 0;
786     scb.scb_alnerrs = 0;
787     scb.scb_rscerrs = 0;
788     scb.scb_ovrnerrs = 0;
789     outw(PIOR1(base), OFFSET_SCB);
790     outsw(PIOP1(base), &scb, sizeof(scb_t)/2);
791
792     SET_CHAN_ATTN(unit);
793
794     outw(PIOR0(base), OFFSET_ISCP + 0); /* address of iscp_busy */
795     for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); )
796         continue;
797     if (i <= 0)
798         printf("wl%d bldcu(): iscp_busy timeout.\n", unit);
799     outw(PIOR0(base), OFFSET_SCB + 0);  /* address of scb_status */
800     for (i = STATUS_TRIES; i-- > 0; ) {
801         if (inw(PIOP0(base)) == (SCB_SW_CX|SCB_SW_CNA)) 
802             break;
803     }
804     if (i <= 0)
805         printf("wl%d bldcu(): not ready after reset.\n", unit);
806     wlack(unit);
807
808     cb.ac_status = 0;
809     cb.ac_command = AC_CW_EL;           /* NOP */
810     cb.ac_link_offset = OFFSET_CU;
811     outw(PIOR1(base), OFFSET_CU);
812     outsw(PIOP1(base), &cb, 6/2);
813
814     tbd.act_count = 0;
815     tbd.next_tbd_offset = I82586NULL;
816     tbd.buffer_addr = 0;
817     tbd.buffer_base = 0;
818     outw(PIOR1(base), OFFSET_TBD);
819     outsw(PIOP1(base), &tbd, sizeof(tbd_t)/2);
820 }
821
822 /*
823  * wlstart:
824  *
825  *      send a packet
826  *
827  * input        : board number
828  * output       : stuff sent to board if any there
829  *
830  */
831 static void
832 wlstart(struct ifnet *ifp)
833 {
834     int                         unit = ifp->if_unit;
835     struct mbuf                 *m;
836     register struct wl_softc    *sc = WLSOFTC(unit);
837     short                       base = sc->base;
838     int                         scb_status, cu_status, scb_command;
839
840 #ifdef WLDEBUG
841     if (sc->wl_if.if_flags & IFF_DEBUG)
842         printf("wl%d: entered wlstart()\n",unit);
843 #endif
844
845     outw(PIOR1(base), OFFSET_CU);
846     cu_status = inw(PIOP1(base));
847     outw(PIOR0(base),OFFSET_SCB + 0);   /* scb_status */
848     scb_status = inw(PIOP0(base));
849     outw(PIOR0(base), OFFSET_SCB + 2);
850     scb_command = inw(PIOP0(base));
851
852     /*
853      * don't need OACTIVE check as tbusy here checks to see
854      * if we are already busy 
855      */
856     if (sc->tbusy) {
857         if ((scb_status & 0x0700) == SCB_CUS_IDLE &&
858            (cu_status & AC_SW_B) == 0){
859             sc->tbusy = 0;
860             untimeout(wlwatchdog, sc, sc->watchdog_ch);
861             sc->wl_ac.ac_if.if_flags &= ~IFF_OACTIVE;
862             /*
863              * This is probably just a race.  The xmt'r is just
864              * became idle but WE have masked interrupts so ...
865              */
866 #ifdef WLDEBUG
867             printf("wl%d: CU idle, scb %04x %04x cu %04x\n",
868                    unit, scb_status, scb_command, cu_status);
869 #endif 
870             if (xmt_watch) printf("!!");
871         } else {
872             return;     /* genuinely still busy */
873         }
874     } else if ((scb_status & 0x0700) == SCB_CUS_ACTV ||
875       (cu_status & AC_SW_B)){
876 #ifdef WLDEBUG
877         printf("wl%d: CU unexpectedly busy; scb %04x cu %04x\n",
878                unit, scb_status, cu_status);
879 #endif
880         if (xmt_watch) printf("wl%d: busy?!",unit);
881         return;         /* hey, why are we busy? */
882     }
883
884     /* get ourselves some data */
885     ifp = &(sc->wl_if);
886     IF_DEQUEUE(&ifp->if_snd, m);
887     if (m != (struct mbuf *)0) {
888         /* let BPF see it before we commit it */
889         if (ifp->if_bpf) {
890             bpf_mtap(ifp, m);
891         }
892         sc->tbusy++;
893         /* set the watchdog timer so that if the board
894          * fails to interrupt we will restart
895          */
896         /* try 10 ticks, not very long */
897         sc->watchdog_ch = timeout(wlwatchdog, sc, 10);
898         sc->wl_ac.ac_if.if_flags |= IFF_OACTIVE;
899         sc->wl_if.if_opackets++;
900         wlxmt(unit, m);
901     } else {
902         sc->wl_ac.ac_if.if_flags &= ~IFF_OACTIVE;
903     }
904     return;
905 }
906
907 /*
908  * wlread:
909  *
910  *      This routine does the actual copy of data (including ethernet header
911  *      structure) from the WaveLAN to an mbuf chain that will be passed up
912  *      to the "if" (network interface) layer.  NOTE:  we currently
913  *      don't handle trailer protocols, so if that is needed, it will
914  *      (at least in part) be added here.  For simplicities sake, this
915  *      routine copies the receive buffers from the board into a local (stack)
916  *      buffer until the frame has been copied from the board.  Once in
917  *      the local buffer, the contents are copied to an mbuf chain that
918  *      is then enqueued onto the appropriate "if" queue.
919  *
920  * input        : board number, and an frame descriptor address
921  * output       : the packet is put into an mbuf chain, and passed up
922  * assumes      : if any errors occur, packet is "dropped on the floor"
923  *
924  */
925 static int
926 wlread(int unit, u_short fd_p)
927 {
928     register struct wl_softc    *sc = WLSOFTC(unit);
929     register struct ifnet       *ifp = &sc->wl_if;
930     short                       base = sc->base;
931     fd_t                        fd;
932     struct ether_header         eh;
933     struct mbuf                 *m, *tm;
934     rbd_t                       rbd;
935     u_char                      *mb_p;
936     u_short                     mlen, len, clen;
937     u_short                     bytes_in_msg, bytes_in_mbuf, bytes;
938
939
940 #ifdef WLDEBUG
941     if (sc->wl_if.if_flags & IFF_DEBUG)
942         printf("wl%d: entered wlread()\n",unit);
943 #endif
944     if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
945         printf("wl%d read(): board is not running.\n", ifp->if_unit);
946         sc->hacr &= ~HACR_INTRON;
947         CMD(unit);              /* turn off interrupts */
948     }
949     /* read ether_header info out of device memory. doesn't
950      * go into mbuf.  goes directly into eh structure
951      */
952     len = sizeof(struct ether_header);  /* 14 bytes */
953     outw(PIOR1(base), fd_p);
954     insw(PIOP1(base), &fd, (sizeof(fd_t) - len)/2);
955     insw(PIOP1(base), &eh, (len-2)/2);
956     eh.ether_type = ntohs(inw(PIOP1(base)));
957 #ifdef WLDEBUG
958     if (sc->wl_if.if_flags & IFF_DEBUG) {
959         printf("wlread: rcv packet, type is %x\n", eh.ether_type);
960     }
961 #endif 
962     /*
963      * WARNING.  above is done now in ether_input, above may be
964      * useful for debug. jrb
965      */
966     eh.ether_type = htons(eh.ether_type);
967
968     if (fd.rbd_offset == I82586NULL) {
969         printf("wl%d read(): Invalid buffer\n", unit);
970         if (wlhwrst(unit) != TRUE) {
971             printf("wl%d read(): hwrst trouble.\n", unit);
972         }
973         return 0;
974     }
975
976     outw(PIOR1(base), fd.rbd_offset);
977     insw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
978     bytes_in_msg = rbd.status & RBD_SW_COUNT;
979     MGETHDR(m, M_DONTWAIT, MT_DATA);
980     tm = m;
981     if (m == (struct mbuf *)0) {
982         /*
983          * not only do we want to return, we need to drop the packet on
984          * the floor to clear the interrupt.
985          *
986          */
987         if (wlhwrst(unit) != TRUE) {
988             sc->hacr &= ~HACR_INTRON;
989             CMD(unit);          /* turn off interrupts */
990             printf("wl%d read(): hwrst trouble.\n", unit);
991         }
992         return 0;
993     }
994     m->m_next = (struct mbuf *) 0;
995     m->m_pkthdr.rcvif = ifp;
996     m->m_pkthdr.len = 0; /* don't know this yet */
997     m->m_len = MHLEN;
998
999     /* always use a cluster. jrb 
1000      */
1001     MCLGET(m, M_DONTWAIT);
1002     if (m->m_flags & M_EXT) {
1003         m->m_len = MCLBYTES;
1004     }
1005     else {
1006         m_freem(m);
1007         if (wlhwrst(unit) != TRUE) {
1008             sc->hacr &= ~HACR_INTRON;
1009             CMD(unit);          /* turn off interrupts */
1010             printf("wl%d read(): hwrst trouble.\n", unit);
1011         }
1012         return 0;
1013     }
1014
1015     mlen = 0;
1016     clen = mlen;
1017     bytes_in_mbuf = m->m_len;
1018     mb_p = mtod(tm, u_char *);
1019     bytes = min(bytes_in_mbuf, bytes_in_msg);
1020     for (;;) {
1021         if (bytes & 1) {
1022             len = bytes + 1;
1023         } else {
1024             len = bytes;
1025         }
1026         outw(PIOR1(base), rbd.buffer_addr);
1027         insw(PIOP1(base), mb_p, len/2);
1028         clen += bytes;
1029         mlen += bytes;
1030
1031         if (!(bytes_in_mbuf -= bytes)) {
1032             MGET(tm->m_next, M_DONTWAIT, MT_DATA);
1033             tm = tm->m_next;
1034             if (tm == (struct mbuf *)0) {
1035                 m_freem(m);
1036                 printf("wl%d read(): No mbuf nth\n", unit);
1037                 if (wlhwrst(unit) != TRUE) {
1038                     sc->hacr &= ~HACR_INTRON;
1039                     CMD(unit);  /* turn off interrupts */
1040                     printf("wl%d read(): hwrst trouble.\n", unit);
1041                 }
1042                 return 0;
1043             }
1044             mlen = 0;
1045             tm->m_len = MLEN;
1046             bytes_in_mbuf = MLEN;
1047             mb_p = mtod(tm, u_char *);
1048         } else {
1049             mb_p += bytes;
1050         }
1051
1052         if (!(bytes_in_msg  -= bytes)) {
1053             if (rbd.status & RBD_SW_EOF ||
1054                 rbd.next_rbd_offset == I82586NULL) {
1055                 tm->m_len = mlen;
1056                 break;
1057             } else {
1058                 outw(PIOR1(base), rbd.next_rbd_offset);
1059                 insw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
1060                 bytes_in_msg = rbd.status & RBD_SW_COUNT;
1061             }
1062         } else {
1063             rbd.buffer_addr += bytes;
1064         }
1065
1066         bytes = min(bytes_in_mbuf, bytes_in_msg);
1067     }
1068
1069     m->m_pkthdr.len = clen;
1070
1071     /*
1072      * If hw is in promiscuous mode (note that I said hardware, not if
1073      * IFF_PROMISC is set in ifnet flags), then if this is a unicast
1074      * packet and the MAC dst is not us, drop it.  This check in normally
1075      * inside ether_input(), but IFF_MULTI causes hw promisc without
1076      * a bpf listener, so this is wrong.
1077      *          Greg Troxel <gdt@ir.bbn.com>, 1998-08-07
1078      */
1079     /*
1080      * TBD: also discard packets where NWID does not match.
1081      * However, there does not appear to be a way to read the nwid
1082      * for a received packet.  -gdt 1998-08-07
1083      */
1084     if (
1085 #ifdef WL_USE_IFNET_PROMISC_CHECK /* not defined */
1086         (sc->wl_ac.ac_if.if_flags & (IFF_PROMISC|IFF_ALLMULTI))
1087 #else
1088         /* hw is in promisc mode if this is true */
1089         (sc->mode & (MOD_PROM | MOD_ENAL))
1090 #endif
1091         &&
1092         (eh.ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
1093         bcmp(eh.ether_dhost, sc->wl_ac.ac_enaddr,
1094              sizeof(eh.ether_dhost)) != 0 ) {
1095       m_freem(m);
1096       return 1;
1097     }
1098
1099 #ifdef WLDEBUG
1100     if (sc->wl_if.if_flags & IFF_DEBUG)
1101         printf("wl%d: wlrecv %d bytes\n", unit, clen);
1102 #endif
1103
1104 #ifdef WLCACHE
1105     wl_cache_store(unit, base, &eh, m);
1106 #endif
1107
1108     /*
1109      * received packet is now in a chain of mbuf's.  next step is
1110      * to pass the packet upwards.
1111      *
1112      */
1113     ether_input(&sc->wl_if, &eh, m);
1114     return 1;
1115 }
1116
1117 /*
1118  * wlioctl:
1119  *
1120  *      This routine processes an ioctl request from the "if" layer
1121  *      above.
1122  *
1123  * input        : pointer the appropriate "if" struct, command, and data
1124  * output       : based on command appropriate action is taken on the
1125  *                WaveLAN board(s) or related structures
1126  * return       : error is returned containing exit conditions
1127  *
1128  */
1129 static int
1130 wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1131 {
1132     register struct ifreq       *ifr = (struct ifreq *)data;
1133     int                         unit = ifp->if_unit;
1134     register struct wl_softc    *sc = WLSOFTC(unit);
1135     short               base = sc->base;
1136     short               mode = 0;
1137     int                 opri, error = 0;
1138     struct thread       *td = curthread;        /* XXX */
1139     int                 irq, irqval, i, isroot;
1140     caddr_t             up;
1141 #ifdef WLCACHE
1142     int                 size;
1143     char *              cpt;
1144 #endif
1145
1146 #ifdef WLDEBUG
1147     if (sc->wl_if.if_flags & IFF_DEBUG)
1148         printf("wl%d: entered wlioctl()\n",unit);
1149 #endif
1150     opri = splimp();
1151     switch (cmd) {
1152     case SIOCSIFADDR:
1153     case SIOCGIFADDR:
1154     case SIOCSIFMTU:
1155         error = ether_ioctl(ifp, cmd, data);
1156         break;
1157
1158     case SIOCSIFFLAGS:
1159         if (ifp->if_flags & IFF_ALLMULTI) {
1160             mode |= MOD_ENAL;
1161         }
1162         if (ifp->if_flags & IFF_PROMISC) {
1163             mode |= MOD_PROM;
1164         }
1165         if (ifp->if_flags & IFF_LINK0) {
1166             mode |= MOD_PROM;
1167         }
1168         /*
1169          * force a complete reset if the recieve multicast/
1170          * promiscuous mode changes so that these take 
1171          * effect immediately.
1172          *
1173          */
1174         if (sc->mode != mode) {
1175             sc->mode = mode;
1176             if (sc->flags & DSF_RUNNING) {
1177                 sc->flags &= ~DSF_RUNNING;
1178                 wlinit(sc);
1179             }
1180         }
1181         /* if interface is marked DOWN and still running then
1182          * stop it.
1183          */
1184         if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) {
1185             printf("wl%d ioctl(): board is not running\n", unit);
1186             sc->flags &= ~DSF_RUNNING;
1187             sc->hacr &= ~HACR_INTRON;
1188             CMD(unit);            /* turn off interrupts */
1189         } 
1190         /* else if interface is UP and RUNNING, start it
1191                 */
1192         else if (ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0) {
1193             wlinit(sc);
1194         }
1195   
1196         /* if WLDEBUG set on interface, then printf rf-modem regs
1197         */
1198         if (ifp->if_flags & IFF_DEBUG)
1199             wlmmcstat(unit);
1200         break;
1201 #if     MULTICAST
1202     case SIOCADDMULTI:
1203     case SIOCDELMULTI:
1204
1205         wlinit(sc);
1206         break;
1207 #endif  /* MULTICAST */
1208
1209     /* DEVICE SPECIFIC */
1210
1211
1212         /* copy the PSA out to the caller */
1213     case SIOCGWLPSA:
1214         /* pointer to buffer in user space */
1215         up = (void *)ifr->ifr_data;
1216         /* work out if they're root */
1217         isroot = (suser(td) == 0);
1218         
1219         for (i = 0; i < 0x40; i++) {
1220             /* don't hand the DES key out to non-root users */
1221             if ((i > WLPSA_DESKEY) && (i < (WLPSA_DESKEY + 8)) && !isroot)
1222                 continue;
1223             if (subyte((up + i), sc->psa[i]))
1224                 return(EFAULT);
1225         }
1226         break;
1227
1228
1229         /* copy the PSA in from the caller; we only copy _some_ values */
1230     case SIOCSWLPSA:
1231         /* root only */
1232         if ((error = suser(td)))
1233             break;
1234         error = EINVAL; /* assume the worst */
1235         /* pointer to buffer in user space containing data */
1236         up = (void *)ifr->ifr_data;
1237         
1238         /* check validity of input range */
1239         for (i = 0; i < 0x40; i++)
1240             if (fubyte(up + i) < 0)
1241                 return(EFAULT);
1242
1243         /* check IRQ value */
1244         irqval = fubyte(up+WLPSA_IRQNO);
1245         for (irq = 15; irq >= 0; irq--)
1246             if (irqvals[irq] == irqval)
1247                 break;
1248         if (irq == 0)                   /* oops */
1249             break;
1250         /* new IRQ */
1251         sc->psa[WLPSA_IRQNO] = irqval;
1252
1253         /* local MAC */
1254         for (i = 0; i < 6; i++)
1255             sc->psa[WLPSA_LOCALMAC+i] = fubyte(up+WLPSA_LOCALMAC+i);
1256                 
1257         /* MAC select */        
1258         sc->psa[WLPSA_MACSEL] = fubyte(up+WLPSA_MACSEL);
1259         
1260         /* default nwid */
1261         sc->psa[WLPSA_NWID] = fubyte(up+WLPSA_NWID);
1262         sc->psa[WLPSA_NWID+1] = fubyte(up+WLPSA_NWID+1);
1263
1264         error = 0;
1265         wlsetpsa(unit);         /* update the PSA */
1266         break;
1267
1268
1269         /* get the current NWID out of the sc since we stored it there */
1270     case SIOCGWLCNWID:
1271         ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]);
1272         break;
1273
1274
1275         /*
1276          * change the nwid dynamically.  This
1277          * ONLY changes the radio modem and does not
1278          * change the PSA.
1279          *
1280          * 2 steps:
1281          *      1. save in softc "soft registers"
1282          *      2. save in radio modem (MMC)
1283          */
1284     case SIOCSWLCNWID:
1285         /* root only */
1286         if ((error = suser(td)))
1287             break;
1288         if (!(ifp->if_flags & IFF_UP)) {
1289             error = EIO;        /* only allowed while up */
1290         } else {
1291             /* 
1292              * soft c nwid shadows radio modem setting
1293              */
1294             sc->nwid[0] = (int)ifr->ifr_data >> 8;
1295             sc->nwid[1] = (int)ifr->ifr_data & 0xff;
1296             MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]);
1297             MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]);
1298         }
1299         break;
1300
1301         /* copy the EEPROM in 2.4 Gz WaveMODEM  out to the caller */
1302     case SIOCGWLEEPROM:
1303         /* root only */
1304         if ((error = suser(td)))
1305             break;
1306         /* pointer to buffer in user space */
1307         up = (void *)ifr->ifr_data;
1308         
1309         for (i=0x00; i<0x80; ++i) {             /* 2.4 Gz: size of EEPROM   */
1310             MMC_WRITE(MMC_EEADDR,i);            /* 2.4 Gz: get frequency    */
1311             MMC_WRITE(MMC_EECTRL,               /* 2.4 Gz: EEPROM read      */
1312                         MMC_EECTRL_EEOP_READ);  /* 2.4 Gz:                  */
1313             DELAY(40);                          /* 2.4 Gz                   */
1314             if (subyte(up + 2*i  ,              /* 2.4 Gz: pass low byte of */
1315                  wlmmcread(base,MMC_EEDATALrv)) /* 2.4 Gz: EEPROM word      */
1316                ) return(EFAULT);                /* 2.4 Gz:                  */
1317             if (subyte(up + 2*i+1,              /* 2.4 Gz: pass hi byte of  */
1318                  wlmmcread(base,MMC_EEDATALrv)) /* 2.4 Gz: EEPROM word      */
1319                ) return(EFAULT);                /* 2.4 Gz:                  */
1320         }
1321         break;
1322
1323 #ifdef WLCACHE
1324         /* zero (Delete) the wl cache */
1325     case SIOCDWLCACHE:
1326         /* root only */
1327         if ((error = suser(td)))
1328             break;
1329         wl_cache_zero(unit);
1330         break;
1331
1332         /* read out the number of used cache elements */
1333     case SIOCGWLCITEM:
1334         ifr->ifr_data = (caddr_t) sc->w_sigitems;
1335         break;
1336
1337         /* read out the wl cache */
1338     case SIOCGWLCACHE:
1339         /* pointer to buffer in user space */
1340         up = (void *)ifr->ifr_data;
1341         cpt = (char *) &sc->w_sigcache[0];
1342         size = sc->w_sigitems * sizeof(struct w_sigcache);
1343         
1344         for (i = 0; i < size; i++) {
1345             if (subyte((up + i), *cpt++))
1346                 return(EFAULT);
1347         }
1348         break;
1349 #endif
1350
1351     default:
1352         error = EINVAL;
1353     }
1354     splx(opri);
1355     return (error);
1356 }
1357
1358 /*
1359  * wlwatchdog():
1360  *
1361  * Called if the timer set in wlstart expires before an interrupt is received
1362  * from the wavelan.   It seems to lose interrupts sometimes.
1363  * The watchdog routine gets called if the transmitter failed to interrupt
1364  *
1365  * input        : which board is timing out
1366  * output       : board reset 
1367  *
1368  */
1369 static void
1370 wlwatchdog(void *vsc)
1371 {
1372     struct wl_softc *sc = vsc;
1373     int unit = sc->unit;
1374
1375     log(LOG_ERR, "wl%d: wavelan device timeout on xmit\n", unit);
1376     sc->wl_ac.ac_if.if_oerrors++;
1377     wlinit(sc);
1378 }
1379
1380 /*
1381  * wlintr:
1382  *
1383  *      This function is the interrupt handler for the WaveLAN
1384  *      board.  This routine will be called whenever either a packet
1385  *      is received, or a packet has successfully been transfered and
1386  *      the unit is ready to transmit another packet.
1387  *
1388  * input        : board number that interrupted
1389  * output       : either a packet is received, or a packet is transfered
1390  *
1391  */
1392 static void
1393 wlintr(unit)
1394 int unit;
1395 {
1396     register struct wl_softc *sc = &wl_softc[unit];
1397     short               base = sc->base;
1398     int                 ac_status;
1399     u_short             int_type, int_type1;
1400
1401 #ifdef WLDEBUG
1402     if (sc->wl_if.if_flags & IFF_DEBUG)
1403         printf("wl%d: wlintr() called\n",unit);
1404 #endif
1405
1406     if ((int_type = inw(HASR(base))) & HASR_MMC_INTR) {
1407         /* handle interrupt from the modem management controler */
1408         /* This will clear the interrupt condition */ 
1409         (void) wlmmcread(base,MMC_DCE_STATUS); /* ignored for now */
1410     }
1411
1412     if (!(int_type & HASR_INTR)){       /* return if no interrupt from 82586 */
1413         /* commented out. jrb.  it happens when reinit occurs
1414            printf("wlintr: int_type %x, dump follows\n", int_type);
1415            wldump(unit);
1416            */
1417         return;
1418     }
1419
1420     if (gathersnr)
1421         getsnr(unit);
1422     for (;;) {
1423         outw(PIOR0(base), OFFSET_SCB + 0);      /* get scb status */
1424         int_type = (inw(PIOP0(base)) & SCB_SW_INT);
1425         if (int_type == 0)                      /* no interrupts left */
1426             break;
1427
1428         int_type1 = wlack(unit);                /* acknowledge interrupt(s) */
1429         /* make sure no bits disappeared (others may appear) */
1430         if ((int_type & int_type1) != int_type)
1431             printf("wlack() int bits disappeared : %04x != int_type %04x\n",
1432                    int_type1, int_type);
1433         int_type = int_type1;                   /* go with the new status */
1434         /* 
1435          * incoming packet
1436          */
1437         if (int_type & SCB_SW_FR) {
1438             sc->wl_if.if_ipackets++;
1439             wlrcv(unit);
1440         }
1441         /*
1442          * receiver not ready
1443          */
1444         if (int_type & SCB_SW_RNR) {
1445             sc->wl_if.if_ierrors++;
1446 #ifdef  WLDEBUG
1447             if (sc->wl_if.if_flags & IFF_DEBUG)
1448                 printf("wl%d intr(): receiver overrun! begin_fd = %x\n",
1449                        unit, sc->begin_fd);
1450 #endif
1451             wlrustrt(unit);
1452         }
1453         /*
1454          * CU not ready
1455          */
1456         if (int_type & SCB_SW_CNA) {
1457             /*
1458              * At present, we don't care about CNA's.  We
1459              * believe they are a side effect of XMT.
1460              */
1461         }
1462         if (int_type & SCB_SW_CX) {
1463             /*
1464              * At present, we only request Interrupt for
1465              * XMT.
1466              */
1467             outw(PIOR1(base), OFFSET_CU);       /* get command status */
1468             ac_status = inw(PIOP1(base));
1469
1470             if (xmt_watch) {                    /* report some anomalies */
1471
1472                 if (sc->tbusy == 0) {
1473                     printf("wl%d: xmt intr but not busy, CU %04x\n",
1474                            unit, ac_status);
1475                 }
1476                 if (ac_status == 0) {
1477                     printf("wl%d: xmt intr but ac_status == 0\n", unit);
1478                 }
1479                 if (ac_status & AC_SW_A) {
1480                     printf("wl%d: xmt aborted\n",unit);
1481                 }
1482 #ifdef  notdef
1483                 if (ac_status & TC_CARRIER) {
1484                     printf("wl%d: no carrier\n", unit);
1485                 }
1486 #endif  /* notdef */
1487                 if (ac_status & TC_CLS) {
1488                     printf("wl%d: no CTS\n", unit);
1489                 }
1490                 if (ac_status & TC_DMA) {
1491                     printf("wl%d: DMA underrun\n", unit);
1492                 }
1493                 if (ac_status & TC_DEFER) {
1494                     printf("wl%d: xmt deferred\n",unit);
1495                 }
1496                 if (ac_status & TC_SQE) {
1497                     printf("wl%d: heart beat\n", unit);
1498                 }
1499                 if (ac_status & TC_COLLISION) {
1500                     printf("wl%d: too many collisions\n", unit);
1501                 }
1502             }
1503             /* if the transmit actually failed, or returned some status */
1504             if ((!(ac_status & AC_SW_OK)) || (ac_status & 0xfff)) {
1505                 if (ac_status & (TC_COLLISION | TC_CLS | TC_DMA)) {
1506                     sc->wl_if.if_oerrors++;
1507                 }
1508                 /* count collisions */
1509                 sc->wl_if.if_collisions += (ac_status & 0xf);
1510                 /* if TC_COLLISION set and collision count zero, 16 collisions */
1511                 if ((ac_status & 0x20) == 0x20) {
1512                     sc->wl_if.if_collisions += 0x10;
1513                 }
1514             }
1515             sc->tbusy = 0;
1516             untimeout(wlwatchdog, sc, sc->watchdog_ch);
1517             sc->wl_ac.ac_if.if_flags &= ~IFF_OACTIVE;
1518             wlstart(&(sc->wl_if));
1519         }
1520     }
1521     return;
1522 }
1523
1524 /*
1525  * wlrcv:
1526  *
1527  *      This routine is called by the interrupt handler to initiate a
1528  *      packet transfer from the board to the "if" layer above this
1529  *      driver.  This routine checks if a buffer has been successfully
1530  *      received by the WaveLAN.  If so, the routine wlread is called
1531  *      to do the actual transfer of the board data (including the
1532  *      ethernet header) into a packet (consisting of an mbuf chain).
1533  *
1534  * input        : number of the board to check
1535  * output       : if a packet is available, it is "sent up"
1536  *
1537  */
1538 static void
1539 wlrcv(int unit)
1540 {
1541     register struct wl_softc *sc = WLSOFTC(unit);
1542     short       base = sc->base;
1543     u_short     fd_p, status, offset, link_offset;
1544
1545 #ifdef WLDEBUG
1546     if (sc->wl_if.if_flags & IFF_DEBUG)
1547         printf("wl%d: entered wlrcv()\n",unit);
1548 #endif
1549     for (fd_p = sc->begin_fd; fd_p != I82586NULL; fd_p = sc->begin_fd) {
1550
1551         outw(PIOR0(base), fd_p + 0);    /* address of status */
1552         status = inw(PIOP0(base));
1553         outw(PIOR1(base), fd_p + 4);    /* address of link_offset */
1554         link_offset = inw(PIOP1(base));
1555         offset = inw(PIOP1(base));      /* rbd_offset */
1556         if (status == 0xffff || offset == 0xffff /*I82586NULL*/) {
1557             if (wlhwrst(unit) != TRUE)
1558                 printf("wl%d rcv(): hwrst ffff trouble.\n", unit);
1559             return;
1560         } else if (status & AC_SW_C) {
1561             if (status == (RFD_DONE|RFD_RSC)) {
1562                 /* lost one */
1563 #ifdef  WLDEBUG
1564                 if (sc->wl_if.if_flags & IFF_DEBUG)
1565                     printf("wl%d RCV: RSC %x\n", unit, status);
1566 #endif
1567                 sc->wl_if.if_ierrors++;
1568             } else if (!(status & RFD_OK)) {
1569                 printf("wl%d RCV: !OK %x\n", unit, status);
1570                 sc->wl_if.if_ierrors++;
1571             } else if (status & 0xfff) {        /* can't happen */
1572                 printf("wl%d RCV: ERRs %x\n", unit, status);
1573                 sc->wl_if.if_ierrors++;
1574             } else if (!wlread(unit, fd_p))
1575                 return;
1576
1577             if (!wlrequeue(unit, fd_p)) {
1578                 /* abort on chain error */
1579                 if (wlhwrst(unit) != TRUE)
1580                     printf("wl%d rcv(): hwrst trouble.\n", unit);
1581                 return;
1582             }
1583             sc->begin_fd = link_offset;
1584         } else {
1585             break;
1586         }
1587     }
1588     return;
1589 }
1590
1591 /*
1592  * wlrequeue:
1593  *
1594  *      This routine puts rbd's used in the last receive back onto the
1595  *      free list for the next receive.
1596  *
1597  */
1598 static int
1599 wlrequeue(int unit, u_short fd_p)
1600 {
1601     register struct wl_softc *sc = WLSOFTC(unit);
1602     short               base = sc->base;
1603     fd_t                fd;
1604     u_short             l_rbdp, f_rbdp, rbd_offset;
1605
1606     outw(PIOR0(base), fd_p + 6);
1607     rbd_offset = inw(PIOP0(base));
1608     if ((f_rbdp = rbd_offset) != I82586NULL) {
1609         l_rbdp = f_rbdp;
1610         for (;;) {
1611             outw(PIOR0(base), l_rbdp + 0);      /* address of status */
1612             if (inw(PIOP0(base)) & RBD_SW_EOF)
1613                 break;
1614             outw(PIOP0(base), 0);
1615             outw(PIOR0(base), l_rbdp + 2);      /* next_rbd_offset */
1616             if ((l_rbdp = inw(PIOP0(base))) == I82586NULL)
1617                 break;
1618         }
1619         outw(PIOP0(base), 0);
1620         outw(PIOR0(base), l_rbdp + 2);          /* next_rbd_offset */
1621         outw(PIOP0(base), I82586NULL);
1622         outw(PIOR0(base), l_rbdp + 8);          /* address of size */
1623         outw(PIOP0(base), inw(PIOP0(base)) | AC_CW_EL);
1624         outw(PIOR0(base), sc->end_rbd + 2);
1625         outw(PIOP0(base), f_rbdp);              /* end_rbd->next_rbd_offset */
1626         outw(PIOR0(base), sc->end_rbd + 8);     /* size */
1627         outw(PIOP0(base), inw(PIOP0(base)) & ~AC_CW_EL);
1628         sc->end_rbd = l_rbdp;
1629     }
1630
1631     fd.status = 0;
1632     fd.command = AC_CW_EL;
1633     fd.link_offset = I82586NULL;
1634     fd.rbd_offset = I82586NULL;
1635     outw(PIOR1(base), fd_p);
1636     outsw(PIOP1(base), &fd, 8/2);
1637     
1638     outw(PIOR1(base), sc->end_fd + 2);  /* addr of command */
1639     outw(PIOP1(base), 0);               /* command = 0 */
1640     outw(PIOP1(base), fd_p);            /* end_fd->link_offset = fd_p */
1641     sc->end_fd = fd_p;
1642
1643     return 1;
1644 }
1645
1646 #ifdef  WLDEBUG
1647 static int xmt_debug = 0;
1648 #endif  /* WLDEBUG */
1649
1650 /*
1651  * wlxmt:
1652  *
1653  *      This routine fills in the appropriate registers and memory
1654  *      locations on the WaveLAN board and starts the board off on
1655  *      the transmit.
1656  *
1657  * input        : board number of interest, and a pointer to the mbuf
1658  * output       : board memory and registers are set for xfer and attention
1659  *
1660  */
1661 static void
1662 wlxmt(int unit, struct mbuf *m)
1663 {
1664     register struct wl_softc *sc = WLSOFTC(unit);
1665     register u_short            xmtdata_p = OFFSET_TBUF;
1666     register u_short            xmtshort_p;
1667     struct      mbuf                    *tm_p = m;
1668     register struct ether_header        *eh_p = mtod(m, struct ether_header *);
1669     u_char                              *mb_p = mtod(m, u_char *) + sizeof(struct ether_header);
1670     u_short                             count = m->m_len - sizeof(struct ether_header);
1671     ac_t                                cb;
1672     u_short                             tbd_p = OFFSET_TBD;
1673     u_short                             len, clen = 0;
1674     short                               base = sc->base;
1675     int                                 spin;
1676         
1677 #ifdef WLDEBUG
1678     if (sc->wl_if.if_flags & IFF_DEBUG)
1679         printf("wl%d: entered wlxmt()\n",unit);
1680 #endif
1681
1682     cb.ac_status = 0;
1683     cb.ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I);
1684     cb.ac_link_offset = I82586NULL;
1685     outw(PIOR1(base), OFFSET_CU);
1686     outsw(PIOP1(base), &cb, 6/2);
1687     outw(PIOP1(base), OFFSET_TBD);      /* cb.cmd.transmit.tbd_offset */
1688     outsw(PIOP1(base), eh_p->ether_dhost, WAVELAN_ADDR_SIZE/2);
1689     outw(PIOP1(base), eh_p->ether_type);
1690
1691 #ifdef  WLDEBUG
1692     if (sc->wl_if.if_flags & IFF_DEBUG) {
1693         if (xmt_debug) {
1694             printf("XMT    mbuf: L%d @%p ", count, (void *)mb_p);
1695             printf("ether type %x\n", eh_p->ether_type);
1696         }
1697     }
1698 #endif  /* WLDEBUG */
1699     outw(PIOR0(base), OFFSET_TBD);
1700     outw(PIOP0(base), 0);               /* act_count */
1701     outw(PIOR1(base), OFFSET_TBD + 4);
1702     outw(PIOP1(base), xmtdata_p);       /* buffer_addr */
1703     outw(PIOP1(base), 0);               /* buffer_base */
1704     for (;;) {
1705         if (count) {
1706             if (clen + count > WAVELAN_MTU)
1707                 break;
1708             if (count & 1)
1709                 len = count + 1;
1710             else
1711                 len = count;
1712             outw(PIOR1(base), xmtdata_p);
1713             outsw(PIOP1(base), mb_p, len/2);
1714             clen += count;
1715             outw(PIOR0(base), tbd_p);  /* address of act_count */
1716             outw(PIOP0(base), inw(PIOP0(base)) + count);
1717             xmtdata_p += len;
1718             if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
1719                 break;
1720             if (count & 1) {
1721                 /* go to the next descriptor */
1722                 outw(PIOR0(base), tbd_p + 2);
1723                 tbd_p += sizeof (tbd_t);
1724                 outw(PIOP0(base), tbd_p); /* next_tbd_offset */
1725                 outw(PIOR0(base), tbd_p);
1726                 outw(PIOP0(base), 0);   /* act_count */
1727                 outw(PIOR1(base), tbd_p + 4);
1728                 outw(PIOP1(base), xmtdata_p); /* buffer_addr */
1729                 outw(PIOP1(base), 0);         /* buffer_base */
1730                 /* at the end -> coallesce remaining mbufs */
1731                 if (tbd_p == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) {
1732                     wlsftwsleaze(&count, &mb_p, &tm_p, unit);
1733                     continue;
1734                 }
1735                 /* next mbuf short -> coallesce as needed */
1736                 if ( (tm_p->m_next == (struct mbuf *) 0) ||
1737 #define HDW_THRESHOLD 55
1738                      tm_p->m_len > HDW_THRESHOLD)
1739                     /* ok */;
1740                 else {
1741                     wlhdwsleaze(&count, &mb_p, &tm_p, unit);
1742                     continue;
1743                 }
1744             }
1745         } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
1746             break;
1747         count = tm_p->m_len;
1748         mb_p = mtod(tm_p, u_char *);
1749 #ifdef  WLDEBUG
1750         if (sc->wl_if.if_flags & IFF_DEBUG)
1751             if (xmt_debug)
1752                 printf("mbuf+ L%d @%p ", count, (void *)mb_p);
1753 #endif  /* WLDEBUG */
1754     }
1755 #ifdef  WLDEBUG
1756     if (sc->wl_if.if_flags & IFF_DEBUG)
1757         if (xmt_debug)
1758             printf("CLEN = %d\n", clen);
1759 #endif  /* WLDEBUG */
1760     outw(PIOR0(base), tbd_p);
1761     if (clen < ETHERMIN) {
1762         outw(PIOP0(base), inw(PIOP0(base)) + ETHERMIN - clen);
1763         outw(PIOR1(base), xmtdata_p);
1764         for (xmtshort_p = xmtdata_p; clen < ETHERMIN; clen += 2)
1765             outw(PIOP1(base), 0);
1766     }   
1767     outw(PIOP0(base), inw(PIOP0(base)) | TBD_SW_EOF);
1768     outw(PIOR0(base), tbd_p + 2);
1769     outw(PIOP0(base), I82586NULL);
1770 #ifdef  WLDEBUG
1771     if (sc->wl_if.if_flags & IFF_DEBUG) {
1772         if (xmt_debug) {
1773             wltbd(unit);
1774             printf("\n");
1775         }
1776     }
1777 #endif  /* WLDEBUG */
1778
1779     outw(PIOR0(base), OFFSET_SCB + 2);  /* address of scb_command */
1780     /* 
1781      * wait for 586 to clear previous command, complain if it takes
1782      * too long
1783      */
1784     for (spin = 1;;spin = (spin + 1) % 10000) {
1785         if (inw(PIOP0(base)) == 0) {            /* it's done, we can go */
1786             break;
1787         }
1788         if ((spin == 0) && xmt_watch) {         /* not waking up, and we care */
1789                 printf("wl%d: slow accepting xmit\n",unit);
1790         }
1791     }
1792     outw(PIOP0(base), SCB_CU_STRT);             /* new command */
1793     SET_CHAN_ATTN(unit);
1794     
1795     m_freem(m);
1796
1797     /* XXX 
1798      * Pause to avoid transmit overrun problems.
1799      * The required delay tends to vary with platform type, and may be
1800      * related to interrupt loss.
1801      */
1802     if (wl_xmit_delay) {
1803         DELAY(wl_xmit_delay);
1804     }
1805     return;
1806 }
1807
1808 /*
1809  * wlbldru:
1810  *
1811  *      This function builds the linear linked lists of fd's and
1812  *      rbd's.  Based on page 4-32 of 1986 Intel microcom handbook.
1813  *
1814  */
1815 static u_short
1816 wlbldru(int unit)
1817 {
1818     register struct wl_softc *sc = WLSOFTC(unit);
1819     short       base = sc->base;
1820     fd_t        fd;
1821     rbd_t       rbd;
1822     u_short     fd_p = OFFSET_RU;
1823     u_short     rbd_p = OFFSET_RBD;
1824     int         i;
1825
1826     sc->begin_fd = fd_p;
1827     for (i = 0; i < N_FD; i++) {
1828         fd.status = 0;
1829         fd.command = 0;
1830         fd.link_offset = fd_p + sizeof(fd_t);
1831         fd.rbd_offset = I82586NULL;
1832         outw(PIOR1(base), fd_p);
1833         outsw(PIOP1(base), &fd, 8/2);
1834         fd_p = fd.link_offset;
1835     }
1836     fd_p -= sizeof(fd_t);
1837     sc->end_fd = fd_p;
1838     outw(PIOR1(base), fd_p + 2);
1839     outw(PIOP1(base), AC_CW_EL);        /* command */
1840     outw(PIOP1(base), I82586NULL);      /* link_offset */
1841     fd_p = OFFSET_RU;
1842     
1843     outw(PIOR0(base), fd_p + 6);        /* address of rbd_offset */
1844     outw(PIOP0(base), rbd_p);
1845     outw(PIOR1(base), rbd_p);
1846     for (i = 0; i < N_RBD; i++) {
1847         rbd.status = 0;
1848         rbd.buffer_addr = rbd_p + sizeof(rbd_t) + 2;
1849         rbd.buffer_base = 0;
1850         rbd.size = RCVBUFSIZE;
1851         if (i != N_RBD-1) {
1852             rbd_p += sizeof(ru_t);
1853             rbd.next_rbd_offset = rbd_p;
1854         } else {
1855             rbd.next_rbd_offset = I82586NULL;
1856             rbd.size |= AC_CW_EL;
1857             sc->end_rbd = rbd_p;
1858         }
1859         outsw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
1860         outw(PIOR1(base), rbd_p);
1861     }
1862     return sc->begin_fd;
1863 }
1864
1865 /*
1866  * wlrustrt:
1867  *
1868  *      This routine starts the receive unit running.  First checks if the
1869  *      board is actually ready, then the board is instructed to receive
1870  *      packets again.
1871  *
1872  */
1873 static void
1874 wlrustrt(int unit)
1875 {
1876     register struct wl_softc *sc = WLSOFTC(unit);
1877     short               base = sc->base;
1878     u_short             rfa;
1879
1880 #ifdef WLDEBUG
1881     if (sc->wl_if.if_flags & IFF_DEBUG)
1882         printf("wl%d: entered wlrustrt()\n",unit);
1883 #endif
1884     outw(PIOR0(base), OFFSET_SCB);
1885     if (inw(PIOP0(base)) & SCB_RUS_READY){
1886         printf("wlrustrt: RUS_READY\n");
1887         return;
1888     }
1889
1890     outw(PIOR0(base), OFFSET_SCB + 2);
1891     outw(PIOP0(base), SCB_RU_STRT);             /* command */
1892     rfa = wlbldru(unit);
1893     outw(PIOR0(base), OFFSET_SCB + 6);  /* address of scb_rfa_offset */
1894     outw(PIOP0(base), rfa);
1895
1896     SET_CHAN_ATTN(unit);
1897     return;
1898 }
1899
1900 /*
1901  * wldiag:
1902  *
1903  *      This routine does a 586 op-code number 7, and obtains the
1904  *      diagnose status for the WaveLAN.
1905  *
1906  */
1907 static int
1908 wldiag(int unit)
1909 {
1910     register struct wl_softc *sc = WLSOFTC(unit);
1911     short               base = sc->base;
1912     short status;
1913
1914 #ifdef WLDEBUG
1915     if (sc->wl_if.if_flags & IFF_DEBUG)
1916         printf("wl%d: entered wldiag()\n",unit);
1917 #endif
1918     outw(PIOR0(base), OFFSET_SCB);
1919     status = inw(PIOP0(base));
1920     if (status & SCB_SW_INT) {
1921                 /* state is 2000 which seems ok
1922                    printf("wl%d diag(): unexpected initial state %\n",
1923                    unit, inw(PIOP0(base)));
1924                 */
1925         wlack(unit);
1926     }
1927     outw(PIOR1(base), OFFSET_CU);
1928     outw(PIOP1(base), 0);                       /* ac_status */
1929     outw(PIOP1(base), AC_DIAGNOSE|AC_CW_EL);/* ac_command */
1930     if (wlcmd(unit, "diag()") == 0)
1931         return 0;
1932     outw(PIOR0(base), OFFSET_CU);
1933     if (inw(PIOP0(base)) & 0x0800) {
1934         printf("wl%d: i82586 Self Test failed!\n", unit);
1935         return 0;
1936     }
1937     return TRUE;
1938 }
1939
1940 /*
1941  * wlconfig:
1942  *
1943  *      This routine does a standard config of the WaveLAN board.
1944  *
1945  */
1946 static int
1947 wlconfig(int unit)
1948 {
1949     configure_t configure;
1950     register struct wl_softc *sc = WLSOFTC(unit);
1951     short               base = sc->base;
1952
1953 #if     MULTICAST
1954     struct ifmultiaddr *ifma;
1955     u_char *addrp;
1956     int cnt = 0;
1957 #endif  /* MULTICAST */
1958
1959 #ifdef WLDEBUG
1960     if (sc->wl_if.if_flags & IFF_DEBUG)
1961         printf("wl%d: entered wlconfig()\n",unit);
1962 #endif
1963     outw(PIOR0(base), OFFSET_SCB);
1964     if (inw(PIOP0(base)) & SCB_SW_INT) {
1965         /*
1966           printf("wl%d config(): unexpected initial state %x\n",
1967           unit, inw(PIOP0(base)));
1968           */
1969     }
1970     wlack(unit);
1971
1972     outw(PIOR1(base), OFFSET_CU);
1973     outw(PIOP1(base), 0);                               /* ac_status */
1974     outw(PIOP1(base), AC_CONFIGURE|AC_CW_EL);   /* ac_command */
1975
1976 /* jrb hack */
1977     configure.fifolim_bytecnt   = 0x080c;
1978     configure.addrlen_mode      = 0x0600;
1979     configure.linprio_interframe        = 0x2060;
1980     configure.slot_time         = 0xf200;
1981     configure.hardware          = 0x0008;       /* tx even w/o CD */
1982     configure.min_frame_len     = 0x0040;
1983 #if 0
1984     /* This is the configuration block suggested by Marc Meertens
1985      * <mmeerten@obelix.utrecht.NCR.COM> in an e-mail message to John
1986      * Ioannidis on 10 Nov 92.
1987      */
1988     configure.fifolim_bytecnt   = 0x040c;
1989     configure.addrlen_mode      = 0x0600;
1990     configure.linprio_interframe        = 0x2060;
1991     configure.slot_time         = 0xf000;
1992     configure.hardware          = 0x0008;       /* tx even w/o CD */
1993     configure.min_frame_len     = 0x0040;
1994 #else
1995     /*
1996      * below is the default board configuration from p2-28 from 586 book
1997      */
1998     configure.fifolim_bytecnt   = 0x080c;
1999     configure.addrlen_mode      = 0x2600;
2000     configure.linprio_interframe        = 0x7820;       /* IFS=120, ACS=2 */
2001     configure.slot_time         = 0xf00c;       /* slottime=12    */
2002     configure.hardware          = 0x0008;       /* tx even w/o CD */
2003     configure.min_frame_len     = 0x0040;
2004 #endif
2005     if (sc->mode & (MOD_PROM | MOD_ENAL))
2006         configure.hardware |= 1;
2007     outw(PIOR1(base), OFFSET_CU + 6);
2008     outsw(PIOP1(base), &configure, sizeof(configure_t)/2);
2009
2010     if (wlcmd(unit, "config()-configure") == 0)
2011         return 0;
2012 #if     MULTICAST
2013     outw(PIOR1(base), OFFSET_CU);
2014     outw(PIOP1(base), 0);                               /* ac_status */
2015     outw(PIOP1(base), AC_MCSETUP|AC_CW_EL);             /* ac_command */
2016     outw(PIOR1(base), OFFSET_CU + 8);
2017     TAILQ_FOREACH(ifma, &sc->wl_if.if_multiaddrs, ifma_link) {
2018         if (ifma->ifma_addr->sa_family != AF_LINK)
2019             continue;
2020         
2021         addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
2022         outw(PIOP1(base), addrp[0] + (addrp[1] << 8));
2023         outw(PIOP1(base), addrp[2] + (addrp[3] << 8));
2024         outw(PIOP1(base), addrp[4] + (addrp[5] << 8));
2025         ++cnt;
2026     }
2027     outw(PIOR1(base), OFFSET_CU + 6);           /* mc-cnt */
2028     outw(PIOP1(base), cnt * WAVELAN_ADDR_SIZE);
2029     if (wlcmd(unit, "config()-mcaddress") == 0)
2030         return 0;
2031 #endif  /* MULTICAST */
2032
2033     outw(PIOR1(base), OFFSET_CU);
2034     outw(PIOP1(base), 0);                               /* ac_status */
2035     outw(PIOP1(base), AC_IASETUP|AC_CW_EL);             /* ac_command */
2036     outw(PIOR1(base), OFFSET_CU + 6);
2037     outsw(PIOP1(base), sc->wl_addr, WAVELAN_ADDR_SIZE/2);
2038
2039     if (wlcmd(unit, "config()-address") == 0)
2040         return(0);
2041
2042     wlinitmmc(unit);
2043
2044     return(1);
2045 }
2046
2047 /*
2048  * wlcmd:
2049  *
2050  * Set channel attention bit and busy wait until command has
2051  * completed. Then acknowledge the command completion.
2052  */
2053 static int
2054 wlcmd(int unit, char *str)
2055 {
2056     register struct wl_softc *sc = WLSOFTC(unit);
2057     short       base = sc->base;
2058     int i;
2059         
2060     outw(PIOR0(base), OFFSET_SCB + 2);  /* address of scb_command */
2061     outw(PIOP0(base), SCB_CU_STRT);
2062     
2063     SET_CHAN_ATTN(unit);
2064     
2065     outw(PIOR0(base), OFFSET_CU);
2066     for (i = 0; i < 0xffff; i++)
2067         if (inw(PIOP0(base)) & AC_SW_C)
2068             break;
2069     if (i == 0xffff || !(inw(PIOP0(base)) & AC_SW_OK)) {
2070         printf("wl%d: %s failed; status = %d, inw = %x, outw = %x\n",
2071                unit, str, inw(PIOP0(base)) & AC_SW_OK, inw(PIOP0(base)), inw(PIOR0(base)));
2072         outw(PIOR0(base), OFFSET_SCB);
2073         printf("scb_status %x\n", inw(PIOP0(base)));
2074         outw(PIOR0(base), OFFSET_SCB+2);
2075         printf("scb_command %x\n", inw(PIOP0(base)));
2076         outw(PIOR0(base), OFFSET_SCB+4);
2077         printf("scb_cbl %x\n", inw(PIOP0(base)));
2078         outw(PIOR0(base), OFFSET_CU+2);
2079         printf("cu_cmd %x\n", inw(PIOP0(base)));
2080         return(0);
2081     }
2082
2083     outw(PIOR0(base), OFFSET_SCB);
2084     if ((inw(PIOP0(base)) & SCB_SW_INT) && (inw(PIOP0(base)) != SCB_SW_CNA)) {
2085         /*
2086           printf("wl%d %s: unexpected final state %x\n",
2087           unit, str, inw(PIOP0(base)));
2088           */
2089     }
2090     wlack(unit);
2091     return(TRUE);
2092 }       
2093
2094 /*
2095  * wlack: if the 82596 wants attention because it has finished
2096  * sending or receiving a packet, acknowledge its desire and
2097  * return bits indicating the kind of attention. wlack() returns
2098  * these bits so that the caller can service exactly the
2099  * conditions that wlack() acknowledged.
2100  */
2101 static int
2102 wlack(int unit)
2103 {
2104     int i;
2105     register u_short cmd;
2106     register struct wl_softc *sc = WLSOFTC(unit);
2107     short base = sc->base;
2108
2109     outw(PIOR1(base), OFFSET_SCB);
2110     if (!(cmd = (inw(PIOP1(base)) & SCB_SW_INT)))
2111         return(0);
2112 #ifdef WLDEBUG
2113     if (sc->wl_if.if_flags & IFF_DEBUG)
2114         printf("wl%d: doing a wlack()\n",unit);
2115 #endif
2116     outw(PIOP1(base), cmd);
2117     SET_CHAN_ATTN(unit);
2118     outw(PIOR0(base), OFFSET_SCB + 2);  /* address of scb_command */
2119     for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); )
2120         continue;
2121     if (i < 1)
2122         printf("wl%d wlack(): board not accepting command.\n", unit);
2123     return(cmd);
2124 }
2125
2126 #ifdef WLDEBUG
2127 static void
2128 wltbd(int unit)
2129 {
2130     register struct wl_softc *sc = WLSOFTC(unit);
2131     short               base = sc->base;
2132     u_short             tbd_p = OFFSET_TBD;
2133     tbd_t               tbd;
2134     int                 i = 0;
2135     int                 sum = 0;
2136
2137     for (;;) {
2138         outw(PIOR1(base), tbd_p);
2139         insw(PIOP1(base), &tbd, sizeof(tbd_t)/2);
2140         sum += (tbd.act_count & ~TBD_SW_EOF);
2141         printf("%d: addr %x, count %d (%d), next %x, base %x\n",
2142                i++, tbd.buffer_addr,
2143                (tbd.act_count & ~TBD_SW_EOF), sum,
2144                tbd.next_tbd_offset, tbd.buffer_base);
2145         if (tbd.act_count & TBD_SW_EOF)
2146             break;
2147         tbd_p = tbd.next_tbd_offset;
2148     }
2149 }
2150 #endif
2151
2152 static void
2153 wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, int unit)
2154 {
2155     struct mbuf *tm_p = *tm_pp;
2156     u_char              *mb_p = *mb_pp;
2157     u_short             count = 0;
2158     u_char              *cp;
2159     int         len;
2160
2161     /*
2162      * can we get a run that will be coallesced or
2163      * that terminates before breaking
2164      */
2165     do {
2166         count += tm_p->m_len;
2167         if (tm_p->m_len & 1)
2168             break;
2169     } while ((tm_p = tm_p->m_next) != (struct mbuf *)0);
2170     if ( (tm_p == (struct mbuf *)0) ||
2171          count > HDW_THRESHOLD) {
2172         *countp = (*tm_pp)->m_len;
2173         *mb_pp = mtod((*tm_pp), u_char *);
2174         return;
2175     }
2176
2177     /* we need to copy */
2178     tm_p = *tm_pp;
2179     mb_p = *mb_pp;
2180     count = 0;
2181     cp = (u_char *) t_packet;
2182     for (;;) {
2183         bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
2184         count += len;
2185         if (count > HDW_THRESHOLD)
2186                         break;
2187         cp += len;
2188         if (tm_p->m_next == (struct mbuf *)0)
2189             break;
2190         tm_p = tm_p->m_next;
2191     }
2192     *countp = count;
2193     *mb_pp = (u_char *) t_packet;
2194     *tm_pp = tm_p;
2195     return;
2196 }
2197
2198
2199 static void
2200 wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, int unit)
2201 {
2202     struct mbuf *tm_p = *tm_pp;
2203     u_short             count = 0;
2204     u_char              *cp = (u_char *) t_packet;
2205     int                 len;
2206
2207     /* we need to copy */
2208     for (;;) {
2209         bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
2210         count += len;
2211         cp += len;
2212         if (tm_p->m_next == (struct mbuf *)0)
2213             break;
2214         tm_p = tm_p->m_next;
2215     }
2216
2217     *countp = count;
2218     *mb_pp = (u_char *) t_packet;
2219     *tm_pp = tm_p;
2220     return;
2221 }
2222
2223 static void
2224 wlmmcstat(int unit)
2225 {
2226     register struct wl_softc *sc = WLSOFTC(unit);
2227     short       base = sc->base;
2228     u_short tmp;
2229
2230     printf("wl%d: DCE_STATUS: 0x%x, ", unit,
2231            wlmmcread(base,MMC_DCE_STATUS) & 0x0f);
2232     tmp = wlmmcread(base,MMC_CORRECT_NWID_H) << 8;
2233     tmp |= wlmmcread(base,MMC_CORRECT_NWID_L);
2234     printf("Correct NWID's: %d, ", tmp);
2235     tmp = wlmmcread(base,MMC_WRONG_NWID_H) << 8;
2236     tmp |= wlmmcread(base,MMC_WRONG_NWID_L);
2237     printf("Wrong NWID's: %d\n", tmp);
2238     printf("THR_PRE_SET: 0x%x, ", wlmmcread(base,MMC_THR_PRE_SET));
2239     printf("SIGNAL_LVL: %d, SILENCE_LVL: %d\n", 
2240            wlmmcread(base,MMC_SIGNAL_LVL),
2241            wlmmcread(base,MMC_SILENCE_LVL));
2242     printf("SIGN_QUAL: 0x%x, NETW_ID: %x:%x, DES: %d\n",
2243            wlmmcread(base,MMC_SIGN_QUAL),
2244            wlmmcread(base,MMC_NETW_ID_H),
2245            wlmmcread(base,MMC_NETW_ID_L),
2246            wlmmcread(base,MMC_DES_AVAIL));
2247 }
2248
2249 static u_short
2250 wlmmcread(u_int base, u_short reg)
2251 {
2252     while (inw(HASR(base)) & HASR_MMC_BUSY)
2253         continue;
2254     outw(MMCR(base),reg << 1);
2255     while (inw(HASR(base)) & HASR_MMC_BUSY)
2256         continue;
2257     return (u_short)inw(MMCR(base)) >> 8;
2258 }
2259
2260 static void
2261 getsnr(int unit)
2262 {
2263     MMC_WRITE(MMC_FREEZE,1);
2264     /* 
2265      * SNR retrieval procedure :
2266      *
2267      * read signal level : wlmmcread(base, MMC_SIGNAL_LVL);
2268      * read silence level : wlmmcread(base, MMC_SILENCE_LVL);
2269      */
2270     MMC_WRITE(MMC_FREEZE,0);
2271     /*
2272      * SNR is signal:silence ratio.
2273      */
2274 }
2275
2276 /*
2277 ** wlgetpsa
2278 **
2279 ** Reads the psa for the wavelan at (base) into (buf)
2280 */
2281 static void
2282 wlgetpsa(int base, u_char *buf)
2283 {
2284     int i;
2285
2286     PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
2287     PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
2288
2289     for (i = 0; i < 0x40; i++) {
2290         outw(PIOR2(base), i);
2291         buf[i] = inb(PIOP2(base));
2292     }
2293     PCMD(base, HACR_DEFAULT);
2294     PCMD(base, HACR_DEFAULT);
2295 }
2296
2297 /*
2298 ** wlsetpsa
2299 **
2300 ** Writes the psa for wavelan (unit) from the softc back to the
2301 ** board.  Updates the CRC and sets the CRC OK flag.
2302 **
2303 ** Do not call this when the board is operating, as it doesn't 
2304 ** preserve the hacr.
2305 */
2306 static void
2307 wlsetpsa(int unit)
2308 {
2309     register struct wl_softc *sc = WLSOFTC(unit);
2310     short       base = sc->base;
2311     int         i, oldpri;
2312     u_short     crc;
2313
2314     crc = wlpsacrc(sc->psa);    /* calculate CRC of PSA */
2315     sc->psa[WLPSA_CRCLOW] = crc & 0xff;
2316     sc->psa[WLPSA_CRCHIGH] = (crc >> 8) & 0xff;
2317     sc->psa[WLPSA_CRCOK] = 0x55;        /* default to 'bad' until programming complete */
2318
2319     oldpri = splimp();          /* ick, long pause */
2320     
2321     PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
2322     PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
2323     
2324     for (i = 0; i < 0x40; i++) {
2325         DELAY(DELAYCONST);
2326         outw(PIOR2(base),i);  /* write param memory */
2327         DELAY(DELAYCONST);
2328         outb(PIOP2(base), sc->psa[i]);
2329     }
2330     DELAY(DELAYCONST);
2331     outw(PIOR2(base),WLPSA_CRCOK);  /* update CRC flag*/
2332     DELAY(DELAYCONST);
2333     sc->psa[WLPSA_CRCOK] = 0xaa;        /* OK now */
2334     outb(PIOP2(base), 0xaa);    /* all OK */
2335     DELAY(DELAYCONST);
2336     
2337     PCMD(base, HACR_DEFAULT);
2338     PCMD(base, HACR_DEFAULT);
2339     
2340     splx(oldpri);
2341 }
2342
2343 /* 
2344 ** CRC routine provided by Christopher Giordano <cgiordan@gdeb.com>,
2345 ** from original code by Tomi Mikkonen (tomitm@remedy.fi)
2346 */
2347
2348 static u_int crc16_table[16] = { 
2349     0x0000, 0xCC01, 0xD801, 0x1400,
2350     0xF001, 0x3C00, 0x2800, 0xE401,
2351     0xA001, 0x6C00, 0x7800, 0xB401,
2352     0x5000, 0x9C01, 0x8801, 0x4400 
2353 };
2354
2355 static u_short
2356 wlpsacrc(u_char *buf)
2357 {
2358     u_short     crc = 0;
2359     int         i, r1;
2360     
2361     for (i = 0; i < 0x3d; i++, buf++) {
2362         /* lower 4 bits */
2363         r1 = crc16_table[crc & 0xF];
2364         crc = (crc >> 4) & 0x0FFF;
2365         crc = crc ^ r1 ^ crc16_table[*buf & 0xF];
2366         
2367         /* upper 4 bits */
2368         r1 = crc16_table[crc & 0xF];
2369         crc = (crc >> 4) & 0x0FFF;
2370         crc = crc ^ r1 ^ crc16_table[(*buf >> 4) & 0xF];
2371     }
2372     return(crc);
2373 }
2374 #ifdef WLCACHE
2375
2376 /*
2377  * wl_cache_store
2378  *
2379  * take input packet and cache various radio hw characteristics
2380  * indexed by MAC address.
2381  *
2382  * Some things to think about:
2383  *      note that no space is malloced. 
2384  *      We might hash the mac address if the cache were bigger.
2385  *      It is not clear that the cache is big enough.
2386  *              It is also not clear how big it should be.
2387  *      The cache is IP-specific.  We don't care about that as
2388  *              we want it to be IP-specific.
2389  *      The last N recv. packets are saved.  This will tend
2390  *              to reward agents and mobile hosts that beacon.
2391  *              That is probably fine for mobile ip.
2392  */
2393
2394 /* globals for wavelan signal strength cache */
2395 /* this should go into softc structure above. 
2396 */
2397
2398 /* set true if you want to limit cache items to broadcast/mcast 
2399  * only packets (not unicast)
2400  */
2401 static int wl_cache_mcastonly = 1;
2402 SYSCTL_INT(_machdep, OID_AUTO, wl_cache_mcastonly, CTLFLAG_RW, 
2403         &wl_cache_mcastonly, 0, "");
2404
2405 /* set true if you want to limit cache items to IP packets only
2406 */
2407 static int wl_cache_iponly = 1;
2408 SYSCTL_INT(_machdep, OID_AUTO, wl_cache_iponly, CTLFLAG_RW, 
2409         &wl_cache_iponly, 0, "");
2410
2411 /* zero out the cache
2412 */
2413 static void
2414 wl_cache_zero(int unit)
2415 {
2416         register struct wl_softc        *sc = WLSOFTC(unit);
2417
2418         bzero(&sc->w_sigcache[0], sizeof(struct w_sigcache) * MAXCACHEITEMS);
2419         sc->w_sigitems = 0;
2420         sc->w_nextcache = 0;
2421         sc->w_wrapindex = 0;
2422 }
2423
2424 /* store hw signal info in cache.
2425  * index is MAC address, but an ip src gets stored too
2426  * There are two filters here controllable via sysctl:
2427  *      throw out unicast (on by default, but can be turned off)
2428  *      throw out non-ip (on by default, but can be turned off)
2429  */
2430 static
2431 void wl_cache_store (int unit, int base, struct ether_header *eh,
2432                      struct mbuf *m)
2433 {
2434         struct ip *ip = NULL;   /* Avoid GCC warning */
2435         int i;
2436         int signal, silence;
2437         int w_insertcache;   /* computed index for cache entry storage */
2438         register struct wl_softc *sc = WLSOFTC(unit);
2439         int ipflag = wl_cache_iponly;
2440
2441         /* filters:
2442          * 1. ip only
2443          * 2. configurable filter to throw out unicast packets,
2444          * keep multicast only.
2445          */
2446  
2447 #ifdef INET
2448         /* reject if not IP packet
2449         */
2450         if ( wl_cache_iponly && (ntohs(eh->ether_type) != 0x800)) {
2451                 return;
2452         }
2453
2454         /* check if broadcast or multicast packet.  we toss
2455          * unicast packets
2456          */
2457         if (wl_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
2458                 return;
2459         }
2460
2461         /* find the ip header.  we want to store the ip_src
2462          * address.  use the mtod macro(in mbuf.h) 
2463          * to typecast m to struct ip *
2464          */
2465         if (ipflag) {
2466                 ip = mtod(m, struct ip *);
2467         }
2468         
2469         /* do a linear search for a matching MAC address 
2470          * in the cache table
2471          * . MAC address is 6 bytes,
2472          * . var w_nextcache holds total number of entries already cached
2473          */
2474         for (i = 0; i < sc->w_nextcache; i++) {
2475                 if (! bcmp(eh->ether_shost, sc->w_sigcache[i].macsrc,  6 )) {
2476                         /* Match!,
2477                          * so we already have this entry,
2478                          * update the data, and LRU age
2479                          */
2480                         break;  
2481                 }
2482         }
2483
2484         /* did we find a matching mac address?
2485          * if yes, then overwrite a previously existing cache entry
2486          */
2487         if (i <  sc->w_nextcache )   {
2488                 w_insertcache = i; 
2489         }
2490         /* else, have a new address entry,so
2491          * add this new entry,
2492          * if table full, then we need to replace entry
2493          */
2494         else    {                          
2495
2496                 /* check for space in cache table 
2497                  * note: w_nextcache also holds number of entries
2498                  * added in the cache table 
2499                  */
2500                 if ( sc->w_nextcache < MAXCACHEITEMS ) {
2501                         w_insertcache = sc->w_nextcache;
2502                         sc->w_nextcache++;                 
2503                         sc->w_sigitems = sc->w_nextcache;
2504                 }
2505                 /* no space found, so simply wrap with wrap index
2506                  * and "zap" the next entry
2507                  */
2508                 else {
2509                         if (sc->w_wrapindex == MAXCACHEITEMS) {
2510                                 sc->w_wrapindex = 0;
2511                         }
2512                         w_insertcache = sc->w_wrapindex++;
2513                 }
2514         }
2515
2516         /* invariant: w_insertcache now points at some slot
2517          * in cache.
2518          */
2519         if (w_insertcache < 0 || w_insertcache >= MAXCACHEITEMS) {
2520                 log(LOG_ERR, 
2521                         "wl_cache_store, bad index: %d of [0..%d], gross cache error\n",
2522                         w_insertcache, MAXCACHEITEMS);
2523                 return;
2524         }
2525
2526         /*  store items in cache
2527          *  .ipsrc
2528          *  .macsrc
2529          *  .signal (0..63) ,silence (0..63) ,quality (0..15)
2530          */
2531         if (ipflag) {
2532                 sc->w_sigcache[w_insertcache].ipsrc = ip->ip_src.s_addr;
2533         }
2534         bcopy( eh->ether_shost, sc->w_sigcache[w_insertcache].macsrc,  6);
2535         signal = sc->w_sigcache[w_insertcache].signal  = wlmmcread(base, MMC_SIGNAL_LVL) & 0x3f;
2536         silence = sc->w_sigcache[w_insertcache].silence = wlmmcread(base, MMC_SILENCE_LVL) & 0x3f;
2537         sc->w_sigcache[w_insertcache].quality = wlmmcread(base, MMC_SIGN_QUAL) & 0x0f;
2538         if (signal > 0)
2539                 sc->w_sigcache[w_insertcache].snr = 
2540                         signal - silence;
2541         else
2542                 sc->w_sigcache[w_insertcache].snr = 0;
2543 #endif /* INET */
2544
2545 }
2546 #endif /* WLCACHE */