if_wi.c revision 47401
146492Swpaul/*
246492Swpaul * Copyright (c) 1997, 1998, 1999
346492Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
446492Swpaul *
546492Swpaul * Redistribution and use in source and binary forms, with or without
646492Swpaul * modification, are permitted provided that the following conditions
746492Swpaul * are met:
846492Swpaul * 1. Redistributions of source code must retain the above copyright
946492Swpaul *    notice, this list of conditions and the following disclaimer.
1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1146492Swpaul *    notice, this list of conditions and the following disclaimer in the
1246492Swpaul *    documentation and/or other materials provided with the distribution.
1346492Swpaul * 3. All advertising materials mentioning features or use of this software
1446492Swpaul *    must display the following acknowledgement:
1546492Swpaul *	This product includes software developed by Bill Paul.
1646492Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1746492Swpaul *    may be used to endorse or promote products derived from this software
1846492Swpaul *    without specific prior written permission.
1946492Swpaul *
2046492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2146492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2246492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2346492Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2446492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2546492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2646492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2746492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2846492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2946492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3046492Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3146492Swpaul *
3247401Swpaul *	$Id: if_wi.c,v 1.54 1999/05/20 04:10:40 wpaul Exp $
3346492Swpaul */
3446492Swpaul
3546492Swpaul/*
3646492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD.
3746492Swpaul *
3846492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
3946492Swpaul * Electrical Engineering Department
4046492Swpaul * Columbia University, New York City
4146492Swpaul */
4246492Swpaul
4346492Swpaul/*
4447401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN
4546492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed
4646492Swpaul * entirely via a firmware-driven controller called the Hermes.
4746492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual
4846492Swpaul * without an NDA (if at all). What they do release is an API library
4946492Swpaul * called the HCF (Hardware Control Functions) which is supposed to
5046492Swpaul * do the device-specific operations of a device driver for you. The
5146492Swpaul * publically available version of the HCF library (the 'HCF Light') is
5247401Swpaul * a) extremely gross, b) lacks certain features, particularly support
5346492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License.
5446492Swpaul *
5546492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it
5646492Swpaul * programs the Hermes controller directly, using information gleaned
5746492Swpaul * from the HCF Light code and corresponding documentation.
5846492Swpaul *
5946492Swpaul * This driver supports both the PCMCIA and ISA versions of the
6046492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really
6146492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter
6246492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is
6346492Swpaul * inserted. Consequently, you need to use the pccard support for
6446492Swpaul * both the ISA and PCMCIA adapters.
6546492Swpaul */
6646492Swpaul
6746492Swpaul#define WI_HERMES_AUTOINC_WAR	/* Work around data write autoinc bug. */
6846492Swpaul#define WI_HERMES_STATS_WAR	/* Work around stats counter bug. */
6946492Swpaul
7046492Swpaul#include "bpfilter.h"
7146492Swpaul#include "card.h"
7246492Swpaul#include "wi.h"
7346492Swpaul
7446492Swpaul#include <sys/param.h>
7546492Swpaul#include <sys/systm.h>
7646492Swpaul#include <sys/sockio.h>
7746492Swpaul#include <sys/mbuf.h>
7846492Swpaul#include <sys/malloc.h>
7946492Swpaul#include <sys/kernel.h>
8046492Swpaul#include <sys/socket.h>
8146492Swpaul
8246492Swpaul#include <net/if.h>
8346492Swpaul#include <net/if_arp.h>
8446492Swpaul#include <net/ethernet.h>
8546492Swpaul#include <net/if_dl.h>
8646492Swpaul#include <net/if_media.h>
8746492Swpaul#include <net/if_types.h>
8846492Swpaul
8946492Swpaul#ifdef INET
9046492Swpaul#include <netinet/in.h>
9146492Swpaul#include <netinet/in_systm.h>
9246492Swpaul#include <netinet/in_var.h>
9346492Swpaul#include <netinet/ip.h>
9446492Swpaul#include <netinet/if_ether.h>
9546492Swpaul#endif
9646492Swpaul
9746492Swpaul#if NBPFILTER > 0
9846492Swpaul#include <net/bpf.h>
9946492Swpaul#endif
10046492Swpaul
10146492Swpaul#include <machine/clock.h>
10246492Swpaul#include <machine/md_var.h>
10346492Swpaul#include <machine/bus_pio.h>
10446492Swpaul#include <machine/bus.h>
10546492Swpaul
10646492Swpaul#include <i386/isa/isa_device.h>
10746492Swpaul#include <i386/isa/icu.h>
10846492Swpaul#include <i386/isa/if_wireg.h>
10946492Swpaul#include <machine/if_wavelan_ieee.h>
11046492Swpaul
11146492Swpaul#if NCARD > 0
11246492Swpaul#include <sys/select.h>
11346492Swpaul#include <pccard/cardinfo.h>
11446492Swpaul#include <pccard/slot.h>
11546492Swpaul#endif
11646492Swpaul
11746492Swpaul#if !defined(lint)
11846492Swpaulstatic const char rcsid[] =
11947401Swpaul	"$Id: if_wi.c,v 1.54 1999/05/20 04:10:40 wpaul Exp $";
12046492Swpaul#endif
12146492Swpaul
12246492Swpaulstatic struct wi_softc wi_softc[NWI];
12346492Swpaul
12446492Swpaul#ifdef foo
12547401Swpaulstatic u_int8_t	wi_mcast_addr[6] = { 0x01, 0x60, 0x1D, 0x00, 0x01, 0x00 };
12646492Swpaul#endif
12746492Swpaul
12846492Swpaulstatic int wi_probe		__P((struct isa_device *));
12946492Swpaulstatic int wi_attach		__P((struct isa_device *));
13046492Swpaul#ifdef PCCARD_MODULE
13146492Swpaulstatic ointhand2_t		wi_intr;
13246492Swpaul#endif
13346492Swpaulstatic void wi_reset		__P((struct wi_softc *));
13446492Swpaulstatic int wi_ioctl		__P((struct ifnet *, u_long, caddr_t));
13546492Swpaulstatic void wi_init		__P((void *));
13646492Swpaulstatic void wi_start		__P((struct ifnet *));
13746492Swpaulstatic void wi_stop		__P((struct wi_softc *));
13846492Swpaulstatic void wi_watchdog		__P((struct ifnet *));
13946492Swpaulstatic void wi_shutdown		__P((int, void *));
14046492Swpaulstatic void wi_rxeof		__P((struct wi_softc *));
14146492Swpaulstatic void wi_txeof		__P((struct wi_softc *, int));
14246492Swpaulstatic void wi_update_stats	__P((struct wi_softc *));
14346492Swpaulstatic void wi_setmulti		__P((struct wi_softc *));
14446492Swpaul
14546492Swpaulstatic int wi_cmd		__P((struct wi_softc *, int, int));
14646492Swpaulstatic int wi_read_record	__P((struct wi_softc *, struct wi_ltv_gen *));
14746492Swpaulstatic int wi_write_record	__P((struct wi_softc *, struct wi_ltv_gen *));
14846492Swpaulstatic int wi_read_data		__P((struct wi_softc *, int,
14946492Swpaul					int, caddr_t, int));
15046492Swpaulstatic int wi_write_data	__P((struct wi_softc *, int,
15146492Swpaul					int, caddr_t, int));
15246492Swpaulstatic int wi_seek		__P((struct wi_softc *, int, int, int));
15346492Swpaulstatic int wi_alloc_nicmem	__P((struct wi_softc *, int, int *));
15446492Swpaulstatic void wi_inquire		__P((void *));
15546492Swpaulstatic void wi_setdef		__P((struct wi_softc *, struct wi_req *));
15646492Swpaulstatic int wi_mgmt_xmit		__P((struct wi_softc *, caddr_t, int));
15746492Swpaul
15846492Swpaulstruct isa_driver widriver = {
15946492Swpaul	wi_probe,
16046492Swpaul	wi_attach,
16146492Swpaul	"wi",
16246492Swpaul	1
16346492Swpaul};
16446492Swpaul
16546492Swpaul#if NCARD > 0
16646492Swpaulstatic int wi_pccard_init	__P((struct pccard_devinfo *));
16746492Swpaulstatic void wi_pccard_unload	__P((struct pccard_devinfo *));
16846492Swpaulstatic int wi_pccard_intr	__P((struct pccard_devinfo *));
16946492Swpaul
17046492Swpaul#ifdef PCCARD_MODULE
17146492SwpaulPCCARD_MODULE(wi, wi_pccard_init, wi_pccard_unload,
17246492Swpaul		wi_pccard_intr, 0, net_imask);
17346492Swpaul#else
17446492Swpaulstatic struct pccard_device wi_info = {
17546492Swpaul	"wi",
17646492Swpaul	wi_pccard_init,
17746492Swpaul	wi_pccard_unload,
17846492Swpaul	wi_pccard_intr,
17946492Swpaul	0,			/* Attributes - presently unused */
18046492Swpaul	&net_imask		/* Interrupt mask for device */
18146492Swpaul				/* XXX - Should this also include net_imask? */
18246492Swpaul};
18346492Swpaul
18446492SwpaulDATA_SET(pccarddrv_set, wi_info);
18546492Swpaul#endif
18646492Swpaul
18746492Swpaul/* Initialize the PCCARD. */
18846492Swpaulstatic int wi_pccard_init(sc_p)
18946492Swpaul	struct pccard_devinfo	*sc_p;
19046492Swpaul{
19146492Swpaul	struct wi_softc		*sc;
19246492Swpaul	int			i;
19346492Swpaul	u_int32_t		irq;
19446492Swpaul
19546492Swpaul	if (sc_p->isahd.id_unit >= NWI)
19646492Swpaul		return(ENODEV);
19746492Swpaul
19846492Swpaul	sc = &wi_softc[sc_p->isahd.id_unit];
19946492Swpaul	sc->wi_gone = 0;
20046492Swpaul	sc->wi_unit = sc_p->isahd.id_unit;
20146492Swpaul	sc->wi_bhandle = sc_p->isahd.id_iobase;
20246492Swpaul	sc->wi_btag = I386_BUS_SPACE_IO;
20346492Swpaul
20446492Swpaul	/* Make sure interrupts are disabled. */
20546492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
20646492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
20746492Swpaul
20846492Swpaul	/* Grr. IRQ is encoded as a bitmask. */
20946492Swpaul	irq = sc_p->isahd.id_irq;
21046492Swpaul	for (i = 0; i < 32; i++) {
21146492Swpaul		if (irq & 0x1)
21246492Swpaul			break;
21346492Swpaul		irq >>= 1;
21446492Swpaul	}
21546492Swpaul
21646492Swpaul	/*
21746492Swpaul	 * Print a nice probe message to let the operator
21846492Swpaul	 * know something interesting is happening.
21946492Swpaul	 */
22046492Swpaul	printf("wi%d: <WaveLAN/IEEE 802.11> at 0x%x-0x%x irq %d on isa\n",
22146492Swpaul	    sc_p->isahd.id_unit, sc_p->isahd.id_iobase,
22246492Swpaul	    sc_p->isahd.id_iobase + WI_IOSIZ - 1, i);
22346492Swpaul
22446492Swpaul	if (wi_attach(&sc_p->isahd))
22546492Swpaul		return(ENXIO);
22646492Swpaul
22746492Swpaul	return(0);
22846492Swpaul}
22946492Swpaul
23046492Swpaulstatic void wi_pccard_unload(sc_p)
23146492Swpaul	struct pccard_devinfo	*sc_p;
23246492Swpaul{
23346492Swpaul	struct wi_softc		*sc;
23446492Swpaul	struct ifnet		*ifp;
23546492Swpaul
23646492Swpaul	sc = &wi_softc[sc_p->isahd.id_unit];
23746492Swpaul	ifp = &sc->arpcom.ac_if;
23846492Swpaul
23946492Swpaul	if (sc->wi_gone) {
24046492Swpaul		printf("wi%d: already unloaded\n", sc_p->isahd.id_unit);
24146492Swpaul		return;
24246492Swpaul	}
24346492Swpaul
24446492Swpaul	ifp->if_flags &= ~IFF_RUNNING;
24546492Swpaul	if_down(ifp);
24646492Swpaul	sc->wi_gone = 1;
24746492Swpaul	printf("wi%d: unloaded\n", sc_p->isahd.id_unit);
24846492Swpaul
24946492Swpaul	return;
25046492Swpaul}
25146492Swpaul
25246492Swpaulstatic int wi_pccard_intr(sc_p)
25346492Swpaul	struct pccard_devinfo	*sc_p;
25446492Swpaul{
25546492Swpaul	wi_intr(sc_p->isahd.id_unit);
25646492Swpaul	return(1);
25746492Swpaul}
25846492Swpaul#endif
25946492Swpaul
26046492Swpaulstatic int wi_probe(isa_dev)
26146492Swpaul	struct isa_device	*isa_dev;
26246492Swpaul{
26346492Swpaul	/*
26446492Swpaul	 * The ISA WaveLAN/IEEE card is actually not an ISA card:
26547401Swpaul	 * it's a PCMCIA card plugged into a PCMCIA bridge adapter
26646492Swpaul	 * that fits into an ISA slot. Consequently, we will always
26746492Swpaul	 * be using the pccard support to probe and attach these
26846492Swpaul	 * devices, so we can never actually probe one from here.
26946492Swpaul	 */
27046492Swpaul	return(0);
27146492Swpaul}
27246492Swpaul
27346492Swpaulstatic int wi_attach(isa_dev)
27446492Swpaul	struct isa_device	*isa_dev;
27546492Swpaul{
27646492Swpaul	struct wi_softc		*sc;
27746492Swpaul	struct wi_ltv_macaddr	mac;
27846563Swpaul	struct wi_ltv_gen	gen;
27946492Swpaul	struct ifnet		*ifp;
28046492Swpaul	char			ifname[IFNAMSIZ];
28146492Swpaul
28246492Swpaul#ifdef PCCARD_MODULE
28346492Swpaul	isa_dev->id_ointr = wi_intr;
28446492Swpaul#endif
28546492Swpaul	sc = &wi_softc[isa_dev->id_unit];
28646492Swpaul	ifp = &sc->arpcom.ac_if;
28746492Swpaul
28846492Swpaul	/* Reset the NIC. */
28946492Swpaul	wi_reset(sc);
29046492Swpaul
29146492Swpaul	/* Read the station address. */
29246492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
29346492Swpaul	mac.wi_len = 4;
29446492Swpaul	wi_read_record(sc, (struct wi_ltv_gen *)&mac);
29546492Swpaul	bcopy((char *)&mac.wi_mac_addr,
29646492Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
29746492Swpaul
29846492Swpaul	printf("wi%d: Ethernet address: %6D\n", sc->wi_unit,
29946492Swpaul	    sc->arpcom.ac_enaddr, ":");
30046492Swpaul
30146492Swpaul	ifp->if_softc = sc;
30246492Swpaul	ifp->if_unit = sc->wi_unit;
30346492Swpaul	ifp->if_name = "wi";
30446492Swpaul	ifp->if_mtu = ETHERMTU;
30546492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30646492Swpaul	ifp->if_ioctl = wi_ioctl;
30746492Swpaul	ifp->if_output = ether_output;
30846492Swpaul	ifp->if_start = wi_start;
30946492Swpaul	ifp->if_watchdog = wi_watchdog;
31046492Swpaul	ifp->if_init = wi_init;
31146492Swpaul	ifp->if_baudrate = 10000000;
31246492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
31346492Swpaul
31446492Swpaul	bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
31546492Swpaul	bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name,
31646492Swpaul	    sizeof(WI_DEFAULT_NODENAME) - 1);
31746492Swpaul
31846492Swpaul	bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
31946492Swpaul	bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name,
32046492Swpaul	    sizeof(WI_DEFAULT_NETNAME) - 1);
32146492Swpaul
32246492Swpaul	bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
32346492Swpaul	bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name,
32446492Swpaul	    sizeof(WI_DEFAULT_IBSS) - 1);
32546492Swpaul
32646492Swpaul	sc->wi_portnum = WI_DEFAULT_PORT;
32746492Swpaul	sc->wi_ptype = WI_PORTTYPE_ADHOC;
32846492Swpaul	sc->wi_ap_density = WI_DEFAULT_AP_DENSITY;
32946492Swpaul	sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH;
33046492Swpaul	sc->wi_tx_rate = WI_DEFAULT_TX_RATE;
33146492Swpaul	sc->wi_max_data_len = WI_DEFAULT_DATALEN;
33246492Swpaul	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
33346611Swpaul	sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED;
33446611Swpaul	sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP;
33546492Swpaul
33646563Swpaul	/*
33746563Swpaul	 * Read the default channel from the NIC. This may vary
33846563Swpaul	 * depending on the country where the NIC was purchased, so
33946563Swpaul	 * we can't hard-code a default and expect it to work for
34046563Swpaul	 * everyone.
34146563Swpaul	 */
34246563Swpaul	gen.wi_type = WI_RID_OWN_CHNL;
34346563Swpaul	gen.wi_len = 2;
34446563Swpaul	wi_read_record(sc, &gen);
34546563Swpaul	sc->wi_channel = gen.wi_val;
34646563Swpaul
34746492Swpaul	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
34846492Swpaul
34946492Swpaul	wi_init(sc);
35046492Swpaul	wi_stop(sc);
35146492Swpaul
35246492Swpaul	/*
35346492Swpaul	 * If this logical interface has already been attached,
35446492Swpaul	 * don't attach it again or chaos will ensue.
35546492Swpaul	 */
35646492Swpaul	sprintf(ifname, "wi%d", sc->wi_unit);
35746492Swpaul
35846492Swpaul	if (ifunit(ifname) == NULL) {
35946492Swpaul		callout_handle_init(&sc->wi_stat_ch);
36046492Swpaul		/*
36146492Swpaul		 * Call MI attach routines.
36246492Swpaul		 */
36346492Swpaul		if_attach(ifp);
36446492Swpaul		ether_ifattach(ifp);
36546492Swpaul
36646492Swpaul#if NBPFILTER > 0
36746492Swpaul		bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
36846492Swpaul#endif
36946492Swpaul
37046492Swpaul		at_shutdown(wi_shutdown, sc, SHUTDOWN_POST_SYNC);
37146492Swpaul	}
37246492Swpaul
37346492Swpaul	return(0);
37446492Swpaul}
37546492Swpaul
37646492Swpaulstatic void wi_rxeof(sc)
37746492Swpaul	struct wi_softc		*sc;
37846492Swpaul{
37946492Swpaul	struct ifnet		*ifp;
38046492Swpaul	struct ether_header	*eh;
38146492Swpaul	struct wi_frame		rx_frame;
38246492Swpaul	struct mbuf		*m;
38346492Swpaul	int			id;
38446492Swpaul
38546492Swpaul	ifp = &sc->arpcom.ac_if;
38646492Swpaul
38746492Swpaul	id = CSR_READ_2(sc, WI_RX_FID);
38846492Swpaul
38946492Swpaul	/* First read in the frame header */
39046492Swpaul	if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) {
39146492Swpaul		ifp->if_ierrors++;
39246492Swpaul		return;
39346492Swpaul	}
39446492Swpaul
39546492Swpaul	if (rx_frame.wi_status & WI_STAT_ERRSTAT) {
39646492Swpaul		ifp->if_ierrors++;
39746492Swpaul		return;
39846492Swpaul	}
39946492Swpaul
40046492Swpaul	MGETHDR(m, M_DONTWAIT, MT_DATA);
40146492Swpaul	if (m == NULL) {
40246492Swpaul		ifp->if_ierrors++;
40346492Swpaul		return;
40446492Swpaul	}
40546492Swpaul	MCLGET(m, M_DONTWAIT);
40646492Swpaul	if (!(m->m_flags & M_EXT)) {
40746492Swpaul		m_freem(m);
40846492Swpaul		ifp->if_ierrors++;
40946492Swpaul		return;
41046492Swpaul	}
41146492Swpaul
41246492Swpaul	eh = mtod(m, struct ether_header *);
41346492Swpaul	m->m_pkthdr.rcvif = ifp;
41446492Swpaul
41546492Swpaul	if (rx_frame.wi_status == WI_STAT_1042 ||
41646492Swpaul	    rx_frame.wi_status == WI_STAT_TUNNEL ||
41746492Swpaul	    rx_frame.wi_status == WI_STAT_WMP_MSG) {
41846492Swpaul		m->m_pkthdr.len = m->m_len =
41946492Swpaul		    rx_frame.wi_dat_len + WI_SNAPHDR_LEN;
42046492Swpaul
42146492Swpaul		bcopy((char *)&rx_frame.wi_addr1,
42246492Swpaul		    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
42346492Swpaul		bcopy((char *)&rx_frame.wi_addr2,
42446492Swpaul		    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
42546492Swpaul		bcopy((char *)&rx_frame.wi_type,
42646492Swpaul		    (char *)&eh->ether_type, sizeof(u_int16_t));
42746492Swpaul
42846492Swpaul		if (wi_read_data(sc, id, WI_802_11_OFFSET,
42946492Swpaul		    mtod(m, caddr_t) + sizeof(struct ether_header),
43046492Swpaul		    m->m_len + 2)) {
43146492Swpaul			m_freem(m);
43246492Swpaul			ifp->if_ierrors++;
43346492Swpaul			return;
43446492Swpaul		}
43546492Swpaul	} else {
43646492Swpaul		m->m_pkthdr.len = m->m_len =
43746492Swpaul		    rx_frame.wi_dat_len + sizeof(struct ether_header);
43846492Swpaul
43946492Swpaul		if (wi_read_data(sc, id, WI_802_3_OFFSET,
44046492Swpaul		    mtod(m, caddr_t), m->m_len + 2)) {
44146492Swpaul			m_freem(m);
44246492Swpaul			ifp->if_ierrors++;
44346492Swpaul			return;
44446492Swpaul		}
44546492Swpaul	}
44646492Swpaul
44746492Swpaul	ifp->if_ipackets++;
44846492Swpaul
44946492Swpaul#if NBPFILTER > 0
45046492Swpaul	/* Handle BPF listeners. */
45146492Swpaul	if (ifp->if_bpf) {
45246492Swpaul		bpf_mtap(ifp, m);
45346492Swpaul		if (ifp->if_flags & IFF_PROMISC &&
45446492Swpaul		    (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
45546492Swpaul		    ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) {
45646492Swpaul			m_freem(m);
45746492Swpaul			return;
45846492Swpaul		}
45946492Swpaul	}
46046492Swpaul#endif
46146492Swpaul
46246492Swpaul	/* Receive packet. */
46346492Swpaul	m_adj(m, sizeof(struct ether_header));
46446492Swpaul	ether_input(ifp, eh, m);
46546492Swpaul
46646492Swpaul	return;
46746492Swpaul}
46846492Swpaul
46946492Swpaulstatic void wi_txeof(sc, status)
47046492Swpaul	struct wi_softc		*sc;
47146492Swpaul	int			status;
47246492Swpaul{
47346492Swpaul	struct ifnet		*ifp;
47446492Swpaul
47546492Swpaul	ifp = &sc->arpcom.ac_if;
47646492Swpaul
47746492Swpaul	ifp->if_timer = 0;
47846492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
47946492Swpaul
48046492Swpaul	if (status & WI_EV_TX_EXC)
48146492Swpaul		ifp->if_oerrors++;
48246492Swpaul	else
48346492Swpaul		ifp->if_opackets++;
48446492Swpaul
48546492Swpaul	return;
48646492Swpaul}
48746492Swpaul
48846492Swpaulvoid wi_inquire(xsc)
48946492Swpaul	void			*xsc;
49046492Swpaul{
49146492Swpaul	struct wi_softc		*sc;
49246492Swpaul	struct ifnet		*ifp;
49346492Swpaul
49446492Swpaul	sc = xsc;
49546492Swpaul	ifp = &sc->arpcom.ac_if;
49646492Swpaul
49746492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
49846492Swpaul
49946492Swpaul	/* Don't do this while we're transmitting */
50046492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
50146492Swpaul		return;
50246492Swpaul
50346492Swpaul	wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS);
50446492Swpaul
50546492Swpaul	return;
50646492Swpaul}
50746492Swpaul
50846492Swpaulvoid wi_update_stats(sc)
50946492Swpaul	struct wi_softc		*sc;
51046492Swpaul{
51146492Swpaul	struct wi_ltv_gen	gen;
51246492Swpaul	u_int16_t		id;
51346492Swpaul	struct ifnet		*ifp;
51446492Swpaul	u_int32_t		*ptr;
51546492Swpaul	int			i;
51646492Swpaul	u_int16_t		t;
51746492Swpaul
51846492Swpaul	ifp = &sc->arpcom.ac_if;
51946492Swpaul
52046492Swpaul	id = CSR_READ_2(sc, WI_INFO_FID);
52146492Swpaul
52246492Swpaul	wi_read_data(sc, id, 0, (char *)&gen, 4);
52346492Swpaul
52446492Swpaul	if (gen.wi_type != WI_INFO_COUNTERS ||
52546492Swpaul	    gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1)
52646492Swpaul		return;
52746492Swpaul
52846492Swpaul	ptr = (u_int32_t *)&sc->wi_stats;
52946492Swpaul
53046492Swpaul	for (i = 0; i < gen.wi_len - 1; i++) {
53146492Swpaul		t = CSR_READ_2(sc, WI_DATA1);
53246492Swpaul#ifdef WI_HERMES_STATS_WAR
53346492Swpaul		if (t > 0xF000)
53446492Swpaul			t = ~t & 0xFFFF;
53546492Swpaul#endif
53646492Swpaul		ptr[i] += t;
53746492Swpaul	}
53846492Swpaul
53946492Swpaul	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
54046492Swpaul	    sc->wi_stats.wi_tx_multi_retries +
54146492Swpaul	    sc->wi_stats.wi_tx_retry_limit;
54246492Swpaul
54346492Swpaul	return;
54446492Swpaul}
54546492Swpaul
54646492Swpaulvoid wi_intr(unit)
54746492Swpaul	int			unit;
54846492Swpaul{
54946492Swpaul	struct wi_softc		*sc;
55046492Swpaul	struct ifnet		*ifp;
55146492Swpaul	u_int16_t		status;
55246492Swpaul
55346492Swpaul	sc = &wi_softc[unit];
55446492Swpaul	ifp = &sc->arpcom.ac_if;
55546492Swpaul
55646492Swpaul	if (!(ifp->if_flags & IFF_UP)) {
55746492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
55846492Swpaul		CSR_WRITE_2(sc, WI_INT_EN, 0);
55946492Swpaul		return;
56046492Swpaul	}
56146492Swpaul
56246492Swpaul	/* Disable interrupts. */
56346492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
56446492Swpaul
56546492Swpaul	status = CSR_READ_2(sc, WI_EVENT_STAT);
56646492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS);
56746492Swpaul
56846492Swpaul	if (status & WI_EV_RX) {
56946492Swpaul		wi_rxeof(sc);
57046492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
57146492Swpaul	}
57246492Swpaul
57346492Swpaul	if (status & WI_EV_TX) {
57446492Swpaul		wi_txeof(sc, status);
57546492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX);
57646492Swpaul	}
57746492Swpaul
57846492Swpaul	if (status & WI_EV_ALLOC) {
57946492Swpaul		int			id;
58046492Swpaul		id = CSR_READ_2(sc, WI_ALLOC_FID);
58146492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
58246492Swpaul		if (id == sc->wi_tx_data_id)
58346492Swpaul			wi_txeof(sc, status);
58446492Swpaul	}
58546492Swpaul
58646492Swpaul	if (status & WI_EV_INFO) {
58746492Swpaul		wi_update_stats(sc);
58846492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
58946492Swpaul	}
59046492Swpaul
59146492Swpaul	if (status & WI_EV_TX_EXC) {
59246492Swpaul		wi_txeof(sc, status);
59346492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
59446492Swpaul	}
59546492Swpaul
59646492Swpaul	if (status & WI_EV_INFO_DROP) {
59746492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP);
59846492Swpaul	}
59946492Swpaul
60046492Swpaul	/* Re-enable interrupts. */
60146492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
60246492Swpaul
60346492Swpaul	if (ifp->if_snd.ifq_head != NULL)
60446492Swpaul		wi_start(ifp);
60546492Swpaul
60646492Swpaul	return;
60746492Swpaul}
60846492Swpaul
60946492Swpaulstatic int wi_cmd(sc, cmd, val)
61046492Swpaul	struct wi_softc		*sc;
61146492Swpaul	int			cmd;
61246492Swpaul	int			val;
61346492Swpaul{
61446492Swpaul	int			i, s = 0;
61546492Swpaul
61646492Swpaul	CSR_WRITE_2(sc, WI_PARAM0, val);
61746492Swpaul	CSR_WRITE_2(sc, WI_COMMAND, cmd);
61846492Swpaul
61946492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
62046492Swpaul		/*
62146492Swpaul		 * Wait for 'command complete' bit to be
62246492Swpaul		 * set in the event status register.
62346492Swpaul		 */
62446492Swpaul		s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD;
62546492Swpaul		if (s) {
62646492Swpaul			/* Ack the event and read result code. */
62746492Swpaul			s = CSR_READ_2(sc, WI_STATUS);
62846492Swpaul			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
62946492Swpaul#ifdef foo
63046492Swpaul			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
63146492Swpaul				return(EIO);
63246492Swpaul#endif
63346492Swpaul			if (s & WI_STAT_CMD_RESULT)
63446492Swpaul				return(EIO);
63546492Swpaul			break;
63646492Swpaul		}
63746492Swpaul	}
63846492Swpaul
63946492Swpaul	if (i == WI_TIMEOUT)
64046492Swpaul		return(ETIMEDOUT);
64146492Swpaul
64246492Swpaul	return(0);
64346492Swpaul}
64446492Swpaul
64546492Swpaulstatic void wi_reset(sc)
64646492Swpaul	struct wi_softc		*sc;
64746492Swpaul{
64846492Swpaul	if (wi_cmd(sc, WI_CMD_INI, 0))
64946492Swpaul		printf("wi%d: init failed\n", sc->wi_unit);
65046492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
65146492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
65246492Swpaul
65346492Swpaul	/* Calibrate timer. */
65446492Swpaul	WI_SETVAL(WI_RID_TICK_TIME, 8);
65546492Swpaul
65646492Swpaul	return;
65746492Swpaul}
65846492Swpaul
65946492Swpaul/*
66046492Swpaul * Read an LTV record from the NIC.
66146492Swpaul */
66246492Swpaulstatic int wi_read_record(sc, ltv)
66346492Swpaul	struct wi_softc		*sc;
66446492Swpaul	struct wi_ltv_gen	*ltv;
66546492Swpaul{
66646492Swpaul	u_int16_t		*ptr;
66746492Swpaul	int			i, len, code;
66846492Swpaul
66946492Swpaul	/* Tell the NIC to enter record read mode. */
67046492Swpaul	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type))
67146492Swpaul		return(EIO);
67246492Swpaul
67346492Swpaul	/* Select the record we want to read. */
67446492Swpaul	CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type);
67546492Swpaul
67646492Swpaul	/* Specify offset -- we always read the whole record. */
67746492Swpaul	CSR_WRITE_2(sc, WI_OFF1, 0);
67846492Swpaul
67946492Swpaul	/* Wait for NIC to acknowledge */
68046492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
68146492Swpaul		if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR)))
68246492Swpaul			break;
68346492Swpaul	}
68446492Swpaul
68546492Swpaul	if (i == WI_TIMEOUT)
68646492Swpaul		return(ETIMEDOUT);
68746492Swpaul
68846492Swpaul	/*
68946492Swpaul	 * Read the length and record type and make sure they
69046492Swpaul	 * match what we expect (this verifies that we have enough
69147401Swpaul	 * room to hold all of the returned data).
69246492Swpaul	 */
69346492Swpaul	len = CSR_READ_2(sc, WI_DATA1);
69446492Swpaul	if (len > ltv->wi_len)
69546492Swpaul		return(ENOSPC);
69646492Swpaul	code = CSR_READ_2(sc, WI_DATA1);
69746492Swpaul	if (code != ltv->wi_type)
69846492Swpaul		return(EIO);
69946492Swpaul
70046492Swpaul	ltv->wi_len = len;
70146492Swpaul	ltv->wi_type = code;
70246492Swpaul
70346492Swpaul	/* Now read the data. */
70446492Swpaul	ptr = &ltv->wi_val;
70546492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
70646492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
70746492Swpaul
70846492Swpaul	return(0);
70946492Swpaul}
71046492Swpaul
71146492Swpaul/*
71246492Swpaul * Same as read, except we inject data instead of reading it.
71346492Swpaul */
71446492Swpaulstatic int wi_write_record(sc, ltv)
71546492Swpaul	struct wi_softc		*sc;
71646492Swpaul	struct wi_ltv_gen	*ltv;
71746492Swpaul{
71846492Swpaul	u_int16_t		*ptr;
71946492Swpaul	int			i;
72046492Swpaul
72146492Swpaul	CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type);
72246492Swpaul	CSR_WRITE_2(sc, WI_OFF1, 0);
72346492Swpaul
72446492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
72546492Swpaul		if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR)))
72646492Swpaul			break;
72746492Swpaul	}
72846492Swpaul
72946492Swpaul	if (i == WI_TIMEOUT)
73046492Swpaul		return(ETIMEDOUT);
73146492Swpaul
73246492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len);
73346492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type);
73446492Swpaul
73546492Swpaul	ptr = &ltv->wi_val;
73646492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
73746492Swpaul		CSR_WRITE_2(sc, WI_DATA1, ptr[i]);
73846492Swpaul
73946492Swpaul	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type))
74046492Swpaul		return(EIO);
74146492Swpaul
74246492Swpaul	return(0);
74346492Swpaul}
74446492Swpaul
74546492Swpaulstatic int wi_seek(sc, id, off, chan)
74646492Swpaul	struct wi_softc		*sc;
74746492Swpaul	int			id, off, chan;
74846492Swpaul{
74946492Swpaul	int			i;
75046492Swpaul	int			selreg, offreg;
75146492Swpaul
75246492Swpaul	switch (chan) {
75346492Swpaul	case WI_BAP0:
75446492Swpaul		selreg = WI_SEL0;
75546492Swpaul		offreg = WI_OFF0;
75646492Swpaul		break;
75746492Swpaul	case WI_BAP1:
75846492Swpaul		selreg = WI_SEL1;
75946492Swpaul		offreg = WI_OFF1;
76046492Swpaul		break;
76146492Swpaul	default:
76246492Swpaul		printf("wi%d: invalid data path: %x\n", sc->wi_unit, chan);
76346492Swpaul		return(EIO);
76446492Swpaul	}
76546492Swpaul
76646492Swpaul	CSR_WRITE_2(sc, selreg, id);
76746492Swpaul	CSR_WRITE_2(sc, offreg, off);
76846492Swpaul
76946492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
77046492Swpaul		if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR)))
77146492Swpaul			break;
77246492Swpaul	}
77346492Swpaul
77446492Swpaul	if (i == WI_TIMEOUT)
77546492Swpaul		return(ETIMEDOUT);
77646492Swpaul
77746492Swpaul	return(0);
77846492Swpaul}
77946492Swpaul
78046492Swpaulstatic int wi_read_data(sc, id, off, buf, len)
78146492Swpaul	struct wi_softc		*sc;
78246492Swpaul	int			id, off;
78346492Swpaul	caddr_t			buf;
78446492Swpaul	int			len;
78546492Swpaul{
78646492Swpaul	int			i;
78746492Swpaul	u_int16_t		*ptr;
78846492Swpaul
78946492Swpaul	if (wi_seek(sc, id, off, WI_BAP1))
79046492Swpaul		return(EIO);
79146492Swpaul
79246492Swpaul	ptr = (u_int16_t *)buf;
79346492Swpaul	for (i = 0; i < len / 2; i++)
79446492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
79546492Swpaul
79646492Swpaul	return(0);
79746492Swpaul}
79846492Swpaul
79946492Swpaul/*
80046492Swpaul * According to the comments in the HCF Light code, there is a bug in
80146492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where
80246492Swpaul * the chip's internal autoincrement counter gets thrown off during
80346492Swpaul * data writes: the autoincrement is missed, causing one data word to
80446492Swpaul * be overwritten and subsequent words to be written to the wrong memory
80546492Swpaul * locations. The end result is that we could end up transmitting bogus
80646492Swpaul * frames without realizing it. The workaround for this is to write a
80746492Swpaul * couple of extra guard words after the end of the transfer, then
80846492Swpaul * attempt to read then back. If we fail to locate the guard words where
80946492Swpaul * we expect them, we preform the transfer over again.
81046492Swpaul */
81146492Swpaulstatic int wi_write_data(sc, id, off, buf, len)
81246492Swpaul	struct wi_softc		*sc;
81346492Swpaul	int			id, off;
81446492Swpaul	caddr_t			buf;
81546492Swpaul	int			len;
81646492Swpaul{
81746492Swpaul	int			i;
81846492Swpaul	u_int16_t		*ptr;
81946492Swpaul
82046492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
82146492Swpaulagain:
82246492Swpaul#endif
82346492Swpaul
82446492Swpaul	if (wi_seek(sc, id, off, WI_BAP0))
82546492Swpaul		return(EIO);
82646492Swpaul
82746492Swpaul	ptr = (u_int16_t *)buf;
82846492Swpaul	for (i = 0; i < (len / 2); i++)
82946492Swpaul		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
83046492Swpaul
83146492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
83246492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x1234);
83346492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x5678);
83446492Swpaul
83546492Swpaul	if (wi_seek(sc, id, off + len, WI_BAP0))
83646492Swpaul		return(EIO);
83746492Swpaul
83846492Swpaul	if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
83946492Swpaul	    CSR_READ_2(sc, WI_DATA0) != 0x5678)
84046492Swpaul		goto again;
84146492Swpaul#endif
84246492Swpaul
84346492Swpaul	return(0);
84446492Swpaul}
84546492Swpaul
84646492Swpaul/*
84746492Swpaul * Allocate a region of memory inside the NIC and zero
84846492Swpaul * it out.
84946492Swpaul */
85046492Swpaulstatic int wi_alloc_nicmem(sc, len, id)
85146492Swpaul	struct wi_softc		*sc;
85246492Swpaul	int			len;
85346492Swpaul	int			*id;
85446492Swpaul{
85546492Swpaul	int			i;
85646492Swpaul
85746492Swpaul	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) {
85846492Swpaul		printf("wi%d: failed to allocate %d bytes on NIC\n",
85946492Swpaul		    sc->wi_unit, len);
86046492Swpaul		return(ENOMEM);
86146492Swpaul	}
86246492Swpaul
86346492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
86446492Swpaul		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
86546492Swpaul			break;
86646492Swpaul	}
86746492Swpaul
86846492Swpaul	if (i == WI_TIMEOUT)
86946492Swpaul		return(ETIMEDOUT);
87046492Swpaul
87146492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
87246492Swpaul	*id = CSR_READ_2(sc, WI_ALLOC_FID);
87346492Swpaul
87446492Swpaul	wi_seek(sc, *id, 0, WI_BAP0);
87546492Swpaul
87646492Swpaul	for (i = 0; i < len / 2; i++)
87746492Swpaul		CSR_WRITE_2(sc, WI_DATA0, 0);
87846492Swpaul
87946492Swpaul	return(0);
88046492Swpaul}
88146492Swpaul
88246492Swpaulstatic void wi_setmulti(sc)
88346492Swpaul	struct wi_softc		*sc;
88446492Swpaul{
88546492Swpaul	struct ifnet		*ifp;
88646492Swpaul	int			i = 0;
88746492Swpaul	struct ifmultiaddr	*ifma;
88846492Swpaul	struct wi_ltv_mcast	mcast;
88946492Swpaul
89046492Swpaul	ifp = &sc->arpcom.ac_if;
89146492Swpaul
89246492Swpaul	bzero((char *)&mcast, sizeof(mcast));
89346492Swpaul
89446492Swpaul	mcast.wi_type = WI_RID_MCAST;
89546492Swpaul	mcast.wi_len = (3 * 16) + 1;
89646492Swpaul
89746492Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
89846492Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
89946492Swpaul		return;
90046492Swpaul	}
90146492Swpaul
90246492Swpaul	for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
90346492Swpaul				ifma = ifma->ifma_link.le_next) {
90446492Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
90546492Swpaul			continue;
90646492Swpaul		if (i < 16) {
90746492Swpaul			bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
90846492Swpaul			    (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN);
90946492Swpaul			i++;
91046492Swpaul		} else {
91146492Swpaul			bzero((char *)&mcast, sizeof(mcast));
91246492Swpaul			break;
91346492Swpaul		}
91446492Swpaul	}
91546492Swpaul
91646492Swpaul	mcast.wi_len = (i * 3) + 1;
91746492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
91846492Swpaul
91946492Swpaul	return;
92046492Swpaul}
92146492Swpaul
92246492Swpaulstatic void wi_setdef(sc, wreq)
92346492Swpaul	struct wi_softc		*sc;
92446492Swpaul	struct wi_req		*wreq;
92546492Swpaul{
92646492Swpaul	struct sockaddr_dl	*sdl;
92746492Swpaul	struct ifaddr		*ifa;
92846492Swpaul	struct ifnet		*ifp;
92946492Swpaul
93046492Swpaul	ifp = &sc->arpcom.ac_if;
93146492Swpaul
93246492Swpaul	switch(wreq->wi_type) {
93346492Swpaul	case WI_RID_MAC_NODE:
93446492Swpaul		ifa = ifnet_addrs[ifp->if_index - 1];
93546492Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
93646492Swpaul		bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr,
93746492Swpaul		   ETHER_ADDR_LEN);
93846492Swpaul		bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN);
93946492Swpaul		break;
94046492Swpaul	case WI_RID_PORTTYPE:
94146492Swpaul		sc->wi_ptype = wreq->wi_val[0];
94246492Swpaul		break;
94346492Swpaul	case WI_RID_TX_RATE:
94446492Swpaul		sc->wi_tx_rate = wreq->wi_val[0];
94546492Swpaul		break;
94646492Swpaul	case WI_RID_MAX_DATALEN:
94746492Swpaul		sc->wi_max_data_len = wreq->wi_val[0];
94846492Swpaul		break;
94946492Swpaul	case WI_RID_RTS_THRESH:
95046492Swpaul		sc->wi_rts_thresh = wreq->wi_val[0];
95146492Swpaul		break;
95246492Swpaul	case WI_RID_SYSTEM_SCALE:
95346492Swpaul		sc->wi_ap_density = wreq->wi_val[0];
95446492Swpaul		break;
95546492Swpaul	case WI_RID_CREATE_IBSS:
95646492Swpaul		sc->wi_create_ibss = wreq->wi_val[0];
95746492Swpaul		break;
95846563Swpaul	case WI_RID_OWN_CHNL:
95946563Swpaul		sc->wi_channel = wreq->wi_val[0];
96046563Swpaul		break;
96146492Swpaul	case WI_RID_NODENAME:
96246492Swpaul		bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
96346492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30);
96446492Swpaul		break;
96546492Swpaul	case WI_RID_DESIRED_SSID:
96646492Swpaul		bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
96746492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30);
96846492Swpaul		break;
96946492Swpaul	case WI_RID_OWN_SSID:
97046492Swpaul		bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
97146492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30);
97246492Swpaul		break;
97346611Swpaul	case WI_RID_PM_ENABLED:
97446611Swpaul		sc->wi_pm_enabled = wreq->wi_val[0];
97546611Swpaul		break;
97646611Swpaul	case WI_RID_MAX_SLEEP:
97746611Swpaul		sc->wi_max_sleep = wreq->wi_val[0];
97846611Swpaul		break;
97946492Swpaul	default:
98046492Swpaul		break;
98146492Swpaul	}
98246492Swpaul
98346563Swpaul	/* Reinitialize WaveLAN. */
98446563Swpaul	wi_init(sc);
98546563Swpaul
98646492Swpaul	return;
98746492Swpaul}
98846492Swpaul
98946492Swpaulstatic int wi_ioctl(ifp, command, data)
99046492Swpaul	struct ifnet		*ifp;
99146492Swpaul	u_long			command;
99246492Swpaul	caddr_t			data;
99346492Swpaul{
99446492Swpaul	int			s, error = 0;
99546492Swpaul	struct wi_softc		*sc;
99646492Swpaul	struct wi_req		wreq;
99746492Swpaul	struct ifreq		*ifr;
99846492Swpaul
99946492Swpaul	s = splimp();
100046492Swpaul
100146492Swpaul	sc = ifp->if_softc;
100246492Swpaul	ifr = (struct ifreq *)data;
100346492Swpaul
100446492Swpaul	if (sc->wi_gone)
100546492Swpaul		return(ENODEV);
100646492Swpaul
100746492Swpaul	switch(command) {
100846492Swpaul	case SIOCSIFADDR:
100946492Swpaul	case SIOCGIFADDR:
101046492Swpaul	case SIOCSIFMTU:
101146492Swpaul		error = ether_ioctl(ifp, command, data);
101246492Swpaul		break;
101346492Swpaul	case SIOCSIFFLAGS:
101446492Swpaul		if (ifp->if_flags & IFF_UP) {
101546492Swpaul			if (ifp->if_flags & IFF_RUNNING &&
101646492Swpaul			    ifp->if_flags & IFF_PROMISC &&
101746492Swpaul			    !(sc->wi_if_flags & IFF_PROMISC)) {
101846492Swpaul				WI_SETVAL(WI_RID_PROMISC, 1);
101946492Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
102046492Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
102146492Swpaul			    sc->wi_if_flags & IFF_PROMISC) {
102246492Swpaul				WI_SETVAL(WI_RID_PROMISC, 0);
102346492Swpaul			} else
102446492Swpaul				wi_init(sc);
102546492Swpaul		} else {
102646492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
102746492Swpaul				wi_stop(sc);
102846492Swpaul			}
102946492Swpaul		}
103046492Swpaul		sc->wi_if_flags = ifp->if_flags;
103146492Swpaul		error = 0;
103246492Swpaul		break;
103346492Swpaul	case SIOCADDMULTI:
103446492Swpaul	case SIOCDELMULTI:
103546492Swpaul		wi_setmulti(sc);
103646492Swpaul		error = 0;
103746492Swpaul		break;
103846492Swpaul	case SIOCGWAVELAN:
103946492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
104046492Swpaul		if (error)
104146492Swpaul			break;
104246492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
104346492Swpaul			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
104446492Swpaul			    sizeof(sc->wi_stats));
104546492Swpaul			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
104646492Swpaul		} else {
104746492Swpaul			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
104846492Swpaul				error = EINVAL;
104946492Swpaul				break;
105046492Swpaul			}
105146492Swpaul		}
105246492Swpaul		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
105346492Swpaul		break;
105446492Swpaul	case SIOCSWAVELAN:
105546492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
105646492Swpaul		if (error)
105746492Swpaul			break;
105846492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
105946492Swpaul			error = EINVAL;
106046492Swpaul			break;
106146492Swpaul		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
106246492Swpaul			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
106346492Swpaul			    wreq.wi_len);
106446492Swpaul		} else {
106546492Swpaul			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
106646492Swpaul			if (!error)
106746492Swpaul				wi_setdef(sc, &wreq);
106846492Swpaul		}
106946492Swpaul		break;
107046492Swpaul	default:
107146492Swpaul		error = EINVAL;
107246492Swpaul		break;
107346492Swpaul	}
107446492Swpaul
107546492Swpaul	splx(s);
107646492Swpaul
107746492Swpaul	return(error);
107846492Swpaul}
107946492Swpaul
108046492Swpaulstatic void wi_init(xsc)
108146492Swpaul	void			*xsc;
108246492Swpaul{
108346492Swpaul	struct wi_softc		*sc = xsc;
108446492Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
108546492Swpaul	int			s;
108646492Swpaul	struct wi_ltv_macaddr	mac;
108746492Swpaul	int			id = 0;
108846492Swpaul
108946492Swpaul	if (sc->wi_gone)
109046492Swpaul		return;
109146492Swpaul
109246492Swpaul	s = splimp();
109346492Swpaul
109446492Swpaul	if (ifp->if_flags & IFF_RUNNING)
109546492Swpaul		wi_stop(sc);
109646492Swpaul
109746492Swpaul	wi_reset(sc);
109846492Swpaul
109946492Swpaul	/* Program max data length. */
110046492Swpaul	WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len);
110146492Swpaul
110247401Swpaul	/* Enable/disable IBSS creation. */
110346492Swpaul	WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss);
110446492Swpaul
110546492Swpaul	/* Set the port type. */
110646492Swpaul	WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype);
110746492Swpaul
110846492Swpaul	/* Program the RTS/CTS threshold. */
110946492Swpaul	WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh);
111046492Swpaul
111146492Swpaul	/* Program the TX rate */
111246492Swpaul	WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate);
111346492Swpaul
111446492Swpaul	/* Access point density */
111546492Swpaul	WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density);
111646492Swpaul
111746611Swpaul	/* Power Management Enabled */
111846611Swpaul	WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled);
111946611Swpaul
112046611Swpaul	/* Power Managment Max Sleep */
112146611Swpaul	WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep);
112246611Swpaul
112346492Swpaul	/* Specify the IBSS name */
112446492Swpaul	WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name);
112546492Swpaul
112646492Swpaul	/* Specify the network name */
112746492Swpaul	WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name);
112846492Swpaul
112946563Swpaul	/* Specify the frequency to use */
113046563Swpaul	WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel);
113146563Swpaul
113246492Swpaul	/* Program the nodename. */
113346492Swpaul	WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name);
113446492Swpaul
113546492Swpaul	/* Set our MAC address. */
113646492Swpaul	mac.wi_len = 4;
113746492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
113846492Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
113946492Swpaul	   (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN);
114046492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mac);
114146492Swpaul
114246492Swpaul	/* Initialize promisc mode. */
114346492Swpaul	if (ifp->if_flags & IFF_PROMISC) {
114446492Swpaul		WI_SETVAL(WI_RID_PROMISC, 1);
114546492Swpaul	} else {
114646492Swpaul		WI_SETVAL(WI_RID_PROMISC, 0);
114746492Swpaul	}
114846492Swpaul
114946492Swpaul	/* Set multicast filter. */
115046492Swpaul	wi_setmulti(sc);
115146492Swpaul
115246492Swpaul	/* Enable desired port */
115346492Swpaul	wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0);
115446492Swpaul
115546492Swpaul	if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id))
115646495Swpaul		printf("wi%d: tx buffer allocation failed\n", sc->wi_unit);
115746492Swpaul	sc->wi_tx_data_id = id;
115846492Swpaul
115946492Swpaul	if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id))
116046495Swpaul		printf("wi%d: mgmt. buffer allocation failed\n", sc->wi_unit);
116146492Swpaul	sc->wi_tx_mgmt_id = id;
116246492Swpaul
116346492Swpaul	/* enable interrupts */
116446492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
116546492Swpaul
116646492Swpaul	splx(s);
116746492Swpaul
116846492Swpaul	ifp->if_flags |= IFF_RUNNING;
116946492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
117046492Swpaul
117146492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
117246492Swpaul
117346492Swpaul	return;
117446492Swpaul}
117546492Swpaul
117646492Swpaulstatic void wi_start(ifp)
117746492Swpaul	struct ifnet		*ifp;
117846492Swpaul{
117946492Swpaul	struct wi_softc		*sc;
118046492Swpaul	struct mbuf		*m0;
118146492Swpaul	struct wi_frame		tx_frame;
118246492Swpaul	struct ether_header	*eh;
118346492Swpaul	int			id;
118446492Swpaul
118546492Swpaul	sc = ifp->if_softc;
118646492Swpaul
118746492Swpaul	if (sc->wi_gone)
118846492Swpaul		return;
118946492Swpaul
119046492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
119146492Swpaul		return;
119246492Swpaul
119346492Swpaul	IF_DEQUEUE(&ifp->if_snd, m0);
119446492Swpaul	if (m0 == NULL)
119546492Swpaul		return;
119646492Swpaul
119746492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
119846492Swpaul	id = sc->wi_tx_data_id;
119946492Swpaul	eh = mtod(m0, struct ether_header *);
120046492Swpaul
120146492Swpaul	/*
120247401Swpaul	 * Use RFC1042 encoding for IP and ARP datagrams,
120346492Swpaul	 * 802.3 for anything else.
120446492Swpaul	 */
120546492Swpaul	if (ntohs(eh->ether_type) == ETHERTYPE_IP ||
120646492Swpaul	    ntohs(eh->ether_type) == ETHERTYPE_ARP ||
120746492Swpaul	    ntohs(eh->ether_type) == ETHERTYPE_REVARP) {
120846492Swpaul		bcopy((char *)&eh->ether_dhost,
120946492Swpaul		    (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN);
121046492Swpaul		bcopy((char *)&eh->ether_shost,
121146492Swpaul		    (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
121246492Swpaul		bcopy((char *)&eh->ether_dhost,
121346492Swpaul		    (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN);
121446492Swpaul		bcopy((char *)&eh->ether_shost,
121546492Swpaul		    (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN);
121646492Swpaul
121746492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN;
121846492Swpaul		tx_frame.wi_frame_ctl = WI_FTYPE_DATA;
121946492Swpaul		tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0);
122046492Swpaul		tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1);
122146492Swpaul		tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
122246492Swpaul		tx_frame.wi_type = eh->ether_type;
122346492Swpaul
122446492Swpaul		m_copydata(m0, sizeof(struct ether_header),
122546492Swpaul		    m0->m_pkthdr.len - sizeof(struct ether_header),
122646492Swpaul		    (caddr_t)&sc->wi_txbuf);
122746492Swpaul
122846492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
122946492Swpaul		    sizeof(struct wi_frame));
123046492Swpaul		wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf,
123146492Swpaul		    (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2);
123246492Swpaul	} else {
123346492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len;
123446492Swpaul
123546492Swpaul		m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf);
123646492Swpaul
123746492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
123846492Swpaul		    sizeof(struct wi_frame));
123946492Swpaul		wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf,
124046492Swpaul		    m0->m_pkthdr.len + 2);
124146492Swpaul	}
124246492Swpaul
124346492Swpaul#if NBPFILTER > 0
124446492Swpaul	/*
124546492Swpaul	 * If there's a BPF listner, bounce a copy of
124646492Swpaul	 * this frame to him.
124746492Swpaul	 */
124846492Swpaul	if (ifp->if_bpf)
124946492Swpaul		bpf_mtap(ifp, m0);
125046492Swpaul#endif
125146492Swpaul
125246492Swpaul	m_freem(m0);
125346492Swpaul
125446492Swpaul	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id))
125546492Swpaul		printf("wi%d: xmit failed\n", sc->wi_unit);
125646492Swpaul
125746492Swpaul	ifp->if_flags |= IFF_OACTIVE;
125846492Swpaul
125946492Swpaul	/*
126046492Swpaul	 * Set a timeout in case the chip goes out to lunch.
126146492Swpaul	 */
126246492Swpaul	ifp->if_timer = 5;
126346492Swpaul
126446492Swpaul	return;
126546492Swpaul}
126646492Swpaul
126746492Swpaulstatic int wi_mgmt_xmit(sc, data, len)
126846492Swpaul	struct wi_softc		*sc;
126946492Swpaul	caddr_t			data;
127046492Swpaul	int			len;
127146492Swpaul{
127246492Swpaul	struct wi_frame		tx_frame;
127346492Swpaul	int			id;
127446492Swpaul	struct wi_80211_hdr	*hdr;
127546492Swpaul	caddr_t			dptr;
127646492Swpaul
127746492Swpaul	if (sc->wi_gone)
127846492Swpaul		return(ENODEV);
127946492Swpaul
128046492Swpaul	hdr = (struct wi_80211_hdr *)data;
128146492Swpaul	dptr = data + sizeof(struct wi_80211_hdr);
128246492Swpaul
128346492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
128446492Swpaul	id = sc->wi_tx_mgmt_id;
128546492Swpaul
128646492Swpaul	bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl,
128746492Swpaul	   sizeof(struct wi_80211_hdr));
128846492Swpaul
128946492Swpaul	tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN;
129046492Swpaul	tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN);
129146492Swpaul
129246492Swpaul	wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame));
129346492Swpaul	wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr,
129446492Swpaul	    (len - sizeof(struct wi_80211_hdr)) + 2);
129546492Swpaul
129646492Swpaul	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) {
129746492Swpaul		printf("wi%d: xmit failed\n", sc->wi_unit);
129846492Swpaul		return(EIO);
129946492Swpaul	}
130046492Swpaul
130146492Swpaul	return(0);
130246492Swpaul}
130346492Swpaul
130446492Swpaulstatic void wi_stop(sc)
130546492Swpaul	struct wi_softc		*sc;
130646492Swpaul{
130746492Swpaul	struct ifnet		*ifp;
130846492Swpaul
130946492Swpaul	if (sc->wi_gone)
131046492Swpaul		return;
131146492Swpaul
131246492Swpaul	ifp = &sc->arpcom.ac_if;
131346492Swpaul
131446492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
131546492Swpaul	wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0);
131646492Swpaul
131746492Swpaul	untimeout(wi_inquire, sc, sc->wi_stat_ch);
131846492Swpaul
131946492Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
132046492Swpaul
132146492Swpaul	return;
132246492Swpaul}
132346492Swpaul
132446492Swpaulstatic void wi_watchdog(ifp)
132546492Swpaul	struct ifnet		*ifp;
132646492Swpaul{
132746492Swpaul	struct wi_softc		*sc;
132846492Swpaul
132946492Swpaul	sc = ifp->if_softc;
133046492Swpaul
133146492Swpaul	printf("wi%d: device timeout\n", sc->wi_unit);
133246492Swpaul
133346492Swpaul	wi_init(sc);
133446492Swpaul
133546492Swpaul	ifp->if_oerrors++;
133646492Swpaul
133746492Swpaul	return;
133846492Swpaul}
133946492Swpaul
134046492Swpaulstatic void wi_shutdown(howto, arg)
134146492Swpaul	int			howto;
134246492Swpaul	void			*arg;
134346492Swpaul{
134446492Swpaul	struct wi_softc		*sc;
134546492Swpaul
134646492Swpaul	sc = arg;
134746492Swpaul	wi_stop(sc);
134846492Swpaul
134946492Swpaul	return;
135046492Swpaul}
1351