1139749Simp/*-
226003Smsmith * Redistribution and use in source and binary forms, with or without
326003Smsmith * modification, are permitted provided that the following conditions
426003Smsmith * are met:
526003Smsmith * 1. Redistributions of source code must retain all copyright
626003Smsmith *    notices, this list of conditions and the following disclaimer.
726003Smsmith * 2. The names of the authors may not be used to endorse or promote products
897748Sschweikh *    derived from this software without specific prior written permission
926003Smsmith *
1026003Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1126003Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1226003Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1326003Smsmith * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1426003Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1526003Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1626003Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1726003Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1826003Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1926003Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2026003Smsmith *
2126003Smsmith */
2226003Smsmith/*
2326003Smsmith * if_wl.c - original MACH, then BSDI ISA wavelan driver
2426003Smsmith *	ported to mach by Anders Klemets
2526003Smsmith *	to BSDI by Robert Morris
2626003Smsmith *	to FreeBSD by Jim Binkley
2726003Smsmith *      to FreeBSD 2.2+ by Michael Smith
2826003Smsmith *
2926003Smsmith * 2.2 update:
3026003Smsmith * Changed interface to match 2.1-2.2 differences.
3126003Smsmith * Implement IRQ selection logic in wlprobe()
3226003Smsmith * Implement PSA updating.
3326003Smsmith * Pruned heading comments for relevance.
3426003Smsmith * Ripped out all the 'interface counters' cruft.
3526003Smsmith * Cut the missing-interrupt timer back to 100ms.
3627817Smsmith * 2.2.1 update:
3727817Smsmith * now supports all multicast mode (mrouted will work),
3827817Smsmith *	but unfortunately must do that by going into promiscuous mode
3927817Smsmith * NWID sysctl added so that normally promiscuous mode is NWID-specific
4027817Smsmith *	but can be made NWID-inspecific
4127817Smsmith *			7/14/97 jrb
4226003Smsmith *
4326003Smsmith * Work done:
4426003Smsmith * Ported to FreeBSD, got promiscuous mode working with bpfs,
4526003Smsmith * and rewired timer routine.  The i82586 will hang occasionally on output
4626003Smsmith * and the watchdog timer will kick it if so and log an entry.
4726003Smsmith * 2 second timeout there.  Apparently the chip loses an interrupt.
4826003Smsmith * Code borrowed from if_ie.c for watchdog timer.
4926003Smsmith *
5026003Smsmith * The wavelan card is a 2mbit radio modem that emulates ethernet;
5126003Smsmith * i.e., it uses MAC addresses.  This should not be a surprise since
5226003Smsmith * it uses an ethernet controller as a major hw item.
5326003Smsmith * It can broadcast, unicast or apparently multicast in a base cell
54108533Sschweikh * using an omni-directional antennae that is
5526003Smsmith * about 800 feet around the base cell barring walls and metal.
5626003Smsmith * With directional antennae, it can be used point to point over a mile
5726003Smsmith * or so apparently (haven't tried that).
5826003Smsmith *
5926003Smsmith * There are ISA and pcmcia versions (not supported by this code).
6026003Smsmith * The ISA card has an Intel 82586 lan controller on it.  It consists
6126003Smsmith * of 2 pieces of hw, the lan controller (intel) and a radio-modem.
6226003Smsmith * The latter has an extra set of controller registers that has nothing
6326003Smsmith * to do with the i82586 and allows setting and monitoring of radio
6426003Smsmith * signal strength, etc.  There is a nvram area called the PSA that
6526003Smsmith * contains a number of setup variables including the IRQ and so-called
6626003Smsmith * NWID or Network ID.  The NWID must be set the same for all radio
6726003Smsmith * cards to communicate (unless you are using the ATT/NCR roaming feature
6826003Smsmith * with their access points.  There is no support for that here. Roaming
6926003Smsmith * involves a link-layer beacon sent out from the access points.  End
7026003Smsmith * stations monitor the signal strength and only use the strongest
7126003Smsmith * access point).  This driver assumes that the base ISA port, IRQ,
7226003Smsmith * and NWID are first set in nvram via the dos-side "instconf.exe" utility
7326003Smsmith * supplied with the card. This driver takes the ISA port from
7426003Smsmith * the kernel configuration setup, and then determines the IRQ either
7526003Smsmith * from the kernel config (if an explicit IRQ is set) or from the
7626003Smsmith * PSA on the card if not.
7726003Smsmith * The hw also magically just uses the IRQ set in the nvram.
7826003Smsmith * The NWID is used magically as well by the radio-modem
7926003Smsmith * to determine which packets to keep or throw out.
8026003Smsmith *
8126003Smsmith * sample config:
8226003Smsmith *
8340565Sbde * device wl0 at isa? port 0x300 net irq ?
8426003Smsmith *
8526003Smsmith * Ifdefs:
8627817Smsmith * 1. WLDEBUG. (off) - if turned on enables IFF_DEBUG set via ifconfig debug
8727817Smsmith * 2. MULTICAST (on) - turned on and works up to and including mrouted
8827817Smsmith * 3. WLCACHE (off) -  define to turn on a signal strength
8927817Smsmith * (and other metric) cache that is indexed by sender MAC address.
9027817Smsmith * Apps can read this out to learn the remote signal strength of a
9127817Smsmith * sender.  Note that it has a switch so that it only stores
9227817Smsmith * broadcast/multicast senders but it could be set to store unicast
9327817Smsmith * too only.  Size is hardwired in if_wl_wavelan.h
9426003Smsmith *
9526003Smsmith * one further note: promiscuous mode is a curious thing.  In this driver,
9627817Smsmith * promiscuous mode apparently CAN catch ALL packets and ignore the NWID
9726003Smsmith * setting.  This is probably more useful in a sense (for snoopers) if
9826003Smsmith * you are interested in all traffic as opposed to if you are interested
9927817Smsmith * in just your own.  There is a driver specific sysctl to turn promiscuous
10027817Smsmith * from just promiscuous to wildly promiscuous...
10127817Smsmith *
10227817Smsmith * This driver also knows how to load the synthesizers in the 2.4 Gz
10327817Smsmith * ISA Half-card, Product number 847647476 (USA/FCC IEEE Channel set).
10427817Smsmith * This product consists of a "mothercard" that contains the 82586,
10527817Smsmith * NVRAM that holds the PSA, and the ISA-buss interface custom ASIC.
10627817Smsmith * The radio transceiver is a "daughtercard" called the WaveMODEM which
10727817Smsmith * connects to the mothercard through two single-inline connectors: a
10827817Smsmith * 20-pin connector provides DC-power and modem signals, and a 3-pin
10927817Smsmith * connector which exports the antenna connection. The code herein
11027817Smsmith * loads the receive and transmit synthesizers and the corresponding
11127817Smsmith * transmitter output power value from an EEPROM controlled through
11227817Smsmith * additional registers via the MMC. The EEPROM address selected
11327817Smsmith * are those whose values are preset by the DOS utility programs
11427817Smsmith * provided with the product, and this provides compatible operation
11527817Smsmith * with the DOS Packet Driver software. A future modification will
11627817Smsmith * add the necessary functionality to this driver and to the wlconfig
11727817Smsmith * utility to completely replace the DOS Configuration Utilities.
11827817Smsmith * The 2.4 Gz WaveMODEM is described in document number 407-024692/E,
11927817Smsmith * and is available through Lucent Technologies OEM supply channels.
12027817Smsmith * --RAB 1997/06/08.
12126003Smsmith */
12226003Smsmith
12326003Smsmith#define MULTICAST  1
12426003Smsmith
12526003Smsmith/*
12626003Smsmith *	Olivetti PC586 Mach Ethernet driver v1.0
12726003Smsmith *	Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
12826003Smsmith *	All rights reserved.
12926003Smsmith *
13026003Smsmith */
13126003Smsmith/*
13226003Smsmith  Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
13326003SmsmithCupertino, California.
13426003Smsmith
13526003Smsmith		All Rights Reserved
13626003Smsmith
13726003Smsmith  Permission to use, copy, modify, and distribute this software and
13826003Smsmithits documentation for any purpose and without fee is hereby
13926003Smsmithgranted, provided that the above copyright notice appears in all
14026003Smsmithcopies and that both the copyright notice and this permission notice
14126003Smsmithappear in supporting documentation, and that the name of Olivetti
14226003Smsmithnot be used in advertising or publicity pertaining to distribution
14326003Smsmithof the software without specific, written prior permission.
14426003Smsmith
14526003Smsmith  OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
14626003SmsmithINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
14726003SmsmithIN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
14826003SmsmithCONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14926003SmsmithLOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
15026003SmsmithNEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
15126003SmsmithWITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15226003Smsmith*/
15326003Smsmith/*
15426003Smsmith  Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
15526003Smsmith
15626003Smsmith		All Rights Reserved
15726003Smsmith
15826003SmsmithPermission to use, copy, modify, and distribute this software and
15926003Smsmithits documentation for any purpose and without fee is hereby
16026003Smsmithgranted, provided that the above copyright notice appears in all
16126003Smsmithcopies and that both the copyright notice and this permission notice
16226003Smsmithappear in supporting documentation, and that the name of Intel
16326003Smsmithnot be used in advertising or publicity pertaining to distribution
16426003Smsmithof the software without specific, written prior permission.
16526003Smsmith
16626003SmsmithINTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
16726003SmsmithINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
16826003SmsmithIN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
16926003SmsmithCONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17026003SmsmithLOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
17126003SmsmithNEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
17226003SmsmithWITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17326003Smsmith*/
17426003Smsmith
175122625Sobrien#include <sys/cdefs.h>
176122625Sobrien__FBSDID("$FreeBSD: stable/10/sys/dev/wl/if_wl.c 320923 2017-07-12 22:16:54Z jhb $");
177122625Sobrien
17826003Smsmith/*
17926003Smsmith * NOTE:
18026003Smsmith *		by rvb:
18126003Smsmith *  1.	The best book on the 82586 is:
18226003Smsmith *		LAN Components User's Manual by Intel
18326003Smsmith *	The copy I found was dated 1984.  This really tells you
18426003Smsmith *	what the state machines are doing
18526003Smsmith *  2.	In the current design, we only do one write at a time,
18626003Smsmith *	though the hardware is capable of chaining and possibly
18726003Smsmith *	even batching.  The problem is that we only make one
18826003Smsmith *	transmit buffer available in sram space.
18926003Smsmith */
19026003Smsmith
19126109Speter#include "opt_wavelan.h"
19232350Seivind#include "opt_inet.h"
19326003Smsmith
19426003Smsmith#include <sys/param.h>
19526003Smsmith#include <sys/systm.h>
19661011Speter#include <sys/kernel.h>
197129879Sphk#include <sys/module.h>
19826114Speter#include <sys/sockio.h>
19926003Smsmith#include <sys/mbuf.h>
200164033Srwatson#include <sys/priv.h>
20126003Smsmith#include <sys/socket.h>
20226003Smsmith#include <sys/syslog.h>
203113571Sjhay#include <machine/bus.h>
204113571Sjhay#include <machine/resource.h>
20561011Speter#include <sys/bus.h>
206113571Sjhay#include <sys/rman.h>
20726003Smsmith
20826003Smsmith#include <sys/sysctl.h>
20926003Smsmith
21050026Smdodd#include <net/ethernet.h>
21126003Smsmith#include <net/if.h>
212113571Sjhay#include <net/if_arp.h>
21326003Smsmith#include <net/if_dl.h>
214147256Sbrooks#include <net/if_types.h>
21526003Smsmith
21626003Smsmith#ifdef INET
21726003Smsmith#include <netinet/in.h>
21827817Smsmith#include <netinet/in_systm.h>
21927817Smsmith#include <netinet/ip.h>
22026003Smsmith#include <netinet/if_ether.h>
22126003Smsmith#endif
22226003Smsmith
22326003Smsmith#include <net/bpf.h>
224113571Sjhay#include <isa/isavar.h>
22526003Smsmith
22626003Smsmith/* was 1000 in original, fed to DELAY(x) */
22726003Smsmith#define DELAYCONST	1000
228146019Snyan#include <dev/wl/if_wl_i82586.h>	/* Definitions for the Intel chip */
22979080Simp#include <dev/wl/if_wl.h>
23026003Smsmith#include <machine/if_wl_wavelan.h>
23126003Smsmith
23226003Smsmithstatic char	t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)];
23326003Smsmith
23426003Smsmithstruct wl_softc{
235147256Sbrooks    struct	ifnet	*ifp;
23626003Smsmith    u_char	psa[0x40];
23726003Smsmith    u_char	nwid[2];	/* current radio modem nwid */
23826003Smsmith    short	base;
23926003Smsmith    short	unit;
24026003Smsmith    int		flags;
24126003Smsmith    int		tbusy;		/* flag to determine if xmit is busy */
24226003Smsmith    u_short	begin_fd;
24326003Smsmith    u_short	end_fd;
24426003Smsmith    u_short	end_rbd;
24526003Smsmith    u_short	hacr;		/* latest host adapter CR command */
24626003Smsmith    short	mode;
24727817Smsmith    u_char      chan24;         /* 2.4 Gz: channel number/EEPROM Area # */
24827817Smsmith    u_short     freq24;         /* 2.4 Gz: resulting frequency  */
249113571Sjhay    int		rid_ioport;
250113571Sjhay    int		rid_irq;
251113571Sjhay    struct resource	*res_ioport;
252113571Sjhay    struct resource	*res_irq;
253113571Sjhay    void		*intr_cookie;
254113571Sjhay    bus_space_tag_t	bt;
255113571Sjhay    bus_space_handle_t	bh;
256113572Sjhay    struct mtx		wl_mtx;
257113571Sjhay    struct callout_handle	watchdog_ch;
25827817Smsmith#ifdef WLCACHE
25927817Smsmith    int 	w_sigitems;     /* number of cached entries */
26027817Smsmith    /*  array of cache entries */
26127817Smsmith    struct w_sigcache w_sigcache[ MAXCACHEITEMS ];
26227817Smsmith    int w_nextcache;            /* next free cache entry */
26327817Smsmith    int w_wrapindex;   		/* next "free" cache entry */
26427817Smsmith#endif
26526003Smsmith};
26626003Smsmith
267113572Sjhay#define WL_LOCK(_sc)	mtx_lock(&(_sc)->wl_mtx)
268122689Ssam#define WL_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->wl_mtx, MA_OWNED)
269113572Sjhay#define WL_UNLOCK(_sc)	mtx_unlock(&(_sc)->wl_mtx)
270113572Sjhay
271113571Sjhaystatic int	wlprobe(device_t);
272113571Sjhaystatic int	wlattach(device_t);
273113571Sjhaystatic int	wldetach(device_t);
27426003Smsmith
275113571Sjhaystatic device_method_t wl_methods[] = {
276113571Sjhay	DEVMETHOD(device_probe,		wlprobe),
277113571Sjhay	DEVMETHOD(device_attach,	wlattach),
278113571Sjhay	DEVMETHOD(device_detach,	wldetach),
279113571Sjhay	{ 0, 0}
280113571Sjhay};
28126003Smsmith
282113571Sjhaystatic driver_t wl_driver = {
28361011Speter	"wl",
284113571Sjhay	wl_methods,
285113571Sjhay	sizeof (struct wl_softc)
28626003Smsmith};
28726003Smsmith
288113571Sjhaydevclass_t wl_devclass;
289113571SjhayDRIVER_MODULE(wl, isa, wl_driver, wl_devclass, 0, 0);
290113571SjhayMODULE_DEPEND(wl, isa, 1, 1, 1);
291113571SjhayMODULE_DEPEND(wl, ether, 1, 1, 1);
292113571Sjhay
293113571Sjhaystatic struct isa_pnp_id wl_ids[] = {
294113571Sjhay	{0,		NULL}
295113571Sjhay};
296113571Sjhay
29726003Smsmith/*
29826003Smsmith * XXX  The Wavelan appears to be prone to dropping stuff if you talk to
29926003Smsmith * it too fast.  This disgusting hack inserts a delay after each packet
30027817Smsmith * is queued which helps avoid this behaviour on fast systems.
30126003Smsmith */
30227817Smsmithstatic int	wl_xmit_delay = 250;
30326003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_xmit_delay, CTLFLAG_RW, &wl_xmit_delay, 0, "");
30426003Smsmith
30527817Smsmith/*
30627817Smsmith * not XXX, but ZZZ (bizarre).
30727817Smsmith * promiscuous mode can be toggled to ignore NWIDs.  By default,
30827817Smsmith * it does not.  Caution should be exercised about combining
30927817Smsmith * this mode with IFF_ALLMULTI which puts this driver in
31027817Smsmith * promiscuous mode.
31127817Smsmith */
31227817Smsmithstatic int	wl_ignore_nwid = 0;
31327817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_ignore_nwid, CTLFLAG_RW, &wl_ignore_nwid, 0, "");
31427817Smsmith
31526003Smsmith/*
31626003Smsmith * Emit diagnostics about transmission problems
31726003Smsmith */
31826003Smsmithstatic int	xmt_watch = 0;
31926003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_xmit_watch, CTLFLAG_RW, &xmt_watch, 0, "");
32026003Smsmith
32126003Smsmith/*
32226003Smsmith * Collect SNR statistics
32326003Smsmith */
32426003Smsmithstatic int	gathersnr = 0;
32526003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_gather_snr, CTLFLAG_RW, &gathersnr, 0, "");
32626003Smsmith
327113571Sjhaystatic int	wl_allocate_resources(device_t device);
328113571Sjhaystatic int	wl_deallocate_resources(device_t device);
32926003Smsmithstatic void	wlstart(struct ifnet *ifp);
33026003Smsmithstatic void	wlinit(void *xsc);
33136735Sdfrstatic int	wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
33227839Smsmithstatic timeout_t wlwatchdog;
333113571Sjhaystatic void	wlintr(void *arg);
334113571Sjhaystatic void	wlxmt(struct wl_softc *sc, struct mbuf *m);
335113571Sjhaystatic int	wldiag(struct wl_softc *sc);
336113571Sjhaystatic int	wlconfig(struct wl_softc *sc);
337113571Sjhaystatic int	wlcmd(struct wl_softc *sc, char *str);
338113571Sjhaystatic void	wlmmcstat(struct wl_softc *sc);
339113571Sjhaystatic u_short	wlbldru(struct wl_softc *sc);
34026003Smsmithstatic u_short	wlmmcread(u_int base, u_short reg);
341113571Sjhaystatic void	wlinitmmc(struct wl_softc *sc);
342113571Sjhaystatic int	wlhwrst(struct wl_softc *sc);
343113571Sjhaystatic void	wlrustrt(struct wl_softc *sc);
344113571Sjhaystatic void	wlbldcu(struct wl_softc *sc);
345113571Sjhaystatic int	wlack(struct wl_softc *sc);
346113571Sjhaystatic int	wlread(struct wl_softc *sc, u_short fd_p);
347113571Sjhaystatic void	getsnr(struct wl_softc *sc);
348113571Sjhaystatic void	wlrcv(struct wl_softc *sc);
349113571Sjhaystatic int	wlrequeue(struct wl_softc *sc, u_short fd_p);
350113571Sjhaystatic void	wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc);
351113571Sjhaystatic void	wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc);
35279081Simp#ifdef WLDEBUG
353113571Sjhaystatic void	wltbd(struct wl_softc *sc);
35479081Simp#endif
35526003Smsmithstatic void	wlgetpsa(int base, u_char *buf);
356113571Sjhaystatic void	wlsetpsa(struct wl_softc *sc);
35726003Smsmithstatic u_short	wlpsacrc(u_char *buf);
358113571Sjhaystatic void	wldump(struct wl_softc *sc);
35927817Smsmith#ifdef WLCACHE
360113571Sjhaystatic void	wl_cache_store(struct wl_softc *, int, struct ether_header *, struct mbuf *);
361113571Sjhaystatic void     wl_cache_zero(struct wl_softc *sc);
36227817Smsmith#endif
36326003Smsmith
36426003Smsmith/* array for maping irq numbers to values for the irq parameter register */
36526003Smsmithstatic int irqvals[16] = {
36626003Smsmith    0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80
36726003Smsmith};
36826003Smsmith
36926003Smsmith/*
37026003Smsmith * wlprobe:
37126003Smsmith *
37226003Smsmith *	This function "probes" or checks for the WaveLAN board on the bus to
37326003Smsmith *	see if it is there.  As far as I can tell, the best break between this
37426003Smsmith *	routine and the attach code is to simply determine whether the board
37526003Smsmith *	is configured in properly.  Currently my approach to this is to write
37626003Smsmith *	and read a word from the SRAM on the board being probed.  If the word
37726003Smsmith *	comes back properly then we assume the board is there.  The config
37826003Smsmith *	code expects to see a successful return from the probe routine before
37926003Smsmith *	attach will be called.
38026003Smsmith *
38126003Smsmith * input	: address device is mapped to, and unit # being checked
38226003Smsmith * output	: a '1' is returned if the board exists, and a 0 otherwise
38326003Smsmith *
38426003Smsmith */
38526003Smsmithstatic int
386113571Sjhaywlprobe(device_t device)
38726003Smsmith{
388113571Sjhay    struct wl_softc	*sc;
389113571Sjhay    short		base;
39026003Smsmith    char		*str = "wl%d: board out of range [0..%d]\n";
39126003Smsmith    u_char		inbuf[100];
392113601Sjhay    unsigned long	junk, oldpri, sirq;
393113571Sjhay    int			error, irq;
39426003Smsmith
395113571Sjhay    error = ISA_PNP_PROBE(device_get_parent(device), device, wl_ids);
396113571Sjhay    if (error == ENXIO || error == 0)
397113571Sjhay	return (error);
398113571Sjhay
399113571Sjhay    sc = device_get_softc(device);
400113571Sjhay    error = wl_allocate_resources(device);
401113571Sjhay    if (error)
402113571Sjhay	goto errexit;
403113571Sjhay
404113571Sjhay    base = rman_get_start(sc->res_ioport);
405113571Sjhay
40626003Smsmith    /* TBD. not true.
40726003Smsmith     * regular CMD() will not work, since no softc yet
40826003Smsmith     */
40926003Smsmith#define PCMD(base, hacr) outw((base), (hacr))
41026003Smsmith
411113601Sjhay    oldpri = splimp();
41226003Smsmith    PCMD(base, HACR_RESET);			/* reset the board */
41326003Smsmith    DELAY(DELAYCONST);				/* >> 4 clocks at 6MHz */
41426003Smsmith    PCMD(base, HACR_RESET);			/* reset the board */
41526003Smsmith    DELAY(DELAYCONST);	                	/* >> 4 clocks at 6MHz */
416113601Sjhay    splx(oldpri);
41726003Smsmith
41826003Smsmith    /* clear reset command and set PIO#1 in autoincrement mode */
41926003Smsmith    PCMD(base, HACR_DEFAULT);
42026003Smsmith    PCMD(base, HACR_DEFAULT);
42126003Smsmith    outw(PIOR1(base), 0);			/* go to beginning of RAM */
42226003Smsmith    outsw(PIOP1(base), str, strlen(str)/2+1);	/* write string */
42326003Smsmith
42426003Smsmith    outw(PIOR1(base), 0);			/* rewind */
42526003Smsmith    insw(PIOP1(base), inbuf, strlen(str)/2+1);	/* read result */
42626003Smsmith
427113571Sjhay    if (bcmp(str, inbuf, strlen(str))) {
428113571Sjhay	error = ENXIO;
429113571Sjhay	goto errexit;
430113571Sjhay    }
43126003Smsmith
43227817Smsmith    sc->chan24 = 0;                             /* 2.4 Gz: config channel */
43327817Smsmith    sc->freq24 = 0;                             /* 2.4 Gz: frequency    */
43427817Smsmith
43526003Smsmith    /* read the PSA from the board into temporary storage */
43626003Smsmith    wlgetpsa(base, inbuf);
43726003Smsmith
43826003Smsmith    /* We read the IRQ value from the PSA on the board. */
43926003Smsmith    for (irq = 15; irq >= 0; irq--)
44026003Smsmith	if (irqvals[irq] == inbuf[WLPSA_IRQNO])
44126003Smsmith	    break;
44226003Smsmith    if ((irq == 0) || (irqvals[irq] == 0)){
443113571Sjhay	printf("wl%d: PSA corrupt (invalid IRQ value)\n",
444113571Sjhay	    device_get_unit(device));
44526003Smsmith    } else {
44626003Smsmith	/*
44726003Smsmith	 * If the IRQ requested by the PSA is already claimed by another
44826003Smsmith	 * device, the board won't work, but the user can still access the
44926003Smsmith	 * driver to change the IRQ.
45026003Smsmith	 */
451113571Sjhay	if (bus_get_resource(device, SYS_RES_IRQ, 0, &sirq, &junk))
452113571Sjhay	    goto errexit;
453113571Sjhay	if (irq != (int)sirq)
454113571Sjhay	    printf("wl%d: board is configured for interrupt %d\n",
455113571Sjhay		device_get_unit(device), irq);
45626003Smsmith    }
457113571Sjhay    wl_deallocate_resources(device);
458113571Sjhay    return (0);
459113571Sjhay
460113571Sjhayerrexit:
461113571Sjhay    wl_deallocate_resources(device);
462113571Sjhay    return (error);
46326003Smsmith}
46426003Smsmith
46526003Smsmith/*
46626003Smsmith * wlattach:
46726003Smsmith *
46826003Smsmith *	This function attaches a WaveLAN board to the "system".  The rest of
46926003Smsmith *	runtime structures are initialized here (this routine is called after
47026003Smsmith *	a successful probe of the board).  Once the ethernet address is read
47126003Smsmith *	and stored, the board's ifnet structure is attached and readied.
47226003Smsmith *
47326003Smsmith * input	: isa_dev structure setup in autoconfig
47426003Smsmith * output	: board structs and ifnet is setup
47526003Smsmith *
47626003Smsmith */
47726003Smsmithstatic int
478113571Sjhaywlattach(device_t device)
47926003Smsmith{
480113571Sjhay    struct wl_softc	*sc;
481113571Sjhay    short		base;
482113571Sjhay    int			error, i, j;
483113571Sjhay    int			unit;
484113571Sjhay    struct ifnet	*ifp;
485147256Sbrooks    u_char		eaddr[6];
48626003Smsmith
487113571Sjhay    sc = device_get_softc(device);
488147256Sbrooks    ifp = sc->ifp = if_alloc(IFT_ETHER);
489147256Sbrooks    if (ifp == NULL) {
490147256Sbrooks	device_printf(device, "can not if_alloc()\n");
491147256Sbrooks	return (ENOSPC);
492147256Sbrooks    }
493113571Sjhay
494113572Sjhay    mtx_init(&sc->wl_mtx, device_get_nameunit(device), MTX_NETWORK_LOCK,
495113572Sjhay	MTX_DEF | MTX_RECURSE);
496113572Sjhay
497113571Sjhay    error = wl_allocate_resources(device);
498113571Sjhay    if (error) {
499113571Sjhay	wl_deallocate_resources(device);
500113571Sjhay	return (ENXIO);
501113571Sjhay    }
502113571Sjhay
503113571Sjhay    base = rman_get_start(sc->res_ioport);
504113571Sjhay    unit = device_get_unit(device);
505113571Sjhay
50626003Smsmith#ifdef WLDEBUG
50726003Smsmith    printf("wlattach: base %x, unit %d\n", base, unit);
50826003Smsmith#endif
509113571Sjhay
51026003Smsmith    sc->base = base;
51126003Smsmith    sc->unit = unit;
51226003Smsmith    sc->flags = 0;
51326003Smsmith    sc->mode = 0;
51426003Smsmith    sc->hacr = HACR_RESET;
51529677Sgibbs    callout_handle_init(&sc->watchdog_ch);
516113571Sjhay    CMD(sc);				/* reset the board */
51726003Smsmith    DELAY(DELAYCONST);	                /* >> 4 clocks at 6MHz */
51826003Smsmith
51926003Smsmith    /* clear reset command and set PIO#2 in parameter access mode */
52026003Smsmith    sc->hacr = (HACR_DEFAULT & ~HACR_16BITS);
521113571Sjhay    CMD(sc);
52226003Smsmith
52326003Smsmith    /* Read the PSA from the board for our later reference */
52426003Smsmith    wlgetpsa(base, sc->psa);
52526003Smsmith
52626003Smsmith    /* fetch NWID */
52726003Smsmith    sc->nwid[0] = sc->psa[WLPSA_NWID];
52826003Smsmith    sc->nwid[1] = sc->psa[WLPSA_NWID+1];
52926003Smsmith
53026003Smsmith    /* fetch MAC address - decide which one first */
53179082Simp    if (sc->psa[WLPSA_MACSEL] & 1)
53226003Smsmith	j = WLPSA_LOCALMAC;
53379082Simp    else
53426003Smsmith	j = WLPSA_UNIMAC;
53579082Simp    for (i=0; i < WAVELAN_ADDR_SIZE; ++i)
536147256Sbrooks	eaddr[i] = sc->psa[j + i];
53726003Smsmith
53826003Smsmith    /* enter normal 16 bit mode operation */
53926003Smsmith    sc->hacr = HACR_DEFAULT;
540113571Sjhay    CMD(sc);
54126003Smsmith
542113571Sjhay    wlinitmmc(sc);
54326003Smsmith    outw(PIOR1(base), OFFSET_SCB + 8);	/* address of scb_crcerrs */
54426003Smsmith    outw(PIOP1(base), 0);			/* clear scb_crcerrs */
54526003Smsmith    outw(PIOP1(base), 0);			/* clear scb_alnerrs */
54626003Smsmith    outw(PIOP1(base), 0);			/* clear scb_rscerrs */
54726003Smsmith    outw(PIOP1(base), 0);			/* clear scb_ovrnerrs */
54826003Smsmith
54926003Smsmith    ifp->if_softc = sc;
55026003Smsmith    ifp->if_mtu = WAVELAN_MTU;
55126003Smsmith    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
55226003Smsmith#ifdef    WLDEBUG
55326003Smsmith    ifp->if_flags |= IFF_DEBUG;
55426003Smsmith#endif
55526003Smsmith#if	MULTICAST
55626003Smsmith    ifp->if_flags |= IFF_MULTICAST;
55741616Seivind#endif	/* MULTICAST */
558121816Sbrooks    if_initname(ifp, device_get_name(device), device_get_unit(device));
55926003Smsmith    ifp->if_init = wlinit;
56026003Smsmith    ifp->if_start = wlstart;
56126003Smsmith    ifp->if_ioctl = wlioctl;
562207554Ssobomax    ifp->if_snd.ifq_maxlen = ifqmaxlen;
56326003Smsmith    /* no entries
56426003Smsmith       ifp->if_done
56526003Smsmith       ifp->if_reset
56626003Smsmith       */
567147256Sbrooks    ether_ifattach(ifp, eaddr);
56826003Smsmith
569126966Smdodd    if_printf(ifp, "NWID 0x%02x%02x", sc->nwid[0], sc->nwid[1]);
57027817Smsmith    if (sc->freq24)
57127817Smsmith	printf(", Freq %d MHz",sc->freq24); 		/* 2.4 Gz       */
57227817Smsmith    printf("\n");                                       /* 2.4 Gz       */
57326003Smsmith
574166923Spiso    bus_setup_intr(device, sc->res_irq, INTR_TYPE_NET, NULL, wlintr, sc, &sc->intr_cookie);
57527817Smsmith
57626003Smsmith    if (bootverbose)
577113571Sjhay	wldump(sc);
578320923Sjhb    device_printf(device,
579320923Sjhb	"WARNING: This driver is deprecated and will be removed.\n");
580113571Sjhay    return (0);
58126003Smsmith}
58226003Smsmith
583113571Sjhaystatic int
584113571Sjhaywldetach(device_t device)
585113571Sjhay{
586113571Sjhay    struct wl_softc *sc = device_get_softc(device);
587113571Sjhay    device_t parent = device_get_parent(device);
588113571Sjhay    struct ifnet *ifp;
589113571Sjhay
590147256Sbrooks    ifp = sc->ifp;
591113571Sjhay    ether_ifdetach(ifp);
592113571Sjhay
593113572Sjhay    WL_LOCK(sc);
594113572Sjhay
595113571Sjhay    /* reset the board */
596113571Sjhay    sc->hacr = HACR_RESET;
597113571Sjhay    CMD(sc);
598113571Sjhay    sc->hacr = HACR_DEFAULT;
599113571Sjhay    CMD(sc);
600113571Sjhay
601113571Sjhay    if (sc->intr_cookie != NULL) {
602113571Sjhay	BUS_TEARDOWN_INTR(parent, device, sc->res_irq, sc->intr_cookie);
603113571Sjhay	sc->intr_cookie = NULL;
604113571Sjhay    }
605113571Sjhay
606113571Sjhay    bus_generic_detach(device);
607113571Sjhay    wl_deallocate_resources(device);
608113572Sjhay    WL_UNLOCK(sc);
609150306Simp    if_free(ifp);
610113572Sjhay    mtx_destroy(&sc->wl_mtx);
611113571Sjhay    return (0);
612113571Sjhay}
613113571Sjhay
614113571Sjhaystatic int
615113571Sjhaywl_allocate_resources(device_t device)
616113571Sjhay{
617113571Sjhay    struct wl_softc *sc = device_get_softc(device);
618113571Sjhay    int ports = 16;		/* Number of ports */
619113571Sjhay
620113571Sjhay    sc->res_ioport = bus_alloc_resource(device, SYS_RES_IOPORT,
621113571Sjhay	&sc->rid_ioport, 0ul, ~0ul, ports, RF_ACTIVE);
622113571Sjhay    if (sc->res_ioport == NULL)
623113571Sjhay	goto errexit;
624113571Sjhay
625127135Snjl    sc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ,
626127135Snjl	&sc->rid_irq, RF_SHAREABLE|RF_ACTIVE);
627113571Sjhay    if (sc->res_irq == NULL)
628113571Sjhay	goto errexit;
629113571Sjhay    return (0);
630113571Sjhay
631113571Sjhayerrexit:
632113571Sjhay    wl_deallocate_resources(device);
633113571Sjhay    return (ENXIO);
634113571Sjhay}
635113571Sjhay
636113571Sjhaystatic int
637113571Sjhaywl_deallocate_resources(device_t device)
638113571Sjhay{
639113571Sjhay    struct wl_softc *sc = device_get_softc(device);
640113571Sjhay
641113571Sjhay    if (sc->res_irq != 0) {
642113571Sjhay	bus_release_resource(device, SYS_RES_IRQ,
643113571Sjhay	    sc->rid_irq, sc->res_irq);
644113571Sjhay	sc->res_irq = 0;
645113571Sjhay    }
646113571Sjhay    if (sc->res_ioport != 0) {
647113571Sjhay	bus_release_resource(device, SYS_RES_IOPORT,
648113571Sjhay	    sc->rid_ioport, sc->res_ioport);
649113571Sjhay	sc->res_ioport = 0;
650113571Sjhay    }
651113571Sjhay    return (0);
652113571Sjhay}
653113571Sjhay
65426003Smsmith/*
65526003Smsmith * Print out interesting information about the 82596.
65626003Smsmith */
65726003Smsmithstatic void
658113571Sjhaywldump(struct wl_softc *sc)
65926003Smsmith{
660113571Sjhay    int		base = sc->base;
66126003Smsmith    int		i;
66226003Smsmith
66326003Smsmith    printf("hasr %04x\n", inw(HASR(base)));
66426003Smsmith
66526003Smsmith    printf("scb at %04x:\n ", OFFSET_SCB);
66626003Smsmith    outw(PIOR1(base), OFFSET_SCB);
66779082Simp    for (i = 0; i < 8; i++)
66826003Smsmith	printf("%04x ", inw(PIOP1(base)));
66926003Smsmith    printf("\n");
67026003Smsmith
67126003Smsmith    printf("cu at %04x:\n ", OFFSET_CU);
67226003Smsmith    outw(PIOR1(base), OFFSET_CU);
67379082Simp    for (i = 0; i < 8; i++)
67426003Smsmith	printf("%04x ", inw(PIOP1(base)));
67526003Smsmith    printf("\n");
67626003Smsmith
67726003Smsmith    printf("tbd at %04x:\n ", OFFSET_TBD);
67826003Smsmith    outw(PIOR1(base), OFFSET_TBD);
67979082Simp    for (i = 0; i < 4; i++)
68026003Smsmith	printf("%04x ", inw(PIOP1(base)));
68126003Smsmith    printf("\n");
68226003Smsmith}
68326003Smsmith
68426003Smsmith/* Initialize the Modem Management Controller */
68526003Smsmithstatic void
686113571Sjhaywlinitmmc(struct wl_softc *sc)
68726003Smsmith{
688113571Sjhay    int		base = sc->base;
68926003Smsmith    int		configured;
690113571Sjhay    int		mode = sc->mode;
69127817Smsmith    int         i;                              /* 2.4 Gz               */
69226003Smsmith
69326003Smsmith    /* enter 8 bit operation */
694113571Sjhay    sc->hacr = (HACR_DEFAULT & ~HACR_16BITS);
695113571Sjhay    CMD(sc);
69626003Smsmith
697113571Sjhay    configured = sc->psa[WLPSA_CONFIGURED] & 1;
69826003Smsmith
69926003Smsmith    /*
70026003Smsmith     * Set default modem control parameters.  Taken from NCR document
70126003Smsmith     *  407-0024326 Rev. A
70226003Smsmith     */
70326003Smsmith    MMC_WRITE(MMC_JABBER_ENABLE, 0x01);
70426003Smsmith    MMC_WRITE(MMC_ANTEN_SEL, 0x02);
70526003Smsmith    MMC_WRITE(MMC_IFS, 0x20);
70626003Smsmith    MMC_WRITE(MMC_MOD_DELAY, 0x04);
70726003Smsmith    MMC_WRITE(MMC_JAM_TIME, 0x38);
70826003Smsmith    MMC_WRITE(MMC_DECAY_PRM, 0x00);		/* obsolete ? */
70926003Smsmith    MMC_WRITE(MMC_DECAY_UPDAT_PRM, 0x00);
71026003Smsmith    if (!configured) {
71126003Smsmith	MMC_WRITE(MMC_LOOPT_SEL, 0x00);
712113571Sjhay	if (sc->psa[WLPSA_COMPATNO] & 1) {
71326003Smsmith	    MMC_WRITE(MMC_THR_PRE_SET, 0x01);	/* 0x04 for AT and 0x01 for MCA */
71426003Smsmith	} else {
71526003Smsmith	    MMC_WRITE(MMC_THR_PRE_SET, 0x04);	/* 0x04 for AT and 0x01 for MCA */
71626003Smsmith	}
71726003Smsmith	MMC_WRITE(MMC_QUALITY_THR, 0x03);
71826003Smsmith    } else {
71926003Smsmith	/* use configuration defaults from parameter storage area */
720113571Sjhay	if (sc->psa[WLPSA_NWIDENABLE] & 1) {
72127817Smsmith	    if ((mode & (MOD_PROM | MOD_ENAL)) && wl_ignore_nwid) {
72226003Smsmith		MMC_WRITE(MMC_LOOPT_SEL, 0x40);
72326003Smsmith	    } else {
72426003Smsmith		MMC_WRITE(MMC_LOOPT_SEL, 0x00);
72526003Smsmith	    }
72626003Smsmith	} else {
72726003Smsmith	    MMC_WRITE(MMC_LOOPT_SEL, 0x40);	/* disable network id check */
72826003Smsmith	}
729113571Sjhay	MMC_WRITE(MMC_THR_PRE_SET, sc->psa[WLPSA_THRESH]);
730113571Sjhay	MMC_WRITE(MMC_QUALITY_THR, sc->psa[WLPSA_QUALTHRESH]);
73126003Smsmith    }
73226003Smsmith    MMC_WRITE(MMC_FREEZE, 0x00);
73326003Smsmith    MMC_WRITE(MMC_ENCR_ENABLE, 0x00);
73426003Smsmith
735113571Sjhay    MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]);	/* set NWID */
736113571Sjhay    MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]);
73726003Smsmith
73826003Smsmith    /* enter normal 16 bit mode operation */
739113571Sjhay    sc->hacr = HACR_DEFAULT;
740113571Sjhay    CMD(sc);
741113571Sjhay    CMD(sc);					/* virtualpc1 needs this! */
74227817Smsmith
743113571Sjhay    if (sc->psa[WLPSA_COMPATNO]==		/* 2.4 Gz: half-card ver     */
74427817Smsmith		WLPSA_COMPATNO_WL24B) {		/* 2.4 Gz		     */
745113571Sjhay	i=sc->chan24<<4;			/* 2.4 Gz: position ch #     */
74627817Smsmith	MMC_WRITE(MMC_EEADDR,i+0x0f);		/* 2.4 Gz: named ch, wc=16   */
74727817Smsmith	MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+	/* 2.4 Gz: Download Synths   */
74827817Smsmith			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz: Read EEPROM	     */
74927817Smsmith	for (i=0; i<1000; ++i) {		/* 2.4 Gz: wait for download */
75027817Smsmith	    DELAY(40);				/* 2.4 Gz	      */
75127817Smsmith	    if ((wlmmcread(base,MMC_EECTRLstat)	/* 2.4 Gz: check DWLD and    */
75227817Smsmith		&(MMC_EECTRLstat_DWLD		/* 2.4 Gz:	 EEBUSY	     */
75327817Smsmith		 +MMC_EECTRLstat_EEBUSY))==0)	/* 2.4 Gz:		     */
75427817Smsmith		break;				/* 2.4 Gz: download finished */
75527817Smsmith	}					/* 2.4 Gz		     */
75627817Smsmith	if (i==1000) printf("wl: synth load failed\n"); /* 2.4 Gz	*/
75727817Smsmith	MMC_WRITE(MMC_EEADDR,0x61);		/* 2.4 Gz: default pwr, wc=2 */
75827817Smsmith	MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+	/* 2.4 Gz: Download Xmit Pwr */
75927817Smsmith			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz: Read EEPROM	     */
76027817Smsmith	for (i=0; i<1000; ++i) {		/* 2.4 Gz: wait for download */
76127817Smsmith	    DELAY(40);				/* 2.4 Gz	      */
76227817Smsmith	    if ((wlmmcread(base,MMC_EECTRLstat)	/* 2.4 Gz: check DWLD and    */
76327817Smsmith		&(MMC_EECTRLstat_DWLD		/* 2.4 Gz:	 EEBUSY	     */
76427817Smsmith		 +MMC_EECTRLstat_EEBUSY))==0)	/* 2.4 Gz:		     */
76527817Smsmith		break;				/* 2.4 Gz: download finished */
76627817Smsmith	}					/* 2.4 Gz		     */
76727817Smsmith	if (i==1000) printf("wl: xmit pwr load failed\n"); /* 2.4 Gz	     */
76827817Smsmith	MMC_WRITE(MMC_ANALCTRL,			/* 2.4 Gz: EXT ant+polarity  */
76927817Smsmith			MMC_ANALCTRL_ANTPOL +	/* 2.4 Gz:		     */
77027817Smsmith			MMC_ANALCTRL_EXTANT);	/* 2.4 Gz:		     */
771113571Sjhay	i=sc->chan24<<4;			/* 2.4 Gz: position ch #     */
77227817Smsmith	MMC_WRITE(MMC_EEADDR,i);		/* 2.4 Gz: get frequency     */
77327817Smsmith	MMC_WRITE(MMC_EECTRL,			/* 2.4 Gz: EEPROM read	    */
77427817Smsmith			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz:		    */
77527817Smsmith	DELAY(40);				/* 2.4 Gz		     */
77627817Smsmith	i = wlmmcread(base,MMC_EEDATALrv)	/* 2.4 Gz: freq val	     */
77727817Smsmith	  + (wlmmcread(base,MMC_EEDATAHrv)<<8);	/* 2.4 Gz		     */
778113571Sjhay	sc->freq24 = (i>>6)+2400;		/* 2.4 Gz: save real freq    */
77927817Smsmith    }
78026003Smsmith}
78126003Smsmith
78226003Smsmith/*
78326003Smsmith * wlinit:
78426003Smsmith *
78526003Smsmith *	Another routine that interfaces the "if" layer to this driver.
78626003Smsmith *	Simply resets the structures that are used by "upper layers".
78726003Smsmith *	As well as calling wlhwrst that does reset the WaveLAN board.
78826003Smsmith *
78926003Smsmith * input	: softc pointer for this interface
79026003Smsmith * output	: structures (if structs) and board are reset
79126003Smsmith *
79226003Smsmith */
79326003Smsmithstatic void
79426003Smsmithwlinit(void *xsc)
79526003Smsmith{
796113566Sjhay    struct wl_softc	*sc = xsc;
797147256Sbrooks    struct ifnet	*ifp = sc->ifp;
79826003Smsmith    int			stat;
799113601Sjhay    u_long		oldpri;
80026003Smsmith
80126003Smsmith#ifdef WLDEBUG
802147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
80326003Smsmith	printf("wl%d: entered wlinit()\n",sc->unit);
80426003Smsmith#endif
805113605Sjhay    WL_LOCK(sc);
806113601Sjhay    oldpri = splimp();
807113571Sjhay    if ((stat = wlhwrst(sc)) == TRUE) {
808148887Srwatson	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;   /* same as DSF_RUNNING */
80926003Smsmith	/*
81026003Smsmith	 * OACTIVE is used by upper-level routines
81126003Smsmith	 * and must be set
81226003Smsmith	 */
813148887Srwatson	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;  /* same as tbusy below */
81426003Smsmith
81526003Smsmith	sc->flags |= DSF_RUNNING;
81626003Smsmith	sc->tbusy = 0;
81729677Sgibbs	untimeout(wlwatchdog, sc, sc->watchdog_ch);
81826003Smsmith
81926003Smsmith	wlstart(ifp);
82026003Smsmith    } else {
82126003Smsmith	printf("wl%d init(): trouble resetting board.\n", sc->unit);
82226003Smsmith    }
823113601Sjhay    splx(oldpri);
824113605Sjhay    WL_UNLOCK(sc);
82526003Smsmith}
82626003Smsmith
82726003Smsmith/*
82826003Smsmith * wlhwrst:
82926003Smsmith *
83026003Smsmith *	This routine resets the WaveLAN board that corresponds to the
83126003Smsmith *	board number passed in.
83226003Smsmith *
83326003Smsmith * input	: board number to do a hardware reset
83426003Smsmith * output	: board is reset
83526003Smsmith *
83626003Smsmith */
83726003Smsmithstatic int
838113571Sjhaywlhwrst(struct wl_softc *sc)
83926003Smsmith{
84026003Smsmith
84126003Smsmith#ifdef WLDEBUG
842147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
843113571Sjhay	printf("wl%d: entered wlhwrst()\n", sc->unit);
84426003Smsmith#endif
84526003Smsmith    sc->hacr = HACR_RESET;
846113571Sjhay    CMD(sc);			/* reset the board */
84726003Smsmith
84826003Smsmith    /* clear reset command and set PIO#1 in autoincrement mode */
84926003Smsmith    sc->hacr = HACR_DEFAULT;
850113571Sjhay    CMD(sc);
85126003Smsmith
85226003Smsmith#ifdef	WLDEBUG
853147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
854113571Sjhay	wlmmcstat(sc);	/* Display MMC registers */
85541616Seivind#endif	/* WLDEBUG */
856113571Sjhay    wlbldcu(sc);		/* set up command unit structures */
85726003Smsmith
858113571Sjhay    if (wldiag(sc) == 0)
85926003Smsmith	return(0);
86026003Smsmith
861113571Sjhay    if (wlconfig(sc) == 0)
86226003Smsmith	    return(0);
86326003Smsmith    /*
86426003Smsmith     * insert code for loopback test here
86526003Smsmith     */
866113571Sjhay    wlrustrt(sc);		/* start receive unit */
86726003Smsmith
86826003Smsmith    /* enable interrupts */
86926003Smsmith    sc->hacr = (HACR_DEFAULT | HACR_INTRON);
870113571Sjhay    CMD(sc);
87126003Smsmith
87226003Smsmith    return(1);
87326003Smsmith}
87426003Smsmith
87526003Smsmith/*
87626003Smsmith * wlbldcu:
87726003Smsmith *
87826003Smsmith *	This function builds up the command unit structures.  It inits
87926003Smsmith *	the scp, iscp, scb, cb, tbd, and tbuf.
88026003Smsmith *
88126003Smsmith */
88226003Smsmithstatic void
883113571Sjhaywlbldcu(struct wl_softc *sc)
88426003Smsmith{
88526003Smsmith    short		base = sc->base;
88626003Smsmith    scp_t		scp;
88726003Smsmith    iscp_t		iscp;
88826003Smsmith    scb_t		scb;
88926003Smsmith    ac_t		cb;
89026003Smsmith    tbd_t		tbd;
891113571Sjhay    int		i;
89226003Smsmith
89326003Smsmith    bzero(&scp, sizeof(scp));
89426003Smsmith    scp.scp_sysbus = 0;
89526003Smsmith    scp.scp_iscp = OFFSET_ISCP;
89626003Smsmith    scp.scp_iscp_base = 0;
89726003Smsmith    outw(PIOR1(base), OFFSET_SCP);
89826003Smsmith    outsw(PIOP1(base), &scp, sizeof(scp_t)/2);
89926003Smsmith
90026003Smsmith    bzero(&iscp, sizeof(iscp));
90126003Smsmith    iscp.iscp_busy = 1;
90226003Smsmith    iscp.iscp_scb_offset = OFFSET_SCB;
90326003Smsmith    iscp.iscp_scb = 0;
90426003Smsmith    iscp.iscp_scb_base = 0;
90526003Smsmith    outw(PIOR1(base), OFFSET_ISCP);
90626003Smsmith    outsw(PIOP1(base), &iscp, sizeof(iscp_t)/2);
90726003Smsmith
90826003Smsmith    scb.scb_status = 0;
90926003Smsmith    scb.scb_command = SCB_RESET;
91026003Smsmith    scb.scb_cbl_offset = OFFSET_CU;
91126003Smsmith    scb.scb_rfa_offset = OFFSET_RU;
91226003Smsmith    scb.scb_crcerrs = 0;
91326003Smsmith    scb.scb_alnerrs = 0;
91426003Smsmith    scb.scb_rscerrs = 0;
91526003Smsmith    scb.scb_ovrnerrs = 0;
91626003Smsmith    outw(PIOR1(base), OFFSET_SCB);
91726003Smsmith    outsw(PIOP1(base), &scb, sizeof(scb_t)/2);
91826003Smsmith
919113571Sjhay    SET_CHAN_ATTN(sc);
92026003Smsmith
92126003Smsmith    outw(PIOR0(base), OFFSET_ISCP + 0);	/* address of iscp_busy */
92279082Simp    for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); )
92379082Simp	continue;
92479082Simp    if (i <= 0)
925113571Sjhay	printf("wl%d bldcu(): iscp_busy timeout.\n", sc->unit);
92626003Smsmith    outw(PIOR0(base), OFFSET_SCB + 0);	/* address of scb_status */
92726003Smsmith    for (i = STATUS_TRIES; i-- > 0; ) {
92826003Smsmith	if (inw(PIOP0(base)) == (SCB_SW_CX|SCB_SW_CNA))
92926003Smsmith	    break;
93026003Smsmith    }
93126003Smsmith    if (i <= 0)
932113571Sjhay	printf("wl%d bldcu(): not ready after reset.\n", sc->unit);
933113571Sjhay    wlack(sc);
93426003Smsmith
93526003Smsmith    cb.ac_status = 0;
93626003Smsmith    cb.ac_command = AC_CW_EL;		/* NOP */
93726003Smsmith    cb.ac_link_offset = OFFSET_CU;
93826003Smsmith    outw(PIOR1(base), OFFSET_CU);
93926003Smsmith    outsw(PIOP1(base), &cb, 6/2);
94026003Smsmith
94126003Smsmith    tbd.act_count = 0;
94226003Smsmith    tbd.next_tbd_offset = I82586NULL;
94326003Smsmith    tbd.buffer_addr = 0;
94426003Smsmith    tbd.buffer_base = 0;
94526003Smsmith    outw(PIOR1(base), OFFSET_TBD);
94626003Smsmith    outsw(PIOP1(base), &tbd, sizeof(tbd_t)/2);
94726003Smsmith}
94826003Smsmith
94926003Smsmith/*
95026003Smsmith * wlstart:
95126003Smsmith *
95226003Smsmith *	send a packet
95326003Smsmith *
95426003Smsmith * input	: board number
95526003Smsmith * output	: stuff sent to board if any there
95626003Smsmith *
95726003Smsmith */
95826003Smsmithstatic void
95926003Smsmithwlstart(struct ifnet *ifp)
96026003Smsmith{
961113566Sjhay    struct mbuf		*m;
962113571Sjhay    struct wl_softc	*sc = ifp->if_softc;
963113566Sjhay    short		base = sc->base;
964113566Sjhay    int			scb_status, cu_status, scb_command;
96526003Smsmith
966113572Sjhay    WL_LOCK(sc);
96726003Smsmith#ifdef WLDEBUG
968147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
969121816Sbrooks	printf("%s: entered wlstart()\n", ifp->if_xname);
97026003Smsmith#endif
97126003Smsmith
97226003Smsmith    outw(PIOR1(base), OFFSET_CU);
97326003Smsmith    cu_status = inw(PIOP1(base));
97426003Smsmith    outw(PIOR0(base),OFFSET_SCB + 0);	/* scb_status */
97526003Smsmith    scb_status = inw(PIOP0(base));
97626003Smsmith    outw(PIOR0(base), OFFSET_SCB + 2);
97726003Smsmith    scb_command = inw(PIOP0(base));
97826003Smsmith
97926003Smsmith    /*
98026003Smsmith     * don't need OACTIVE check as tbusy here checks to see
98126003Smsmith     * if we are already busy
98226003Smsmith     */
98326003Smsmith    if (sc->tbusy) {
98479082Simp	if ((scb_status & 0x0700) == SCB_CUS_IDLE &&
98526003Smsmith	   (cu_status & AC_SW_B) == 0){
98626003Smsmith	    sc->tbusy = 0;
98729677Sgibbs	    untimeout(wlwatchdog, sc, sc->watchdog_ch);
988148887Srwatson	    sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
98926003Smsmith	    /*
99026003Smsmith	     * This is probably just a race.  The xmt'r is just
99126003Smsmith	     * became idle but WE have masked interrupts so ...
99226003Smsmith	     */
99326003Smsmith#ifdef WLDEBUG
994121816Sbrooks	    printf("%s: CU idle, scb %04x %04x cu %04x\n",
995121816Sbrooks		   ifp->if_xname, scb_status, scb_command, cu_status);
99626003Smsmith#endif
99726003Smsmith	    if (xmt_watch) printf("!!");
99826003Smsmith	} else {
999113572Sjhay	    WL_UNLOCK(sc);
100026003Smsmith	    return;	/* genuinely still busy */
100126003Smsmith	}
100279082Simp    } else if ((scb_status & 0x0700) == SCB_CUS_ACTV ||
100379082Simp      (cu_status & AC_SW_B)){
100426003Smsmith#ifdef WLDEBUG
1005121816Sbrooks	printf("%s: CU unexpectedly busy; scb %04x cu %04x\n",
1006121816Sbrooks	       ifp->if_xname, scb_status, cu_status);
100726003Smsmith#endif
1008121816Sbrooks	if (xmt_watch) printf("%s: busy?!",ifp->if_xname);
1009113572Sjhay	WL_UNLOCK(sc);
101026003Smsmith	return;		/* hey, why are we busy? */
101126003Smsmith    }
101226003Smsmith
101326003Smsmith    /* get ourselves some data */
1014147256Sbrooks    ifp = sc->ifp;
101526003Smsmith    IF_DEQUEUE(&ifp->if_snd, m);
101626003Smsmith    if (m != (struct mbuf *)0) {
101726003Smsmith	/* let BPF see it before we commit it */
1018106937Ssam	BPF_MTAP(ifp, m);
101926003Smsmith	sc->tbusy++;
102026003Smsmith	/* set the watchdog timer so that if the board
102126003Smsmith	 * fails to interrupt we will restart
102226003Smsmith	 */
102326003Smsmith	/* try 10 ticks, not very long */
102429677Sgibbs	sc->watchdog_ch = timeout(wlwatchdog, sc, 10);
1025148887Srwatson	sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1026147256Sbrooks	sc->ifp->if_opackets++;
1027113571Sjhay	wlxmt(sc, m);
102826003Smsmith    } else {
1029148887Srwatson	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
103026003Smsmith    }
1031113572Sjhay    WL_UNLOCK(sc);
103226003Smsmith    return;
103326003Smsmith}
103426003Smsmith
103526003Smsmith/*
103626003Smsmith * wlread:
103726003Smsmith *
103826003Smsmith *	This routine does the actual copy of data (including ethernet header
103926003Smsmith *	structure) from the WaveLAN to an mbuf chain that will be passed up
104026003Smsmith *	to the "if" (network interface) layer.  NOTE:  we currently
104126003Smsmith *	don't handle trailer protocols, so if that is needed, it will
104226003Smsmith *	(at least in part) be added here.  For simplicities sake, this
104326003Smsmith *	routine copies the receive buffers from the board into a local (stack)
104426003Smsmith *	buffer until the frame has been copied from the board.  Once in
104526003Smsmith *	the local buffer, the contents are copied to an mbuf chain that
104626003Smsmith *	is then enqueued onto the appropriate "if" queue.
104726003Smsmith *
1048108470Sschweikh * input	: board number, and a frame descriptor address
104926003Smsmith * output	: the packet is put into an mbuf chain, and passed up
105026003Smsmith * assumes	: if any errors occur, packet is "dropped on the floor"
105126003Smsmith *
105226003Smsmith */
105326003Smsmithstatic int
1054113571Sjhaywlread(struct wl_softc *sc, u_short fd_p)
105526003Smsmith{
1056147256Sbrooks    struct ifnet	*ifp = sc->ifp;
1057113566Sjhay    short		base = sc->base;
1058113566Sjhay    fd_t		fd;
1059113566Sjhay    struct ether_header	*eh;
1060113566Sjhay    struct mbuf		*m;
1061113566Sjhay    rbd_t		rbd;
1062113566Sjhay    u_char		*mb_p;
1063113566Sjhay    u_short		mlen, len;
1064113566Sjhay    u_short		bytes_in_msg, bytes_in_mbuf, bytes;
106526003Smsmith
1066122689Ssam    WL_LOCK_ASSERT(sc);
106726003Smsmith
106826003Smsmith#ifdef WLDEBUG
1069147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1070113571Sjhay	printf("wl%d: entered wlread()\n", sc->unit);
107126003Smsmith#endif
1072148887Srwatson    if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
1073121816Sbrooks	printf("%s read(): board is not running.\n", ifp->if_xname);
107426003Smsmith	sc->hacr &= ~HACR_INTRON;
1075113571Sjhay	CMD(sc);		/* turn off interrupts */
107626003Smsmith    }
1077106937Ssam
1078106937Ssam    /*
1079106937Ssam     * Collect message size.
108026003Smsmith     */
108126003Smsmith    outw(PIOR1(base), fd_p);
1082106937Ssam    insw(PIOP1(base), &fd, sizeof(fd_t)/2);
108326003Smsmith    if (fd.rbd_offset == I82586NULL) {
1084113571Sjhay	if (wlhwrst(sc) != TRUE) {
1085106937Ssam	    sc->hacr &= ~HACR_INTRON;
1086113571Sjhay	    CMD(sc);		/* turn off interrupts */
1087113571Sjhay	    printf("wl%d read(): hwrst trouble.\n", sc->unit);
108826003Smsmith	}
108926003Smsmith	return 0;
109026003Smsmith    }
109126003Smsmith
109226003Smsmith    outw(PIOR1(base), fd.rbd_offset);
109326003Smsmith    insw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
109426003Smsmith    bytes_in_msg = rbd.status & RBD_SW_COUNT;
1095106937Ssam
1096106937Ssam    /*
1097106937Ssam     * Allocate a cluster'd mbuf to receive the packet.
1098106937Ssam     */
1099243857Sglebius    m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1100106937Ssam    if (m == NULL) {
1101113571Sjhay	if (wlhwrst(sc) != TRUE) {
110226003Smsmith	    sc->hacr &= ~HACR_INTRON;
1103113571Sjhay	    CMD(sc);		/* turn off interrupts */
1104113571Sjhay	    printf("wl%d read(): hwrst trouble.\n", sc->unit);
110526003Smsmith	}
110626003Smsmith	return 0;
110726003Smsmith    }
1108106937Ssam    m->m_pkthdr.len = m->m_len = MCLBYTES;
1109106937Ssam    m_adj(m, ETHER_ALIGN);		/* align IP header */
111027817Smsmith
1111106937Ssam    /*
1112106937Ssam     * Collect the message data.
111327817Smsmith     */
111426003Smsmith    mlen = 0;
1115106937Ssam    mb_p = mtod(m, u_char *);
1116113606Sjhay    bytes_in_mbuf = m->m_len;
1117113606Sjhay
1118113606Sjhay    /* Put the ethernet header inside the mbuf. */
1119113606Sjhay    bcopy(&fd.destination[0], mb_p, 14);
1120113606Sjhay    mb_p += 14;
1121113606Sjhay    mlen += 14;
1122113606Sjhay    bytes_in_mbuf -= 14;
1123113606Sjhay
112426003Smsmith    bytes = min(bytes_in_mbuf, bytes_in_msg);
112526003Smsmith    for (;;) {
112626003Smsmith	if (bytes & 1) {
112726003Smsmith	    len = bytes + 1;
112826003Smsmith	} else {
112926003Smsmith	    len = bytes;
113026003Smsmith	}
113126003Smsmith	outw(PIOR1(base), rbd.buffer_addr);
113226003Smsmith	insw(PIOP1(base), mb_p, len/2);
113326003Smsmith	mlen += bytes;
113426003Smsmith
1135106937Ssam	if (bytes > bytes_in_mbuf) {
1136106937Ssam	    /* XXX something wrong, a packet should fit in 1 cluster */
1137106937Ssam	    m_freem(m);
1138106937Ssam	    printf("wl%d read(): packet too large (%u > %u)\n",
1139113571Sjhay		   sc->unit, bytes, bytes_in_mbuf);
1140113571Sjhay	    if (wlhwrst(sc) != TRUE) {
1141106937Ssam		sc->hacr &= ~HACR_INTRON;
1142113571Sjhay		CMD(sc);  /* turn off interrupts */
1143113571Sjhay		printf("wl%d read(): hwrst trouble.\n", sc->unit);
114426003Smsmith	    }
1145106937Ssam	    return 0;
114626003Smsmith	}
1147106937Ssam	mb_p += bytes;
1148113606Sjhay	bytes_in_mbuf -= bytes;
1149106937Ssam	bytes_in_msg -= bytes;
1150106937Ssam	if (bytes_in_msg == 0) {
1151106937Ssam	    if (rbd.status & RBD_SW_EOF || rbd.next_rbd_offset == I82586NULL) {
115226003Smsmith		break;
115326003Smsmith	    }
1154106937Ssam	    outw(PIOR1(base), rbd.next_rbd_offset);
1155106937Ssam	    insw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
1156106937Ssam	    bytes_in_msg = rbd.status & RBD_SW_COUNT;
115726003Smsmith	} else {
115826003Smsmith	    rbd.buffer_addr += bytes;
115926003Smsmith	}
116026003Smsmith
116126003Smsmith	bytes = min(bytes_in_mbuf, bytes_in_msg);
116226003Smsmith    }
116326003Smsmith
1164106937Ssam    m->m_pkthdr.len = m->m_len = mlen;
1165106937Ssam    m->m_pkthdr.rcvif = ifp;
116626003Smsmith
116726003Smsmith    /*
116838447Smsmith     * If hw is in promiscuous mode (note that I said hardware, not if
116938447Smsmith     * IFF_PROMISC is set in ifnet flags), then if this is a unicast
117060536Sarchie     * packet and the MAC dst is not us, drop it.  This check in normally
117160536Sarchie     * inside ether_input(), but IFF_MULTI causes hw promisc without
117238447Smsmith     * a bpf listener, so this is wrong.
117338447Smsmith     *		Greg Troxel <gdt@ir.bbn.com>, 1998-08-07
117438447Smsmith     */
117538447Smsmith    /*
117638447Smsmith     * TBD: also discard packets where NWID does not match.
117738447Smsmith     * However, there does not appear to be a way to read the nwid
117838447Smsmith     * for a received packet.  -gdt 1998-08-07
117938447Smsmith     */
1180106937Ssam    /* XXX verify mbuf length */
1181106937Ssam    eh = mtod(m, struct ether_header *);
118238447Smsmith    if (
118338447Smsmith#ifdef WL_USE_IFNET_PROMISC_CHECK /* not defined */
1184147256Sbrooks	(sc->ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI))
118538447Smsmith#else
118638447Smsmith	/* hw is in promisc mode if this is true */
118738447Smsmith	(sc->mode & (MOD_PROM | MOD_ENAL))
118838447Smsmith#endif
118938447Smsmith	&&
1190106937Ssam	(eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
1191152315Sru	bcmp(eh->ether_dhost, IF_LLADDR(sc->ifp),
1192106937Ssam	     sizeof(eh->ether_dhost)) != 0 ) {
119338447Smsmith      m_freem(m);
119438447Smsmith      return 1;
119538447Smsmith    }
119626003Smsmith
119726003Smsmith#ifdef WLDEBUG
1198147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1199113571Sjhay	printf("wl%d: wlrecv %u bytes\n", sc->unit, mlen);
120026003Smsmith#endif
120126003Smsmith
120227817Smsmith#ifdef WLCACHE
1203113571Sjhay    wl_cache_store(sc, base, eh, m);
120427817Smsmith#endif
120527817Smsmith
120626003Smsmith    /*
120726003Smsmith     * received packet is now in a chain of mbuf's.  next step is
120826003Smsmith     * to pass the packet upwards.
120926003Smsmith     */
1210122689Ssam    WL_UNLOCK(sc);
1211106937Ssam    (*ifp->if_input)(ifp, m);
1212122689Ssam    WL_LOCK(sc);
121326003Smsmith    return 1;
121426003Smsmith}
121526003Smsmith
121626003Smsmith/*
121726003Smsmith * wlioctl:
121826003Smsmith *
121926003Smsmith *	This routine processes an ioctl request from the "if" layer
122026003Smsmith *	above.
122126003Smsmith *
122226003Smsmith * input	: pointer the appropriate "if" struct, command, and data
122326003Smsmith * output	: based on command appropriate action is taken on the
122426003Smsmith *	 	  WaveLAN board(s) or related structures
122526003Smsmith * return	: error is returned containing exit conditions
122626003Smsmith *
122726003Smsmith */
122826003Smsmithstatic int
122936735Sdfrwlioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
123026003Smsmith{
1231113566Sjhay    struct ifreq	*ifr = (struct ifreq *)data;
1232113571Sjhay    struct wl_softc	*sc = ifp->if_softc;
123326003Smsmith    short		base = sc->base;
123426003Smsmith    short		mode = 0;
1235113601Sjhay    int			opri, error = 0;
123683366Sjulian    struct thread	*td = curthread;	/* XXX */
123779081Simp    int			irq, irqval, i, isroot;
123826003Smsmith    caddr_t		up;
123979081Simp#ifdef WLCACHE
124079081Simp    int			size;
124127817Smsmith    char * 	        cpt;
124279081Simp#endif
124326003Smsmith
1244113572Sjhay    WL_LOCK(sc);
124526003Smsmith#ifdef WLDEBUG
1246147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1247121816Sbrooks	printf("%s: entered wlioctl()\n", ifp->if_xname);
124826003Smsmith#endif
1249113601Sjhay    opri = splimp();
125026003Smsmith    switch (cmd) {
125126003Smsmith    case SIOCSIFFLAGS:
125227817Smsmith	if (ifp->if_flags & IFF_ALLMULTI) {
125326003Smsmith	    mode |= MOD_ENAL;
125427817Smsmith	}
125527817Smsmith	if (ifp->if_flags & IFF_PROMISC) {
125626003Smsmith	    mode |= MOD_PROM;
125727817Smsmith	}
125879082Simp	if (ifp->if_flags & IFF_LINK0) {
125926003Smsmith	    mode |= MOD_PROM;
126027817Smsmith	}
126126003Smsmith	/*
126226003Smsmith	 * force a complete reset if the recieve multicast/
126326003Smsmith	 * promiscuous mode changes so that these take
126426003Smsmith	 * effect immediately.
126527817Smsmith	 *
126627817Smsmith	 */
126726003Smsmith	if (sc->mode != mode) {
126826003Smsmith	    sc->mode = mode;
126926003Smsmith	    if (sc->flags & DSF_RUNNING) {
127026003Smsmith		sc->flags &= ~DSF_RUNNING;
127126003Smsmith		wlinit(sc);
127226003Smsmith	    }
127326003Smsmith	}
127426003Smsmith	/* if interface is marked DOWN and still running then
127527817Smsmith	 * stop it.
127627817Smsmith	 */
127726003Smsmith	if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) {
1278121816Sbrooks	    printf("%s ioctl(): board is not running\n", ifp->if_xname);
127926003Smsmith	    sc->flags &= ~DSF_RUNNING;
128026003Smsmith	    sc->hacr &= ~HACR_INTRON;
1281113571Sjhay	    CMD(sc);		  /* turn off interrupts */
128226003Smsmith	}
128326003Smsmith	/* else if interface is UP and RUNNING, start it
128426003Smsmith		*/
128526003Smsmith	else if (ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0) {
128626003Smsmith	    wlinit(sc);
128726003Smsmith	}
128826003Smsmith
128926003Smsmith	/* if WLDEBUG set on interface, then printf rf-modem regs
129027817Smsmith	*/
129179082Simp	if (ifp->if_flags & IFF_DEBUG)
1292113571Sjhay	    wlmmcstat(sc);
129326003Smsmith	break;
129426003Smsmith#if	MULTICAST
129526003Smsmith    case SIOCADDMULTI:
129626003Smsmith    case SIOCDELMULTI:
129727817Smsmith
129851309Sroberto	wlinit(sc);
129926003Smsmith	break;
130041616Seivind#endif	/* MULTICAST */
130126003Smsmith
130227817Smsmith    /* DEVICE SPECIFIC */
130327817Smsmith
130427817Smsmith
130526003Smsmith	/* copy the PSA out to the caller */
130626003Smsmith    case SIOCGWLPSA:
130726003Smsmith	/* pointer to buffer in user space */
130826003Smsmith	up = (void *)ifr->ifr_data;
130926003Smsmith	/* work out if they're root */
1310164033Srwatson	isroot = (priv_check(td, PRIV_NET80211_GETKEY) == 0);
131126003Smsmith
131226003Smsmith	for (i = 0; i < 0x40; i++) {
131326003Smsmith	    /* don't hand the DES key out to non-root users */
131426003Smsmith	    if ((i > WLPSA_DESKEY) && (i < (WLPSA_DESKEY + 8)) && !isroot)
131526003Smsmith		continue;
1316113572Sjhay	    if (subyte((up + i), sc->psa[i])) {
1317113572Sjhay		WL_UNLOCK(sc);
131826003Smsmith		return(EFAULT);
1319113572Sjhay	    }
132026003Smsmith	}
132126003Smsmith	break;
132226003Smsmith
132327817Smsmith
132426003Smsmith	/* copy the PSA in from the caller; we only copy _some_ values */
132526003Smsmith    case SIOCSWLPSA:
132626003Smsmith	/* root only */
1327164033Srwatson	if ((error = priv_check(td, PRIV_DRIVER)))
132826003Smsmith	    break;
132926003Smsmith	error = EINVAL;	/* assume the worst */
133026003Smsmith	/* pointer to buffer in user space containing data */
133126003Smsmith	up = (void *)ifr->ifr_data;
133226003Smsmith
133326003Smsmith	/* check validity of input range */
133426003Smsmith	for (i = 0; i < 0x40; i++)
1335113572Sjhay	    if (fubyte(up + i) < 0) {
1336113572Sjhay		WL_UNLOCK(sc);
133726003Smsmith		return(EFAULT);
1338113572Sjhay	    }
133926003Smsmith
134026003Smsmith	/* check IRQ value */
134126003Smsmith	irqval = fubyte(up+WLPSA_IRQNO);
134226003Smsmith	for (irq = 15; irq >= 0; irq--)
134379082Simp	    if (irqvals[irq] == irqval)
134426003Smsmith		break;
134526003Smsmith	if (irq == 0)			/* oops */
134626003Smsmith	    break;
134726003Smsmith	/* new IRQ */
134826003Smsmith	sc->psa[WLPSA_IRQNO] = irqval;
134926003Smsmith
135026003Smsmith	/* local MAC */
135126003Smsmith	for (i = 0; i < 6; i++)
135226003Smsmith	    sc->psa[WLPSA_LOCALMAC+i] = fubyte(up+WLPSA_LOCALMAC+i);
135326003Smsmith
135426003Smsmith	/* MAC select */
135526003Smsmith	sc->psa[WLPSA_MACSEL] = fubyte(up+WLPSA_MACSEL);
135626003Smsmith
135726003Smsmith	/* default nwid */
135826003Smsmith	sc->psa[WLPSA_NWID] = fubyte(up+WLPSA_NWID);
135926003Smsmith	sc->psa[WLPSA_NWID+1] = fubyte(up+WLPSA_NWID+1);
136026003Smsmith
136126003Smsmith	error = 0;
1362113571Sjhay	wlsetpsa(sc);		/* update the PSA */
136326003Smsmith	break;
136426003Smsmith
136526003Smsmith
136626003Smsmith	/* get the current NWID out of the sc since we stored it there */
136726003Smsmith    case SIOCGWLCNWID:
136826003Smsmith	ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]);
136926003Smsmith	break;
137026003Smsmith
137126003Smsmith
137226003Smsmith	/*
137326003Smsmith	 * change the nwid dynamically.  This
137426003Smsmith	 * ONLY changes the radio modem and does not
137526003Smsmith	 * change the PSA.
137626003Smsmith	 *
137726003Smsmith	 * 2 steps:
137826003Smsmith	 *	1. save in softc "soft registers"
137926003Smsmith	 *	2. save in radio modem (MMC)
138026003Smsmith	 */
138126003Smsmith    case SIOCSWLCNWID:
138226003Smsmith	/* root only */
1383164033Srwatson	if ((error = priv_check(td, PRIV_DRIVER)))
138426003Smsmith	    break;
138526003Smsmith	if (!(ifp->if_flags & IFF_UP)) {
138626003Smsmith	    error = EIO;	/* only allowed while up */
138726003Smsmith	} else {
138826003Smsmith	    /*
138926003Smsmith	     * soft c nwid shadows radio modem setting
139026003Smsmith	     */
139126003Smsmith	    sc->nwid[0] = (int)ifr->ifr_data >> 8;
139226003Smsmith	    sc->nwid[1] = (int)ifr->ifr_data & 0xff;
139326003Smsmith	    MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]);
139426003Smsmith	    MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]);
139526003Smsmith	}
139626003Smsmith	break;
139726003Smsmith
139827817Smsmith	/* copy the EEPROM in 2.4 Gz WaveMODEM  out to the caller */
139927817Smsmith    case SIOCGWLEEPROM:
140027817Smsmith	/* root only */
1401164033Srwatson	if ((error = priv_check(td, PRIV_DRIVER)))
140227817Smsmith	    break;
140327817Smsmith	/* pointer to buffer in user space */
140427817Smsmith	up = (void *)ifr->ifr_data;
140527817Smsmith
140627817Smsmith	for (i=0x00; i<0x80; ++i) {		/* 2.4 Gz: size of EEPROM   */
140727817Smsmith	    MMC_WRITE(MMC_EEADDR,i);		/* 2.4 Gz: get frequency    */
140827817Smsmith	    MMC_WRITE(MMC_EECTRL,		/* 2.4 Gz: EEPROM read	    */
140927817Smsmith			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz:		    */
141027817Smsmith	    DELAY(40);				/* 2.4 Gz		    */
1411113572Sjhay	    if (subyte(up + 2*i,		/* 2.4 Gz: pass low byte of */
1412113572Sjhay		wlmmcread(base,MMC_EEDATALrv))) {/* 2.4 Gz: EEPROM word      */
1413113572Sjhay		WL_UNLOCK(sc);
1414113572Sjhay	        return(EFAULT);			/* 2.4 Gz:		    */
1415113572Sjhay	    }
141627817Smsmith	    if (subyte(up + 2*i+1,		/* 2.4 Gz: pass hi byte of  */
1417113572Sjhay		wlmmcread(base,MMC_EEDATALrv)))	{/* 2.4 Gz: EEPROM word      */
1418113572Sjhay		WL_UNLOCK(sc);
1419113572Sjhay	        return(EFAULT);			/* 2.4 Gz:		    */
1420113572Sjhay	    }
142127817Smsmith	}
142227817Smsmith	break;
142327817Smsmith
142427817Smsmith#ifdef WLCACHE
142527817Smsmith	/* zero (Delete) the wl cache */
142627817Smsmith    case SIOCDWLCACHE:
142727817Smsmith	/* root only */
1428164033Srwatson	if ((error = priv_check(td, PRIV_DRIVER)))
142927817Smsmith	    break;
1430113571Sjhay	wl_cache_zero(sc);
143127817Smsmith	break;
143227817Smsmith
143327817Smsmith	/* read out the number of used cache elements */
143427817Smsmith    case SIOCGWLCITEM:
143527817Smsmith	ifr->ifr_data = (caddr_t) sc->w_sigitems;
143627817Smsmith	break;
143727817Smsmith
143827817Smsmith	/* read out the wl cache */
143927817Smsmith    case SIOCGWLCACHE:
144027817Smsmith	/* pointer to buffer in user space */
144127817Smsmith	up = (void *)ifr->ifr_data;
144227817Smsmith	cpt = (char *) &sc->w_sigcache[0];
144327817Smsmith	size = sc->w_sigitems * sizeof(struct w_sigcache);
144427817Smsmith
144527817Smsmith	for (i = 0; i < size; i++) {
1446113572Sjhay	    if (subyte((up + i), *cpt++)) {
1447113572Sjhay		WL_UNLOCK(sc);
144827817Smsmith		return(EFAULT);
1449113572Sjhay	    }
145027817Smsmith	}
145127817Smsmith	break;
145227817Smsmith#endif
145327817Smsmith
145426003Smsmith    default:
1455106937Ssam        error = ether_ioctl(ifp, cmd, data);
1456106937Ssam	break;
145726003Smsmith    }
1458113601Sjhay    splx(opri);
1459113572Sjhay    WL_UNLOCK(sc);
146026003Smsmith    return (error);
146126003Smsmith}
146226003Smsmith
146326003Smsmith/*
146426003Smsmith * wlwatchdog():
146526003Smsmith *
146626003Smsmith * Called if the timer set in wlstart expires before an interrupt is received
146726003Smsmith * from the wavelan.   It seems to lose interrupts sometimes.
146826003Smsmith * The watchdog routine gets called if the transmitter failed to interrupt
146926003Smsmith *
147026003Smsmith * input	: which board is timing out
147126003Smsmith * output	: board reset
147226003Smsmith *
147326003Smsmith */
147426003Smsmithstatic void
147527839Smsmithwlwatchdog(void *vsc)
147626003Smsmith{
147727839Smsmith    struct wl_softc *sc = vsc;
147826003Smsmith    int unit = sc->unit;
147926003Smsmith
148026003Smsmith    log(LOG_ERR, "wl%d: wavelan device timeout on xmit\n", unit);
1481113572Sjhay    WL_LOCK(sc);
1482147256Sbrooks    sc->ifp->if_oerrors++;
148326003Smsmith    wlinit(sc);
1484113572Sjhay    WL_UNLOCK(sc);
148526003Smsmith}
148626003Smsmith
148726003Smsmith/*
148826003Smsmith * wlintr:
148926003Smsmith *
149026003Smsmith *	This function is the interrupt handler for the WaveLAN
149126003Smsmith *	board.  This routine will be called whenever either a packet
149226003Smsmith *	is received, or a packet has successfully been transfered and
149326003Smsmith *	the unit is ready to transmit another packet.
149426003Smsmith *
149526003Smsmith * input	: board number that interrupted
149626003Smsmith * output	: either a packet is received, or a packet is transfered
149726003Smsmith *
149826003Smsmith */
149940565Sbdestatic void
1500113571Sjhaywlintr(void *arg)
150126003Smsmith{
1502113571Sjhay    struct wl_softc	*sc = (struct wl_softc *)arg;
150326003Smsmith    short		base = sc->base;
150441591Sarchie    int			ac_status;
150526003Smsmith    u_short		int_type, int_type1;
150626003Smsmith
1507113572Sjhay    WL_LOCK(sc);
150826003Smsmith#ifdef WLDEBUG
1509147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1510113571Sjhay	printf("wl%d: wlintr() called\n", sc->unit);
151126003Smsmith#endif
151226003Smsmith
151379082Simp    if ((int_type = inw(HASR(base))) & HASR_MMC_INTR) {
151426003Smsmith	/* handle interrupt from the modem management controler */
151526003Smsmith	/* This will clear the interrupt condition */
151626003Smsmith	(void) wlmmcread(base,MMC_DCE_STATUS); /* ignored for now */
151726003Smsmith    }
151826003Smsmith
151979082Simp    if (!(int_type & HASR_INTR)){	/* return if no interrupt from 82586 */
152026003Smsmith	/* commented out. jrb.  it happens when reinit occurs
152126003Smsmith	   printf("wlintr: int_type %x, dump follows\n", int_type);
152226003Smsmith	   wldump(unit);
152326003Smsmith	   */
1524113572Sjhay	WL_UNLOCK(sc);
152526003Smsmith	return;
152626003Smsmith    }
152726003Smsmith
152826003Smsmith    if (gathersnr)
1529113571Sjhay	getsnr(sc);
153079082Simp    for (;;) {
153126003Smsmith	outw(PIOR0(base), OFFSET_SCB + 0);	/* get scb status */
153226003Smsmith	int_type = (inw(PIOP0(base)) & SCB_SW_INT);
153326003Smsmith	if (int_type == 0)			/* no interrupts left */
153426003Smsmith	    break;
153526003Smsmith
1536113571Sjhay	int_type1 = wlack(sc);		/* acknowledge interrupt(s) */
153726003Smsmith	/* make sure no bits disappeared (others may appear) */
153826003Smsmith	if ((int_type & int_type1) != int_type)
153926003Smsmith	    printf("wlack() int bits disappeared : %04x != int_type %04x\n",
154026003Smsmith		   int_type1, int_type);
154126003Smsmith	int_type = int_type1;			/* go with the new status */
154226003Smsmith	/*
154326003Smsmith	 * incoming packet
154426003Smsmith	 */
154526003Smsmith	if (int_type & SCB_SW_FR) {
1546147256Sbrooks	    sc->ifp->if_ipackets++;
1547113571Sjhay	    wlrcv(sc);
154826003Smsmith	}
154926003Smsmith	/*
155026003Smsmith	 * receiver not ready
155126003Smsmith	 */
155226003Smsmith	if (int_type & SCB_SW_RNR) {
1553147256Sbrooks	    sc->ifp->if_ierrors++;
155426003Smsmith#ifdef	WLDEBUG
1555147256Sbrooks	    if (sc->ifp->if_flags & IFF_DEBUG)
155626003Smsmith		printf("wl%d intr(): receiver overrun! begin_fd = %x\n",
1557113571Sjhay		       sc->unit, sc->begin_fd);
155826003Smsmith#endif
1559113571Sjhay	    wlrustrt(sc);
156026003Smsmith	}
156126003Smsmith	/*
156226003Smsmith	 * CU not ready
156326003Smsmith	 */
156426003Smsmith	if (int_type & SCB_SW_CNA) {
156526003Smsmith	    /*
156626003Smsmith	     * At present, we don't care about CNA's.  We
156726003Smsmith	     * believe they are a side effect of XMT.
156826003Smsmith	     */
156926003Smsmith	}
157026003Smsmith	if (int_type & SCB_SW_CX) {
157126003Smsmith	    /*
157226003Smsmith	     * At present, we only request Interrupt for
157326003Smsmith	     * XMT.
157426003Smsmith	     */
157526003Smsmith	    outw(PIOR1(base), OFFSET_CU);	/* get command status */
157626003Smsmith	    ac_status = inw(PIOP1(base));
157726003Smsmith
157826003Smsmith	    if (xmt_watch) {			/* report some anomalies */
157926003Smsmith
158026003Smsmith		if (sc->tbusy == 0) {
158126003Smsmith		    printf("wl%d: xmt intr but not busy, CU %04x\n",
1582113571Sjhay			   sc->unit, ac_status);
158326003Smsmith		}
158426003Smsmith		if (ac_status == 0) {
1585113571Sjhay		    printf("wl%d: xmt intr but ac_status == 0\n", sc->unit);
158626003Smsmith		}
158726003Smsmith		if (ac_status & AC_SW_A) {
1588113571Sjhay		    printf("wl%d: xmt aborted\n", sc->unit);
158926003Smsmith		}
159026003Smsmith#ifdef	notdef
159126003Smsmith		if (ac_status & TC_CARRIER) {
1592113571Sjhay		    printf("wl%d: no carrier\n", sc->unit);
159326003Smsmith		}
159441616Seivind#endif	/* notdef */
159526003Smsmith		if (ac_status & TC_CLS) {
1596113571Sjhay		    printf("wl%d: no CTS\n", sc->unit);
159726003Smsmith		}
159826003Smsmith		if (ac_status & TC_DMA) {
1599113571Sjhay		    printf("wl%d: DMA underrun\n", sc->unit);
160026003Smsmith		}
160126003Smsmith		if (ac_status & TC_DEFER) {
1602113571Sjhay		    printf("wl%d: xmt deferred\n", sc->unit);
160326003Smsmith		}
160426003Smsmith		if (ac_status & TC_SQE) {
1605113571Sjhay		    printf("wl%d: heart beat\n", sc->unit);
160626003Smsmith		}
160726003Smsmith		if (ac_status & TC_COLLISION) {
1608113571Sjhay		    printf("wl%d: too many collisions\n", sc->unit);
160926003Smsmith		}
161026003Smsmith	    }
161126003Smsmith	    /* if the transmit actually failed, or returned some status */
161226003Smsmith	    if ((!(ac_status & AC_SW_OK)) || (ac_status & 0xfff)) {
161326003Smsmith		if (ac_status & (TC_COLLISION | TC_CLS | TC_DMA)) {
1614147256Sbrooks		    sc->ifp->if_oerrors++;
161526003Smsmith		}
161626003Smsmith		/* count collisions */
1617147256Sbrooks		sc->ifp->if_collisions += (ac_status & 0xf);
161826003Smsmith		/* if TC_COLLISION set and collision count zero, 16 collisions */
161926003Smsmith		if ((ac_status & 0x20) == 0x20) {
1620147256Sbrooks		    sc->ifp->if_collisions += 0x10;
162126003Smsmith		}
162226003Smsmith	    }
162326003Smsmith	    sc->tbusy = 0;
162429677Sgibbs	    untimeout(wlwatchdog, sc, sc->watchdog_ch);
1625148887Srwatson	    sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1626147256Sbrooks	    wlstart(sc->ifp);
162726003Smsmith	}
162826003Smsmith    }
1629113572Sjhay    WL_UNLOCK(sc);
163026003Smsmith    return;
163126003Smsmith}
163226003Smsmith
163326003Smsmith/*
163426003Smsmith * wlrcv:
163526003Smsmith *
163626003Smsmith *	This routine is called by the interrupt handler to initiate a
163726003Smsmith *	packet transfer from the board to the "if" layer above this
163826003Smsmith *	driver.  This routine checks if a buffer has been successfully
163926003Smsmith *	received by the WaveLAN.  If so, the routine wlread is called
164026003Smsmith *	to do the actual transfer of the board data (including the
164126003Smsmith *	ethernet header) into a packet (consisting of an mbuf chain).
164226003Smsmith *
164326003Smsmith * input	: number of the board to check
164426003Smsmith * output	: if a packet is available, it is "sent up"
164526003Smsmith *
164626003Smsmith */
164726003Smsmithstatic void
1648113571Sjhaywlrcv(struct wl_softc *sc)
164926003Smsmith{
165026003Smsmith    short	base = sc->base;
165126003Smsmith    u_short	fd_p, status, offset, link_offset;
165226003Smsmith
165326003Smsmith#ifdef WLDEBUG
1654147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1655113571Sjhay	printf("wl%d: entered wlrcv()\n", sc->unit);
165626003Smsmith#endif
165726003Smsmith    for (fd_p = sc->begin_fd; fd_p != I82586NULL; fd_p = sc->begin_fd) {
165826003Smsmith
165926003Smsmith	outw(PIOR0(base), fd_p + 0);	/* address of status */
166026003Smsmith	status = inw(PIOP0(base));
166126003Smsmith	outw(PIOR1(base), fd_p + 4);	/* address of link_offset */
166226003Smsmith	link_offset = inw(PIOP1(base));
166326003Smsmith	offset = inw(PIOP1(base));	/* rbd_offset */
166426003Smsmith	if (status == 0xffff || offset == 0xffff /*I82586NULL*/) {
1665113571Sjhay	    if (wlhwrst(sc) != TRUE)
1666113571Sjhay		printf("wl%d rcv(): hwrst ffff trouble.\n", sc->unit);
166726003Smsmith	    return;
166826003Smsmith	} else if (status & AC_SW_C) {
166926003Smsmith	    if (status == (RFD_DONE|RFD_RSC)) {
167026003Smsmith		/* lost one */
167126003Smsmith#ifdef	WLDEBUG
1672147256Sbrooks		if (sc->ifp->if_flags & IFF_DEBUG)
1673113571Sjhay		    printf("wl%d RCV: RSC %x\n", sc->unit, status);
167426003Smsmith#endif
1675147256Sbrooks		sc->ifp->if_ierrors++;
167626003Smsmith	    } else if (!(status & RFD_OK)) {
1677113571Sjhay		printf("wl%d RCV: !OK %x\n", sc->unit, status);
1678147256Sbrooks		sc->ifp->if_ierrors++;
167926003Smsmith	    } else if (status & 0xfff) {	/* can't happen */
1680113571Sjhay		printf("wl%d RCV: ERRs %x\n", sc->unit, status);
1681147256Sbrooks		sc->ifp->if_ierrors++;
1682113571Sjhay	    } else if (!wlread(sc, fd_p))
168326003Smsmith		return;
168426003Smsmith
1685113571Sjhay	    if (!wlrequeue(sc, fd_p)) {
168626003Smsmith		/* abort on chain error */
1687113571Sjhay		if (wlhwrst(sc) != TRUE)
1688113571Sjhay		    printf("wl%d rcv(): hwrst trouble.\n", sc->unit);
168926003Smsmith		return;
169026003Smsmith	    }
169126003Smsmith	    sc->begin_fd = link_offset;
169226003Smsmith	} else {
169326003Smsmith	    break;
169426003Smsmith	}
169526003Smsmith    }
169626003Smsmith    return;
169726003Smsmith}
169826003Smsmith
169926003Smsmith/*
170026003Smsmith * wlrequeue:
170126003Smsmith *
170226003Smsmith *	This routine puts rbd's used in the last receive back onto the
170326003Smsmith *	free list for the next receive.
170426003Smsmith *
170526003Smsmith */
170626003Smsmithstatic int
1707113571Sjhaywlrequeue(struct wl_softc *sc, u_short fd_p)
170826003Smsmith{
170926003Smsmith    short		base = sc->base;
171026003Smsmith    fd_t		fd;
171126003Smsmith    u_short		l_rbdp, f_rbdp, rbd_offset;
171226003Smsmith
171326003Smsmith    outw(PIOR0(base), fd_p + 6);
171426003Smsmith    rbd_offset = inw(PIOP0(base));
171526003Smsmith    if ((f_rbdp = rbd_offset) != I82586NULL) {
171626003Smsmith	l_rbdp = f_rbdp;
171779082Simp	for (;;) {
171826003Smsmith	    outw(PIOR0(base), l_rbdp + 0);	/* address of status */
171979082Simp	    if (inw(PIOP0(base)) & RBD_SW_EOF)
172026003Smsmith		break;
172126003Smsmith	    outw(PIOP0(base), 0);
172226003Smsmith	    outw(PIOR0(base), l_rbdp + 2);	/* next_rbd_offset */
172379082Simp	    if ((l_rbdp = inw(PIOP0(base))) == I82586NULL)
172426003Smsmith		break;
172526003Smsmith	}
172626003Smsmith	outw(PIOP0(base), 0);
172726003Smsmith	outw(PIOR0(base), l_rbdp + 2);		/* next_rbd_offset */
172826003Smsmith	outw(PIOP0(base), I82586NULL);
172926003Smsmith	outw(PIOR0(base), l_rbdp + 8);		/* address of size */
173026003Smsmith	outw(PIOP0(base), inw(PIOP0(base)) | AC_CW_EL);
173126003Smsmith	outw(PIOR0(base), sc->end_rbd + 2);
173226003Smsmith	outw(PIOP0(base), f_rbdp);		/* end_rbd->next_rbd_offset */
173326003Smsmith	outw(PIOR0(base), sc->end_rbd + 8);	/* size */
173426003Smsmith	outw(PIOP0(base), inw(PIOP0(base)) & ~AC_CW_EL);
173526003Smsmith	sc->end_rbd = l_rbdp;
173626003Smsmith    }
173726003Smsmith
173826003Smsmith    fd.status = 0;
173926003Smsmith    fd.command = AC_CW_EL;
174026003Smsmith    fd.link_offset = I82586NULL;
174126003Smsmith    fd.rbd_offset = I82586NULL;
174226003Smsmith    outw(PIOR1(base), fd_p);
174326003Smsmith    outsw(PIOP1(base), &fd, 8/2);
174426003Smsmith
174526003Smsmith    outw(PIOR1(base), sc->end_fd + 2);	/* addr of command */
174626003Smsmith    outw(PIOP1(base), 0);		/* command = 0 */
174726003Smsmith    outw(PIOP1(base), fd_p);		/* end_fd->link_offset = fd_p */
174826003Smsmith    sc->end_fd = fd_p;
174926003Smsmith
175026003Smsmith    return 1;
175126003Smsmith}
175226003Smsmith
175326003Smsmith#ifdef	WLDEBUG
175426003Smsmithstatic int xmt_debug = 0;
175541616Seivind#endif	/* WLDEBUG */
175626003Smsmith
175726003Smsmith/*
175826003Smsmith * wlxmt:
175926003Smsmith *
176026003Smsmith *	This routine fills in the appropriate registers and memory
176126003Smsmith *	locations on the WaveLAN board and starts the board off on
176226003Smsmith *	the transmit.
176326003Smsmith *
1764121816Sbrooks * input	: pointers to board of interest's softc and the mbuf
176526003Smsmith * output	: board memory and registers are set for xfer and attention
176626003Smsmith *
176726003Smsmith */
176826003Smsmithstatic void
1769113571Sjhaywlxmt(struct wl_softc *sc, struct mbuf *m)
177026003Smsmith{
1771113566Sjhay    u_short		xmtdata_p = OFFSET_TBUF;
1772113566Sjhay    u_short		xmtshort_p;
1773113566Sjhay    struct mbuf		*tm_p = m;
1774113566Sjhay    struct ether_header	*eh_p = mtod(m, struct ether_header *);
1775113566Sjhay    u_char		*mb_p = mtod(m, u_char *) + sizeof(struct ether_header);
1776113566Sjhay    u_short		count = m->m_len - sizeof(struct ether_header);
1777113566Sjhay    ac_t		cb;
1778113566Sjhay    u_short		tbd_p = OFFSET_TBD;
1779113566Sjhay    u_short		len, clen = 0;
1780113566Sjhay    short		base = sc->base;
1781113566Sjhay    int			spin;
178226003Smsmith
178326003Smsmith#ifdef WLDEBUG
1784147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1785147256Sbrooks	printf("%s: entered wlxmt()\n", sc->ifp->if_xname);
178626003Smsmith#endif
178726003Smsmith
178826003Smsmith    cb.ac_status = 0;
178926003Smsmith    cb.ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I);
179026003Smsmith    cb.ac_link_offset = I82586NULL;
179126003Smsmith    outw(PIOR1(base), OFFSET_CU);
179226003Smsmith    outsw(PIOP1(base), &cb, 6/2);
179326003Smsmith    outw(PIOP1(base), OFFSET_TBD);	/* cb.cmd.transmit.tbd_offset */
179426003Smsmith    outsw(PIOP1(base), eh_p->ether_dhost, WAVELAN_ADDR_SIZE/2);
179526003Smsmith    outw(PIOP1(base), eh_p->ether_type);
179626003Smsmith
179726003Smsmith#ifdef	WLDEBUG
1798147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG) {
179926003Smsmith	if (xmt_debug) {
180038505Sbde	    printf("XMT    mbuf: L%d @%p ", count, (void *)mb_p);
180126003Smsmith	    printf("ether type %x\n", eh_p->ether_type);
180226003Smsmith	}
180326003Smsmith    }
180441616Seivind#endif	/* WLDEBUG */
180526003Smsmith    outw(PIOR0(base), OFFSET_TBD);
180626003Smsmith    outw(PIOP0(base), 0);		/* act_count */
180726003Smsmith    outw(PIOR1(base), OFFSET_TBD + 4);
180826003Smsmith    outw(PIOP1(base), xmtdata_p);	/* buffer_addr */
180926003Smsmith    outw(PIOP1(base), 0);		/* buffer_base */
181026003Smsmith    for (;;) {
181126003Smsmith	if (count) {
181226003Smsmith	    if (clen + count > WAVELAN_MTU)
181326003Smsmith		break;
181426003Smsmith	    if (count & 1)
181526003Smsmith		len = count + 1;
181626003Smsmith	    else
181726003Smsmith		len = count;
181826003Smsmith	    outw(PIOR1(base), xmtdata_p);
181926003Smsmith	    outsw(PIOP1(base), mb_p, len/2);
182026003Smsmith	    clen += count;
182126003Smsmith	    outw(PIOR0(base), tbd_p);  /* address of act_count */
182226003Smsmith	    outw(PIOP0(base), inw(PIOP0(base)) + count);
182326003Smsmith	    xmtdata_p += len;
182426003Smsmith	    if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
182526003Smsmith		break;
182626003Smsmith	    if (count & 1) {
182726003Smsmith		/* go to the next descriptor */
182826003Smsmith		outw(PIOR0(base), tbd_p + 2);
182926003Smsmith		tbd_p += sizeof (tbd_t);
183026003Smsmith		outw(PIOP0(base), tbd_p); /* next_tbd_offset */
183126003Smsmith		outw(PIOR0(base), tbd_p);
183226003Smsmith		outw(PIOP0(base), 0);	/* act_count */
183326003Smsmith		outw(PIOR1(base), tbd_p + 4);
183426003Smsmith		outw(PIOP1(base), xmtdata_p); /* buffer_addr */
183526003Smsmith		outw(PIOP1(base), 0);	      /* buffer_base */
183626003Smsmith		/* at the end -> coallesce remaining mbufs */
183726003Smsmith		if (tbd_p == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) {
1838113571Sjhay		    wlsftwsleaze(&count, &mb_p, &tm_p, sc);
183926003Smsmith		    continue;
184026003Smsmith		}
184126003Smsmith		/* next mbuf short -> coallesce as needed */
184226003Smsmith		if ( (tm_p->m_next == (struct mbuf *) 0) ||
184326003Smsmith#define HDW_THRESHOLD 55
184426003Smsmith		     tm_p->m_len > HDW_THRESHOLD)
184526003Smsmith		    /* ok */;
184626003Smsmith		else {
1847113571Sjhay		    wlhdwsleaze(&count, &mb_p, &tm_p, sc);
184826003Smsmith		    continue;
184926003Smsmith		}
185026003Smsmith	    }
185126003Smsmith	} else if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
185226003Smsmith	    break;
185326003Smsmith	count = tm_p->m_len;
185426003Smsmith	mb_p = mtod(tm_p, u_char *);
185526003Smsmith#ifdef	WLDEBUG
1856147256Sbrooks	if (sc->ifp->if_flags & IFF_DEBUG)
185726003Smsmith	    if (xmt_debug)
185838505Sbde		printf("mbuf+ L%d @%p ", count, (void *)mb_p);
185941616Seivind#endif	/* WLDEBUG */
186026003Smsmith    }
186126003Smsmith#ifdef	WLDEBUG
1862147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
186326003Smsmith	if (xmt_debug)
186426003Smsmith	    printf("CLEN = %d\n", clen);
186541616Seivind#endif	/* WLDEBUG */
186626003Smsmith    outw(PIOR0(base), tbd_p);
186726003Smsmith    if (clen < ETHERMIN) {
186826003Smsmith	outw(PIOP0(base), inw(PIOP0(base)) + ETHERMIN - clen);
186926003Smsmith	outw(PIOR1(base), xmtdata_p);
187026003Smsmith	for (xmtshort_p = xmtdata_p; clen < ETHERMIN; clen += 2)
187126003Smsmith	    outw(PIOP1(base), 0);
187226003Smsmith    }
187326003Smsmith    outw(PIOP0(base), inw(PIOP0(base)) | TBD_SW_EOF);
187426003Smsmith    outw(PIOR0(base), tbd_p + 2);
187526003Smsmith    outw(PIOP0(base), I82586NULL);
187626003Smsmith#ifdef	WLDEBUG
1877147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG) {
187826003Smsmith	if (xmt_debug) {
1879113571Sjhay	    wltbd(sc);
188026003Smsmith	    printf("\n");
188126003Smsmith	}
188226003Smsmith    }
188341616Seivind#endif	/* WLDEBUG */
188426003Smsmith
188526003Smsmith    outw(PIOR0(base), OFFSET_SCB + 2);	/* address of scb_command */
188626003Smsmith    /*
188726003Smsmith     * wait for 586 to clear previous command, complain if it takes
188826003Smsmith     * too long
188926003Smsmith     */
189026003Smsmith    for (spin = 1;;spin = (spin + 1) % 10000) {
189126003Smsmith	if (inw(PIOP0(base)) == 0) {		/* it's done, we can go */
189226003Smsmith	    break;
189326003Smsmith	}
189426003Smsmith	if ((spin == 0) && xmt_watch) {		/* not waking up, and we care */
1895147256Sbrooks		printf("%s: slow accepting xmit\n", sc->ifp->if_xname);
189626003Smsmith	}
189726003Smsmith    }
189826003Smsmith    outw(PIOP0(base), SCB_CU_STRT);		/* new command */
1899113571Sjhay    SET_CHAN_ATTN(sc);
190026003Smsmith
190126003Smsmith    m_freem(m);
190226003Smsmith
190326003Smsmith    /* XXX
190426003Smsmith     * Pause to avoid transmit overrun problems.
190526003Smsmith     * The required delay tends to vary with platform type, and may be
190626003Smsmith     * related to interrupt loss.
190726003Smsmith     */
190826003Smsmith    if (wl_xmit_delay) {
190926003Smsmith	DELAY(wl_xmit_delay);
191026003Smsmith    }
191126003Smsmith    return;
191226003Smsmith}
191326003Smsmith
191426003Smsmith/*
191526003Smsmith * wlbldru:
191626003Smsmith *
191726003Smsmith *	This function builds the linear linked lists of fd's and
191826003Smsmith *	rbd's.  Based on page 4-32 of 1986 Intel microcom handbook.
191926003Smsmith *
192026003Smsmith */
192126003Smsmithstatic u_short
1922113571Sjhaywlbldru(struct wl_softc *sc)
192326003Smsmith{
192426003Smsmith    short	base = sc->base;
192526003Smsmith    fd_t	fd;
192626003Smsmith    rbd_t	rbd;
192726003Smsmith    u_short	fd_p = OFFSET_RU;
192826003Smsmith    u_short	rbd_p = OFFSET_RBD;
192926003Smsmith    int 	i;
193026003Smsmith
193126003Smsmith    sc->begin_fd = fd_p;
193279082Simp    for (i = 0; i < N_FD; i++) {
193326003Smsmith	fd.status = 0;
193426003Smsmith	fd.command = 0;
193526003Smsmith	fd.link_offset = fd_p + sizeof(fd_t);
193626003Smsmith	fd.rbd_offset = I82586NULL;
193726003Smsmith	outw(PIOR1(base), fd_p);
193826003Smsmith	outsw(PIOP1(base), &fd, 8/2);
193926003Smsmith	fd_p = fd.link_offset;
194026003Smsmith    }
194126003Smsmith    fd_p -= sizeof(fd_t);
194226003Smsmith    sc->end_fd = fd_p;
194326003Smsmith    outw(PIOR1(base), fd_p + 2);
194426003Smsmith    outw(PIOP1(base), AC_CW_EL);	/* command */
194526003Smsmith    outw(PIOP1(base), I82586NULL);	/* link_offset */
194626003Smsmith    fd_p = OFFSET_RU;
194726003Smsmith
194826003Smsmith    outw(PIOR0(base), fd_p + 6);	/* address of rbd_offset */
194926003Smsmith    outw(PIOP0(base), rbd_p);
195026003Smsmith    outw(PIOR1(base), rbd_p);
195179082Simp    for (i = 0; i < N_RBD; i++) {
195226003Smsmith	rbd.status = 0;
195326003Smsmith	rbd.buffer_addr = rbd_p + sizeof(rbd_t) + 2;
195426003Smsmith	rbd.buffer_base = 0;
195526003Smsmith	rbd.size = RCVBUFSIZE;
195626003Smsmith	if (i != N_RBD-1) {
195726003Smsmith	    rbd_p += sizeof(ru_t);
195826003Smsmith	    rbd.next_rbd_offset = rbd_p;
195926003Smsmith	} else {
196026003Smsmith	    rbd.next_rbd_offset = I82586NULL;
196126003Smsmith	    rbd.size |= AC_CW_EL;
196226003Smsmith	    sc->end_rbd = rbd_p;
196326003Smsmith	}
196426003Smsmith	outsw(PIOP1(base), &rbd, sizeof(rbd_t)/2);
196526003Smsmith	outw(PIOR1(base), rbd_p);
196626003Smsmith    }
196726003Smsmith    return sc->begin_fd;
196826003Smsmith}
196926003Smsmith
197026003Smsmith/*
197126003Smsmith * wlrustrt:
197226003Smsmith *
197326003Smsmith *	This routine starts the receive unit running.  First checks if the
197426003Smsmith *	board is actually ready, then the board is instructed to receive
197526003Smsmith *	packets again.
197626003Smsmith *
197726003Smsmith */
197826003Smsmithstatic void
1979113571Sjhaywlrustrt(struct wl_softc *sc)
198026003Smsmith{
198126003Smsmith    short		base = sc->base;
198226003Smsmith    u_short		rfa;
198326003Smsmith
198426003Smsmith#ifdef WLDEBUG
1985147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
1986113571Sjhay	printf("wl%d: entered wlrustrt()\n", sc->unit);
198726003Smsmith#endif
198826003Smsmith    outw(PIOR0(base), OFFSET_SCB);
198926003Smsmith    if (inw(PIOP0(base)) & SCB_RUS_READY){
199026003Smsmith	printf("wlrustrt: RUS_READY\n");
199126003Smsmith	return;
199226003Smsmith    }
199326003Smsmith
199426003Smsmith    outw(PIOR0(base), OFFSET_SCB + 2);
199526003Smsmith    outw(PIOP0(base), SCB_RU_STRT);		/* command */
1996113571Sjhay    rfa = wlbldru(sc);
199726003Smsmith    outw(PIOR0(base), OFFSET_SCB + 6);	/* address of scb_rfa_offset */
199826003Smsmith    outw(PIOP0(base), rfa);
199926003Smsmith
2000113571Sjhay    SET_CHAN_ATTN(sc);
200126003Smsmith    return;
200226003Smsmith}
200326003Smsmith
200426003Smsmith/*
200526003Smsmith * wldiag:
200626003Smsmith *
200726003Smsmith *	This routine does a 586 op-code number 7, and obtains the
200826003Smsmith *	diagnose status for the WaveLAN.
200926003Smsmith *
201026003Smsmith */
201126003Smsmithstatic int
2012113571Sjhaywldiag(struct wl_softc *sc)
201326003Smsmith{
2014113571Sjhay    short	base = sc->base;
2015113571Sjhay    short	status;
201626003Smsmith
201726003Smsmith#ifdef WLDEBUG
2018147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
2019113571Sjhay	printf("wl%d: entered wldiag()\n", sc->unit);
202026003Smsmith#endif
202126003Smsmith    outw(PIOR0(base), OFFSET_SCB);
202226003Smsmith    status = inw(PIOP0(base));
202326003Smsmith    if (status & SCB_SW_INT) {
202426003Smsmith		/* state is 2000 which seems ok
202526003Smsmith		   printf("wl%d diag(): unexpected initial state %\n",
2026113571Sjhay		   sc->unit, inw(PIOP0(base)));
202726003Smsmith		*/
2028113571Sjhay	wlack(sc);
202926003Smsmith    }
203026003Smsmith    outw(PIOR1(base), OFFSET_CU);
203126003Smsmith    outw(PIOP1(base), 0);			/* ac_status */
203226003Smsmith    outw(PIOP1(base), AC_DIAGNOSE|AC_CW_EL);/* ac_command */
2033113571Sjhay    if (wlcmd(sc, "diag()") == 0)
203426003Smsmith	return 0;
203526003Smsmith    outw(PIOR0(base), OFFSET_CU);
203626003Smsmith    if (inw(PIOP0(base)) & 0x0800) {
2037113571Sjhay	printf("wl%d: i82586 Self Test failed!\n", sc->unit);
203826003Smsmith	return 0;
203926003Smsmith    }
204026003Smsmith    return TRUE;
204126003Smsmith}
204226003Smsmith
204326003Smsmith/*
204426003Smsmith * wlconfig:
204526003Smsmith *
204626003Smsmith *	This routine does a standard config of the WaveLAN board.
204726003Smsmith *
204826003Smsmith */
204926003Smsmithstatic int
2050113571Sjhaywlconfig(struct wl_softc *sc)
205126003Smsmith{
2052113571Sjhay    configure_t	configure;
205326003Smsmith    short		base = sc->base;
205426003Smsmith
205526003Smsmith#if	MULTICAST
205626114Speter    struct ifmultiaddr *ifma;
205726114Speter    u_char *addrp;
205827817Smsmith    int cnt = 0;
205941616Seivind#endif	/* MULTICAST */
206026003Smsmith
206126003Smsmith#ifdef WLDEBUG
2062147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
2063113571Sjhay	printf("wl%d: entered wlconfig()\n", sc->unit);
206426003Smsmith#endif
206526003Smsmith    outw(PIOR0(base), OFFSET_SCB);
206626003Smsmith    if (inw(PIOP0(base)) & SCB_SW_INT) {
206726003Smsmith	/*
206826003Smsmith	  printf("wl%d config(): unexpected initial state %x\n",
2069113571Sjhay	  sc->unit, inw(PIOP0(base)));
207026003Smsmith	  */
207126003Smsmith    }
2072113571Sjhay    wlack(sc);
207326003Smsmith
207426003Smsmith    outw(PIOR1(base), OFFSET_CU);
207526003Smsmith    outw(PIOP1(base), 0);				/* ac_status */
207626003Smsmith    outw(PIOP1(base), AC_CONFIGURE|AC_CW_EL);	/* ac_command */
207726003Smsmith
207826003Smsmith/* jrb hack */
207926003Smsmith    configure.fifolim_bytecnt 	= 0x080c;
208026003Smsmith    configure.addrlen_mode  	= 0x0600;
208126003Smsmith    configure.linprio_interframe	= 0x2060;
208226003Smsmith    configure.slot_time      	= 0xf200;
208326003Smsmith    configure.hardware	     	= 0x0008;	/* tx even w/o CD */
208426003Smsmith    configure.min_frame_len   	= 0x0040;
208526003Smsmith#if 0
208626003Smsmith    /* This is the configuration block suggested by Marc Meertens
208726003Smsmith     * <mmeerten@obelix.utrecht.NCR.COM> in an e-mail message to John
208826003Smsmith     * Ioannidis on 10 Nov 92.
208926003Smsmith     */
209026003Smsmith    configure.fifolim_bytecnt 	= 0x040c;
209126003Smsmith    configure.addrlen_mode  	= 0x0600;
209226003Smsmith    configure.linprio_interframe	= 0x2060;
209326003Smsmith    configure.slot_time      	= 0xf000;
209426003Smsmith    configure.hardware	     	= 0x0008;	/* tx even w/o CD */
209526003Smsmith    configure.min_frame_len   	= 0x0040;
209626003Smsmith#else
209726003Smsmith    /*
209826003Smsmith     * below is the default board configuration from p2-28 from 586 book
209926003Smsmith     */
210026003Smsmith    configure.fifolim_bytecnt 	= 0x080c;
210126003Smsmith    configure.addrlen_mode  	= 0x2600;
210226003Smsmith    configure.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */
210326003Smsmith    configure.slot_time      	= 0xf00c;	/* slottime=12    */
210426003Smsmith    configure.hardware	     	= 0x0008;	/* tx even w/o CD */
210526003Smsmith    configure.min_frame_len   	= 0x0040;
210626003Smsmith#endif
210779082Simp    if (sc->mode & (MOD_PROM | MOD_ENAL))
210826003Smsmith	configure.hardware |= 1;
210926003Smsmith    outw(PIOR1(base), OFFSET_CU + 6);
211026003Smsmith    outsw(PIOP1(base), &configure, sizeof(configure_t)/2);
211126003Smsmith
2112113571Sjhay    if (wlcmd(sc, "config()-configure") == 0)
211326003Smsmith	return 0;
211426003Smsmith#if	MULTICAST
211526003Smsmith    outw(PIOR1(base), OFFSET_CU);
211626003Smsmith    outw(PIOP1(base), 0);				/* ac_status */
211726003Smsmith    outw(PIOP1(base), AC_MCSETUP|AC_CW_EL);		/* ac_command */
211826003Smsmith    outw(PIOR1(base), OFFSET_CU + 8);
2119195049Srwatson    if_maddr_rlock(sc->ifp);
2120147256Sbrooks    TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
212126114Speter	if (ifma->ifma_addr->sa_family != AF_LINK)
212226114Speter	    continue;
212327817Smsmith
212426114Speter	addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
212527817Smsmith        outw(PIOP1(base), addrp[0] + (addrp[1] << 8));
212627817Smsmith        outw(PIOP1(base), addrp[2] + (addrp[3] << 8));
212727817Smsmith        outw(PIOP1(base), addrp[4] + (addrp[5] << 8));
212827817Smsmith        ++cnt;
212926114Speter    }
2130195049Srwatson    if_maddr_runlock(sc->ifp);
213126003Smsmith    outw(PIOR1(base), OFFSET_CU + 6);		/* mc-cnt */
213226003Smsmith    outw(PIOP1(base), cnt * WAVELAN_ADDR_SIZE);
2133113571Sjhay    if (wlcmd(sc, "config()-mcaddress") == 0)
213426003Smsmith	return 0;
213541616Seivind#endif	/* MULTICAST */
213626003Smsmith
213726003Smsmith    outw(PIOR1(base), OFFSET_CU);
213826003Smsmith    outw(PIOP1(base), 0);				/* ac_status */
213926003Smsmith    outw(PIOP1(base), AC_IASETUP|AC_CW_EL);		/* ac_command */
214026003Smsmith    outw(PIOR1(base), OFFSET_CU + 6);
2141152315Sru    outsw(PIOP1(base), IF_LLADDR(sc->ifp), WAVELAN_ADDR_SIZE/2);
214226003Smsmith
2143113571Sjhay    if (wlcmd(sc, "config()-address") == 0)
214426003Smsmith	return(0);
214526003Smsmith
2146113571Sjhay    wlinitmmc(sc);
214726003Smsmith
214826003Smsmith    return(1);
214926003Smsmith}
215026003Smsmith
215126003Smsmith/*
215226003Smsmith * wlcmd:
215326003Smsmith *
215426003Smsmith * Set channel attention bit and busy wait until command has
215526003Smsmith * completed. Then acknowledge the command completion.
215626003Smsmith */
215726003Smsmithstatic int
2158113571Sjhaywlcmd(struct wl_softc *sc, char *str)
215926003Smsmith{
216026003Smsmith    short	base = sc->base;
216126003Smsmith    int i;
216226003Smsmith
216326003Smsmith    outw(PIOR0(base), OFFSET_SCB + 2);	/* address of scb_command */
216426003Smsmith    outw(PIOP0(base), SCB_CU_STRT);
216526003Smsmith
2166113571Sjhay    SET_CHAN_ATTN(sc);
216726003Smsmith
216826003Smsmith    outw(PIOR0(base), OFFSET_CU);
216979082Simp    for (i = 0; i < 0xffff; i++)
217026003Smsmith	if (inw(PIOP0(base)) & AC_SW_C)
217126003Smsmith	    break;
217226003Smsmith    if (i == 0xffff || !(inw(PIOP0(base)) & AC_SW_OK)) {
217326003Smsmith	printf("wl%d: %s failed; status = %d, inw = %x, outw = %x\n",
2174113571Sjhay	       sc->unit, str, inw(PIOP0(base)) & AC_SW_OK, inw(PIOP0(base)), inw(PIOR0(base)));
217526003Smsmith	outw(PIOR0(base), OFFSET_SCB);
217626003Smsmith	printf("scb_status %x\n", inw(PIOP0(base)));
217726003Smsmith	outw(PIOR0(base), OFFSET_SCB+2);
217826003Smsmith	printf("scb_command %x\n", inw(PIOP0(base)));
217926003Smsmith	outw(PIOR0(base), OFFSET_SCB+4);
218026003Smsmith	printf("scb_cbl %x\n", inw(PIOP0(base)));
218126003Smsmith	outw(PIOR0(base), OFFSET_CU+2);
218226003Smsmith	printf("cu_cmd %x\n", inw(PIOP0(base)));
218326003Smsmith	return(0);
218426003Smsmith    }
218526003Smsmith
218626003Smsmith    outw(PIOR0(base), OFFSET_SCB);
218726003Smsmith    if ((inw(PIOP0(base)) & SCB_SW_INT) && (inw(PIOP0(base)) != SCB_SW_CNA)) {
218826003Smsmith	/*
218926003Smsmith	  printf("wl%d %s: unexpected final state %x\n",
2190113571Sjhay	  sc->unit, str, inw(PIOP0(base)));
219126003Smsmith	  */
219226003Smsmith    }
2193113571Sjhay    wlack(sc);
219426003Smsmith    return(TRUE);
219526003Smsmith}
219626003Smsmith
219726003Smsmith/*
219826003Smsmith * wlack: if the 82596 wants attention because it has finished
219926003Smsmith * sending or receiving a packet, acknowledge its desire and
220026003Smsmith * return bits indicating the kind of attention. wlack() returns
220126003Smsmith * these bits so that the caller can service exactly the
220226003Smsmith * conditions that wlack() acknowledged.
220326003Smsmith */
220426003Smsmithstatic int
2205113571Sjhaywlack(struct wl_softc *sc)
220626003Smsmith{
220726003Smsmith    int i;
2208113566Sjhay    u_short cmd;
220926003Smsmith    short base = sc->base;
221026003Smsmith
221126003Smsmith    outw(PIOR1(base), OFFSET_SCB);
221279082Simp    if (!(cmd = (inw(PIOP1(base)) & SCB_SW_INT)))
221326003Smsmith	return(0);
221426003Smsmith#ifdef WLDEBUG
2215147256Sbrooks    if (sc->ifp->if_flags & IFF_DEBUG)
2216113571Sjhay	printf("wl%d: doing a wlack()\n", sc->unit);
221726003Smsmith#endif
221826003Smsmith    outw(PIOP1(base), cmd);
2219113571Sjhay    SET_CHAN_ATTN(sc);
222026003Smsmith    outw(PIOR0(base), OFFSET_SCB + 2);	/* address of scb_command */
222179082Simp    for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); )
222279082Simp	continue;
222326003Smsmith    if (i < 1)
2224113571Sjhay	printf("wl%d wlack(): board not accepting command.\n", sc->unit);
222526003Smsmith    return(cmd);
222626003Smsmith}
222726003Smsmith
222879081Simp#ifdef WLDEBUG
222926003Smsmithstatic void
2230113571Sjhaywltbd(struct wl_softc *sc)
223126003Smsmith{
223226003Smsmith    short		base = sc->base;
223326003Smsmith    u_short		tbd_p = OFFSET_TBD;
223426003Smsmith    tbd_t		tbd;
223526003Smsmith    int 		i = 0;
223626003Smsmith    int			sum = 0;
223726003Smsmith
223826003Smsmith    for (;;) {
223926003Smsmith	outw(PIOR1(base), tbd_p);
224026003Smsmith	insw(PIOP1(base), &tbd, sizeof(tbd_t)/2);
224126003Smsmith	sum += (tbd.act_count & ~TBD_SW_EOF);
224226003Smsmith	printf("%d: addr %x, count %d (%d), next %x, base %x\n",
224326003Smsmith	       i++, tbd.buffer_addr,
224426003Smsmith	       (tbd.act_count & ~TBD_SW_EOF), sum,
224526003Smsmith	       tbd.next_tbd_offset, tbd.buffer_base);
224626003Smsmith	if (tbd.act_count & TBD_SW_EOF)
224726003Smsmith	    break;
224826003Smsmith	tbd_p = tbd.next_tbd_offset;
224926003Smsmith    }
225026003Smsmith}
225179081Simp#endif
225226003Smsmith
225326003Smsmithstatic void
2254113571Sjhaywlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc)
225526003Smsmith{
225626003Smsmith    struct mbuf	*tm_p = *tm_pp;
2257113571Sjhay    u_char		*mb_p = *mb_pp;
2258113571Sjhay    u_short		count = 0;
2259113571Sjhay    u_char		*cp;
226026003Smsmith    int		len;
226126003Smsmith
226226003Smsmith    /*
226326003Smsmith     * can we get a run that will be coallesced or
226426003Smsmith     * that terminates before breaking
226526003Smsmith     */
226626003Smsmith    do {
226726003Smsmith	count += tm_p->m_len;
226826003Smsmith	if (tm_p->m_len & 1)
226926003Smsmith	    break;
227026003Smsmith    } while ((tm_p = tm_p->m_next) != (struct mbuf *)0);
227126003Smsmith    if ( (tm_p == (struct mbuf *)0) ||
227226003Smsmith	 count > HDW_THRESHOLD) {
227326003Smsmith	*countp = (*tm_pp)->m_len;
227426003Smsmith	*mb_pp = mtod((*tm_pp), u_char *);
227526003Smsmith	return;
227626003Smsmith    }
227726003Smsmith
227826003Smsmith    /* we need to copy */
227926003Smsmith    tm_p = *tm_pp;
228026003Smsmith    mb_p = *mb_pp;
228126003Smsmith    count = 0;
228226003Smsmith    cp = (u_char *) t_packet;
228326003Smsmith    for (;;) {
228426003Smsmith	bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
228526003Smsmith	count += len;
228626003Smsmith	if (count > HDW_THRESHOLD)
228726003Smsmith			break;
228826003Smsmith	cp += len;
228926003Smsmith	if (tm_p->m_next == (struct mbuf *)0)
229026003Smsmith	    break;
229126003Smsmith	tm_p = tm_p->m_next;
229226003Smsmith    }
229326003Smsmith    *countp = count;
229426003Smsmith    *mb_pp = (u_char *) t_packet;
229526003Smsmith    *tm_pp = tm_p;
229626003Smsmith    return;
229726003Smsmith}
229826003Smsmith
229926003Smsmith
230026003Smsmithstatic void
2301113571Sjhaywlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc)
230226003Smsmith{
230326003Smsmith    struct mbuf	*tm_p = *tm_pp;
2304113571Sjhay    u_short		count = 0;
2305113571Sjhay    u_char		*cp = (u_char *) t_packet;
2306113571Sjhay    int			len;
230726003Smsmith
230826003Smsmith    /* we need to copy */
230926003Smsmith    for (;;) {
231026003Smsmith	bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
231126003Smsmith	count += len;
231226003Smsmith	cp += len;
231326003Smsmith	if (tm_p->m_next == (struct mbuf *)0)
231426003Smsmith	    break;
231526003Smsmith	tm_p = tm_p->m_next;
231626003Smsmith    }
231726003Smsmith
231826003Smsmith    *countp = count;
231926003Smsmith    *mb_pp = (u_char *) t_packet;
232026003Smsmith    *tm_pp = tm_p;
232126003Smsmith    return;
232226003Smsmith}
232326003Smsmith
232426003Smsmithstatic void
2325113571Sjhaywlmmcstat(struct wl_softc *sc)
232626003Smsmith{
232726003Smsmith    short	base = sc->base;
232826003Smsmith    u_short tmp;
232926003Smsmith
2330113571Sjhay    printf("wl%d: DCE_STATUS: 0x%x, ", sc->unit,
233126003Smsmith	   wlmmcread(base,MMC_DCE_STATUS) & 0x0f);
233226003Smsmith    tmp = wlmmcread(base,MMC_CORRECT_NWID_H) << 8;
233326003Smsmith    tmp |= wlmmcread(base,MMC_CORRECT_NWID_L);
233426003Smsmith    printf("Correct NWID's: %d, ", tmp);
233526003Smsmith    tmp = wlmmcread(base,MMC_WRONG_NWID_H) << 8;
233626003Smsmith    tmp |= wlmmcread(base,MMC_WRONG_NWID_L);
233726003Smsmith    printf("Wrong NWID's: %d\n", tmp);
233826003Smsmith    printf("THR_PRE_SET: 0x%x, ", wlmmcread(base,MMC_THR_PRE_SET));
233926003Smsmith    printf("SIGNAL_LVL: %d, SILENCE_LVL: %d\n",
234026003Smsmith	   wlmmcread(base,MMC_SIGNAL_LVL),
234126003Smsmith	   wlmmcread(base,MMC_SILENCE_LVL));
234226003Smsmith    printf("SIGN_QUAL: 0x%x, NETW_ID: %x:%x, DES: %d\n",
234326003Smsmith	   wlmmcread(base,MMC_SIGN_QUAL),
234426003Smsmith	   wlmmcread(base,MMC_NETW_ID_H),
234526003Smsmith	   wlmmcread(base,MMC_NETW_ID_L),
234626003Smsmith	   wlmmcread(base,MMC_DES_AVAIL));
234726003Smsmith}
234826003Smsmith
234926003Smsmithstatic u_short
235026003Smsmithwlmmcread(u_int base, u_short reg)
235126003Smsmith{
235279082Simp    while (inw(HASR(base)) & HASR_MMC_BUSY)
235379082Simp	continue;
235426003Smsmith    outw(MMCR(base),reg << 1);
235579082Simp    while (inw(HASR(base)) & HASR_MMC_BUSY)
235679082Simp	continue;
235726003Smsmith    return (u_short)inw(MMCR(base)) >> 8;
235826003Smsmith}
235926003Smsmith
236026003Smsmithstatic void
2361113571Sjhaygetsnr(struct wl_softc *sc)
236226003Smsmith{
236326003Smsmith    MMC_WRITE(MMC_FREEZE,1);
236426003Smsmith    /*
236526003Smsmith     * SNR retrieval procedure :
236626003Smsmith     *
236726003Smsmith     * read signal level : wlmmcread(base, MMC_SIGNAL_LVL);
236826003Smsmith     * read silence level : wlmmcread(base, MMC_SILENCE_LVL);
236926003Smsmith     */
237026003Smsmith    MMC_WRITE(MMC_FREEZE,0);
237126003Smsmith    /*
237226003Smsmith     * SNR is signal:silence ratio.
237326003Smsmith     */
237426003Smsmith}
237526003Smsmith
237626003Smsmith/*
237726003Smsmith** wlgetpsa
237826003Smsmith**
237926003Smsmith** Reads the psa for the wavelan at (base) into (buf)
238026003Smsmith*/
238126003Smsmithstatic void
238226003Smsmithwlgetpsa(int base, u_char *buf)
238326003Smsmith{
238426003Smsmith    int	i;
238526003Smsmith
238626003Smsmith    PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
238726003Smsmith    PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
238826003Smsmith
238926003Smsmith    for (i = 0; i < 0x40; i++) {
239026003Smsmith	outw(PIOR2(base), i);
239126003Smsmith	buf[i] = inb(PIOP2(base));
239226003Smsmith    }
239326003Smsmith    PCMD(base, HACR_DEFAULT);
239426003Smsmith    PCMD(base, HACR_DEFAULT);
239526003Smsmith}
239626003Smsmith
239726003Smsmith/*
239826003Smsmith** wlsetpsa
239926003Smsmith**
240026003Smsmith** Writes the psa for wavelan (unit) from the softc back to the
240126003Smsmith** board.  Updates the CRC and sets the CRC OK flag.
240226003Smsmith**
240326003Smsmith** Do not call this when the board is operating, as it doesn't
240426003Smsmith** preserve the hacr.
240526003Smsmith*/
240626003Smsmithstatic void
2407113571Sjhaywlsetpsa(struct wl_softc *sc)
240826003Smsmith{
240926003Smsmith    short	base = sc->base;
2410113601Sjhay    int		i, oldpri;
241126003Smsmith    u_short	crc;
241226003Smsmith
241326003Smsmith    crc = wlpsacrc(sc->psa);	/* calculate CRC of PSA */
241426003Smsmith    sc->psa[WLPSA_CRCLOW] = crc & 0xff;
241526003Smsmith    sc->psa[WLPSA_CRCHIGH] = (crc >> 8) & 0xff;
241626003Smsmith    sc->psa[WLPSA_CRCOK] = 0x55;	/* default to 'bad' until programming complete */
241726003Smsmith
2418113601Sjhay    oldpri = splimp();		/* ick, long pause */
2419113601Sjhay
242026003Smsmith    PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
242126003Smsmith    PCMD(base, HACR_DEFAULT & ~HACR_16BITS);
242226003Smsmith
242326003Smsmith    for (i = 0; i < 0x40; i++) {
242426003Smsmith	DELAY(DELAYCONST);
242526003Smsmith	outw(PIOR2(base),i);  /* write param memory */
242626003Smsmith	DELAY(DELAYCONST);
242726003Smsmith	outb(PIOP2(base), sc->psa[i]);
242826003Smsmith    }
242926003Smsmith    DELAY(DELAYCONST);
243026003Smsmith    outw(PIOR2(base),WLPSA_CRCOK);  /* update CRC flag*/
243126003Smsmith    DELAY(DELAYCONST);
243226003Smsmith    sc->psa[WLPSA_CRCOK] = 0xaa;	/* OK now */
243326003Smsmith    outb(PIOP2(base), 0xaa);	/* all OK */
243426003Smsmith    DELAY(DELAYCONST);
243526003Smsmith
243626003Smsmith    PCMD(base, HACR_DEFAULT);
243726003Smsmith    PCMD(base, HACR_DEFAULT);
2438113601Sjhay
2439113601Sjhay    splx(oldpri);
244026003Smsmith}
244126003Smsmith
244226003Smsmith/*
244326003Smsmith** CRC routine provided by Christopher Giordano <cgiordan@gdeb.com>,
244426003Smsmith** from original code by Tomi Mikkonen (tomitm@remedy.fi)
244526003Smsmith*/
244626003Smsmith
244726003Smsmithstatic u_int crc16_table[16] = {
244826003Smsmith    0x0000, 0xCC01, 0xD801, 0x1400,
244926003Smsmith    0xF001, 0x3C00, 0x2800, 0xE401,
245026003Smsmith    0xA001, 0x6C00, 0x7800, 0xB401,
245126003Smsmith    0x5000, 0x9C01, 0x8801, 0x4400
245226003Smsmith};
245326003Smsmith
245426003Smsmithstatic u_short
245526003Smsmithwlpsacrc(u_char *buf)
245626003Smsmith{
245726003Smsmith    u_short	crc = 0;
245826003Smsmith    int		i, r1;
245926003Smsmith
246026003Smsmith    for (i = 0; i < 0x3d; i++, buf++) {
246126003Smsmith	/* lower 4 bits */
246226003Smsmith	r1 = crc16_table[crc & 0xF];
246326003Smsmith	crc = (crc >> 4) & 0x0FFF;
246426003Smsmith	crc = crc ^ r1 ^ crc16_table[*buf & 0xF];
246526003Smsmith
246626003Smsmith	/* upper 4 bits */
246726003Smsmith	r1 = crc16_table[crc & 0xF];
246826003Smsmith	crc = (crc >> 4) & 0x0FFF;
246926003Smsmith	crc = crc ^ r1 ^ crc16_table[(*buf >> 4) & 0xF];
247026003Smsmith    }
247126003Smsmith    return(crc);
247226003Smsmith}
247327817Smsmith#ifdef WLCACHE
247427817Smsmith
247527817Smsmith/*
247627817Smsmith * wl_cache_store
247727817Smsmith *
247827817Smsmith * take input packet and cache various radio hw characteristics
247927817Smsmith * indexed by MAC address.
248027817Smsmith *
248127817Smsmith * Some things to think about:
248227817Smsmith *	note that no space is malloced.
248327817Smsmith *	We might hash the mac address if the cache were bigger.
248427817Smsmith *	It is not clear that the cache is big enough.
248527817Smsmith *		It is also not clear how big it should be.
248627817Smsmith *	The cache is IP-specific.  We don't care about that as
248727817Smsmith *		we want it to be IP-specific.
248827817Smsmith *	The last N recv. packets are saved.  This will tend
248927817Smsmith *		to reward agents and mobile hosts that beacon.
249027817Smsmith *		That is probably fine for mobile ip.
249127817Smsmith */
249227817Smsmith
249327817Smsmith/* globals for wavelan signal strength cache */
249427817Smsmith/* this should go into softc structure above.
249527817Smsmith*/
249627817Smsmith
249727817Smsmith/* set true if you want to limit cache items to broadcast/mcast
249827817Smsmith * only packets (not unicast)
249927817Smsmith */
250027817Smsmithstatic int wl_cache_mcastonly = 1;
250127817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_cache_mcastonly, CTLFLAG_RW,
250227817Smsmith	&wl_cache_mcastonly, 0, "");
250327817Smsmith
250427817Smsmith/* set true if you want to limit cache items to IP packets only
250527817Smsmith*/
250627817Smsmithstatic int wl_cache_iponly = 1;
250727817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_cache_iponly, CTLFLAG_RW,
250827817Smsmith	&wl_cache_iponly, 0, "");
250927817Smsmith
251027817Smsmith/* zero out the cache
251127817Smsmith*/
251227817Smsmithstatic void
2513113571Sjhaywl_cache_zero(struct wl_softc *sc)
251427817Smsmith{
251527817Smsmith
251627817Smsmith	bzero(&sc->w_sigcache[0], sizeof(struct w_sigcache) * MAXCACHEITEMS);
251727817Smsmith	sc->w_sigitems = 0;
251827817Smsmith	sc->w_nextcache = 0;
251927817Smsmith	sc->w_wrapindex = 0;
252027817Smsmith}
252127817Smsmith
252227817Smsmith/* store hw signal info in cache.
252327817Smsmith * index is MAC address, but an ip src gets stored too
252427817Smsmith * There are two filters here controllable via sysctl:
252527817Smsmith *	throw out unicast (on by default, but can be turned off)
252627817Smsmith *	throw out non-ip (on by default, but can be turned off)
252727817Smsmith */
252827817Smsmithstatic
2529113571Sjhayvoid wl_cache_store (struct wl_softc *sc, int base, struct ether_header *eh,
253027817Smsmith      		     struct mbuf *m)
253127817Smsmith{
2532113571Sjhay#ifdef INET
253342546Seivind	struct ip *ip = NULL;	/* Avoid GCC warning */
253427817Smsmith	int i;
253527817Smsmith	int signal, silence;
253627817Smsmith	int w_insertcache;   /* computed index for cache entry storage */
253727817Smsmith	int ipflag = wl_cache_iponly;
2538113571Sjhay#endif
253927817Smsmith
254027817Smsmith	/* filters:
254127817Smsmith	 * 1. ip only
254227817Smsmith	 * 2. configurable filter to throw out unicast packets,
254327817Smsmith	 * keep multicast only.
254427817Smsmith	 */
254527817Smsmith
254632350Seivind#ifdef INET
254727817Smsmith	/* reject if not IP packet
254827817Smsmith	*/
254927817Smsmith	if ( wl_cache_iponly && (ntohs(eh->ether_type) != 0x800)) {
255027817Smsmith		return;
255127817Smsmith	}
255227817Smsmith
255327817Smsmith	/* check if broadcast or multicast packet.  we toss
255427817Smsmith	 * unicast packets
255527817Smsmith	 */
255627817Smsmith	if (wl_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
255727817Smsmith		return;
255827817Smsmith	}
255927817Smsmith
256027817Smsmith	/* find the ip header.  we want to store the ip_src
256127817Smsmith	 * address.  use the mtod macro(in mbuf.h)
256227817Smsmith	 * to typecast m to struct ip *
256327817Smsmith	 */
256427817Smsmith	if (ipflag) {
256527817Smsmith		ip = mtod(m, struct ip *);
256627817Smsmith	}
256727817Smsmith
256827817Smsmith	/* do a linear search for a matching MAC address
256927817Smsmith	 * in the cache table
257027817Smsmith	 * . MAC address is 6 bytes,
257127817Smsmith	 * . var w_nextcache holds total number of entries already cached
257227817Smsmith	 */
257379082Simp	for (i = 0; i < sc->w_nextcache; i++) {
257427817Smsmith		if (! bcmp(eh->ether_shost, sc->w_sigcache[i].macsrc,  6 )) {
257527817Smsmith			/* Match!,
257627817Smsmith			 * so we already have this entry,
257727817Smsmith			 * update the data, and LRU age
257827817Smsmith			 */
257927817Smsmith			break;
258027817Smsmith		}
258127817Smsmith	}
258227817Smsmith
258327817Smsmith	/* did we find a matching mac address?
258427817Smsmith	 * if yes, then overwrite a previously existing cache entry
258527817Smsmith	 */
258627817Smsmith	if (i <  sc->w_nextcache )   {
258727817Smsmith		w_insertcache = i;
258827817Smsmith	}
258927817Smsmith	/* else, have a new address entry,so
259027817Smsmith	 * add this new entry,
259127817Smsmith	 * if table full, then we need to replace entry
259227817Smsmith	 */
259327817Smsmith	else    {
259427817Smsmith
259527817Smsmith		/* check for space in cache table
259627817Smsmith		 * note: w_nextcache also holds number of entries
259727817Smsmith		 * added in the cache table
259827817Smsmith		 */
259927817Smsmith		if ( sc->w_nextcache < MAXCACHEITEMS ) {
260027817Smsmith			w_insertcache = sc->w_nextcache;
260127817Smsmith			sc->w_nextcache++;
260227817Smsmith			sc->w_sigitems = sc->w_nextcache;
260327817Smsmith		}
260427817Smsmith        	/* no space found, so simply wrap with wrap index
260527817Smsmith		 * and "zap" the next entry
260627817Smsmith		 */
260727817Smsmith		else {
260827817Smsmith			if (sc->w_wrapindex == MAXCACHEITEMS) {
260927817Smsmith				sc->w_wrapindex = 0;
261027817Smsmith			}
261127817Smsmith			w_insertcache = sc->w_wrapindex++;
261227817Smsmith		}
261327817Smsmith	}
261427817Smsmith
261527817Smsmith	/* invariant: w_insertcache now points at some slot
261627817Smsmith	 * in cache.
261727817Smsmith	 */
261827817Smsmith	if (w_insertcache < 0 || w_insertcache >= MAXCACHEITEMS) {
261927817Smsmith		log(LOG_ERR,
262027817Smsmith			"wl_cache_store, bad index: %d of [0..%d], gross cache error\n",
262127817Smsmith			w_insertcache, MAXCACHEITEMS);
262227817Smsmith		return;
262327817Smsmith	}
262427817Smsmith
262527817Smsmith	/*  store items in cache
262627817Smsmith	 *  .ipsrc
262727817Smsmith	 *  .macsrc
262827817Smsmith	 *  .signal (0..63) ,silence (0..63) ,quality (0..15)
262927817Smsmith	 */
263027817Smsmith	if (ipflag) {
263127817Smsmith		sc->w_sigcache[w_insertcache].ipsrc = ip->ip_src.s_addr;
263227817Smsmith	}
263327817Smsmith	bcopy( eh->ether_shost, sc->w_sigcache[w_insertcache].macsrc,  6);
263427817Smsmith	signal = sc->w_sigcache[w_insertcache].signal  = wlmmcread(base, MMC_SIGNAL_LVL) & 0x3f;
263527817Smsmith	silence = sc->w_sigcache[w_insertcache].silence = wlmmcread(base, MMC_SILENCE_LVL) & 0x3f;
263627817Smsmith	sc->w_sigcache[w_insertcache].quality = wlmmcread(base, MMC_SIGN_QUAL) & 0x0f;
263727817Smsmith	if (signal > 0)
263827817Smsmith		sc->w_sigcache[w_insertcache].snr =
263927817Smsmith			signal - silence;
264027817Smsmith	else
264127817Smsmith		sc->w_sigcache[w_insertcache].snr = 0;
264232350Seivind#endif /* INET */
264327817Smsmith
264427817Smsmith}
264527817Smsmith#endif /* WLCACHE */
2646