if_wi.c revision 46492
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 *
3246492Swpaul *	$Id: if_wi.c,v 1.48 1999/05/05 00:32:13 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/*
4446492Swpaul * 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
5246492Swpaul * a) extremely gross, b) lacks certain fearures, 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[] =
11946492Swpaul	"$Id: if_wi.c,v 1.48 1999/05/05 00:32:13 wpaul Exp $";
12046492Swpaul#endif
12146492Swpaul
12246492Swpaulstatic struct wi_softc wi_softc[NWI];
12346492Swpaul
12446492Swpaul#ifdef foo
12546492Swpaulstatic u_int8_t	wi_mcast_addr[6] = { 0x00, 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:
26546492Swpaul	 * it's a PCMCIA card plugged into a PCMCIA expander card
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;
27846492Swpaul	struct ifnet		*ifp;
27946492Swpaul	char			ifname[IFNAMSIZ];
28046492Swpaul
28146492Swpaul#ifdef PCCARD_MODULE
28246492Swpaul	isa_dev->id_ointr = wi_intr;
28346492Swpaul#endif
28446492Swpaul	sc = &wi_softc[isa_dev->id_unit];
28546492Swpaul	ifp = &sc->arpcom.ac_if;
28646492Swpaul
28746492Swpaul	/* Reset the NIC. */
28846492Swpaul	wi_reset(sc);
28946492Swpaul
29046492Swpaul	/* Read the station address. */
29146492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
29246492Swpaul	mac.wi_len = 4;
29346492Swpaul	wi_read_record(sc, (struct wi_ltv_gen *)&mac);
29446492Swpaul	bcopy((char *)&mac.wi_mac_addr,
29546492Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
29646492Swpaul
29746492Swpaul	printf("wi%d: Ethernet address: %6D\n", sc->wi_unit,
29846492Swpaul	    sc->arpcom.ac_enaddr, ":");
29946492Swpaul
30046492Swpaul	ifp->if_softc = sc;
30146492Swpaul	ifp->if_unit = sc->wi_unit;
30246492Swpaul	ifp->if_name = "wi";
30346492Swpaul	ifp->if_mtu = ETHERMTU;
30446492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30546492Swpaul	ifp->if_ioctl = wi_ioctl;
30646492Swpaul	ifp->if_output = ether_output;
30746492Swpaul	ifp->if_start = wi_start;
30846492Swpaul	ifp->if_watchdog = wi_watchdog;
30946492Swpaul	ifp->if_init = wi_init;
31046492Swpaul	ifp->if_baudrate = 10000000;
31146492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
31246492Swpaul
31346492Swpaul	bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
31446492Swpaul	bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name,
31546492Swpaul	    sizeof(WI_DEFAULT_NODENAME) - 1);
31646492Swpaul
31746492Swpaul	bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
31846492Swpaul	bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name,
31946492Swpaul	    sizeof(WI_DEFAULT_NETNAME) - 1);
32046492Swpaul
32146492Swpaul	bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
32246492Swpaul	bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name,
32346492Swpaul	    sizeof(WI_DEFAULT_IBSS) - 1);
32446492Swpaul
32546492Swpaul	sc->wi_portnum = WI_DEFAULT_PORT;
32646492Swpaul	sc->wi_ptype = WI_PORTTYPE_ADHOC;
32746492Swpaul	sc->wi_ap_density = WI_DEFAULT_AP_DENSITY;
32846492Swpaul	sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH;
32946492Swpaul	sc->wi_tx_rate = WI_DEFAULT_TX_RATE;
33046492Swpaul	sc->wi_max_data_len = WI_DEFAULT_DATALEN;
33146492Swpaul	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
33246492Swpaul
33346492Swpaul	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
33446492Swpaul
33546492Swpaul	wi_init(sc);
33646492Swpaul	wi_stop(sc);
33746492Swpaul
33846492Swpaul	/*
33946492Swpaul	 * If this logical interface has already been attached,
34046492Swpaul	 * don't attach it again or chaos will ensue.
34146492Swpaul	 */
34246492Swpaul	sprintf(ifname, "wi%d", sc->wi_unit);
34346492Swpaul
34446492Swpaul	if (ifunit(ifname) == NULL) {
34546492Swpaul		callout_handle_init(&sc->wi_stat_ch);
34646492Swpaul		/*
34746492Swpaul		 * Call MI attach routines.
34846492Swpaul		 */
34946492Swpaul		if_attach(ifp);
35046492Swpaul		ether_ifattach(ifp);
35146492Swpaul
35246492Swpaul#if NBPFILTER > 0
35346492Swpaul		bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
35446492Swpaul#endif
35546492Swpaul
35646492Swpaul		at_shutdown(wi_shutdown, sc, SHUTDOWN_POST_SYNC);
35746492Swpaul	}
35846492Swpaul
35946492Swpaul	return(0);
36046492Swpaul}
36146492Swpaul
36246492Swpaulstatic void wi_rxeof(sc)
36346492Swpaul	struct wi_softc		*sc;
36446492Swpaul{
36546492Swpaul	struct ifnet		*ifp;
36646492Swpaul	struct ether_header	*eh;
36746492Swpaul	struct wi_frame		rx_frame;
36846492Swpaul	struct mbuf		*m;
36946492Swpaul	int			id;
37046492Swpaul
37146492Swpaul	ifp = &sc->arpcom.ac_if;
37246492Swpaul
37346492Swpaul	id = CSR_READ_2(sc, WI_RX_FID);
37446492Swpaul
37546492Swpaul	/* First read in the frame header */
37646492Swpaul	if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) {
37746492Swpaul		ifp->if_ierrors++;
37846492Swpaul		return;
37946492Swpaul	}
38046492Swpaul
38146492Swpaul	if (rx_frame.wi_status & WI_STAT_ERRSTAT) {
38246492Swpaul		ifp->if_ierrors++;
38346492Swpaul		return;
38446492Swpaul	}
38546492Swpaul
38646492Swpaul	MGETHDR(m, M_DONTWAIT, MT_DATA);
38746492Swpaul	if (m == NULL) {
38846492Swpaul		ifp->if_ierrors++;
38946492Swpaul		return;
39046492Swpaul	}
39146492Swpaul	MCLGET(m, M_DONTWAIT);
39246492Swpaul	if (!(m->m_flags & M_EXT)) {
39346492Swpaul		m_freem(m);
39446492Swpaul		ifp->if_ierrors++;
39546492Swpaul		return;
39646492Swpaul	}
39746492Swpaul
39846492Swpaul	eh = mtod(m, struct ether_header *);
39946492Swpaul	m->m_pkthdr.rcvif = ifp;
40046492Swpaul
40146492Swpaul	if (rx_frame.wi_status == WI_STAT_1042 ||
40246492Swpaul	    rx_frame.wi_status == WI_STAT_TUNNEL ||
40346492Swpaul	    rx_frame.wi_status == WI_STAT_WMP_MSG) {
40446492Swpaul		m->m_pkthdr.len = m->m_len =
40546492Swpaul		    rx_frame.wi_dat_len + WI_SNAPHDR_LEN;
40646492Swpaul
40746492Swpaul		bcopy((char *)&rx_frame.wi_addr1,
40846492Swpaul		    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
40946492Swpaul		bcopy((char *)&rx_frame.wi_addr2,
41046492Swpaul		    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
41146492Swpaul		bcopy((char *)&rx_frame.wi_type,
41246492Swpaul		    (char *)&eh->ether_type, sizeof(u_int16_t));
41346492Swpaul
41446492Swpaul		if (wi_read_data(sc, id, WI_802_11_OFFSET,
41546492Swpaul		    mtod(m, caddr_t) + sizeof(struct ether_header),
41646492Swpaul		    m->m_len + 2)) {
41746492Swpaul			m_freem(m);
41846492Swpaul			ifp->if_ierrors++;
41946492Swpaul			return;
42046492Swpaul		}
42146492Swpaul	} else {
42246492Swpaul		m->m_pkthdr.len = m->m_len =
42346492Swpaul		    rx_frame.wi_dat_len + sizeof(struct ether_header);
42446492Swpaul
42546492Swpaul		if (wi_read_data(sc, id, WI_802_3_OFFSET,
42646492Swpaul		    mtod(m, caddr_t), m->m_len + 2)) {
42746492Swpaul			m_freem(m);
42846492Swpaul			ifp->if_ierrors++;
42946492Swpaul			return;
43046492Swpaul		}
43146492Swpaul	}
43246492Swpaul
43346492Swpaul	ifp->if_ipackets++;
43446492Swpaul
43546492Swpaul#if NBPFILTER > 0
43646492Swpaul	/* Handle BPF listeners. */
43746492Swpaul	if (ifp->if_bpf) {
43846492Swpaul		bpf_mtap(ifp, m);
43946492Swpaul		if (ifp->if_flags & IFF_PROMISC &&
44046492Swpaul		    (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
44146492Swpaul		    ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) {
44246492Swpaul			m_freem(m);
44346492Swpaul			return;
44446492Swpaul		}
44546492Swpaul	}
44646492Swpaul#endif
44746492Swpaul
44846492Swpaul	/* Receive packet. */
44946492Swpaul	m_adj(m, sizeof(struct ether_header));
45046492Swpaul	ether_input(ifp, eh, m);
45146492Swpaul
45246492Swpaul	return;
45346492Swpaul}
45446492Swpaul
45546492Swpaulstatic void wi_txeof(sc, status)
45646492Swpaul	struct wi_softc		*sc;
45746492Swpaul	int			status;
45846492Swpaul{
45946492Swpaul	struct ifnet		*ifp;
46046492Swpaul
46146492Swpaul	ifp = &sc->arpcom.ac_if;
46246492Swpaul
46346492Swpaul	ifp->if_timer = 0;
46446492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
46546492Swpaul
46646492Swpaul	if (status & WI_EV_TX_EXC)
46746492Swpaul		ifp->if_oerrors++;
46846492Swpaul	else
46946492Swpaul		ifp->if_opackets++;
47046492Swpaul
47146492Swpaul	return;
47246492Swpaul}
47346492Swpaul
47446492Swpaulvoid wi_inquire(xsc)
47546492Swpaul	void			*xsc;
47646492Swpaul{
47746492Swpaul	struct wi_softc		*sc;
47846492Swpaul	struct ifnet		*ifp;
47946492Swpaul
48046492Swpaul	sc = xsc;
48146492Swpaul	ifp = &sc->arpcom.ac_if;
48246492Swpaul
48346492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
48446492Swpaul
48546492Swpaul	/* Don't do this while we're transmitting */
48646492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
48746492Swpaul		return;
48846492Swpaul
48946492Swpaul	wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS);
49046492Swpaul
49146492Swpaul	return;
49246492Swpaul}
49346492Swpaul
49446492Swpaulvoid wi_update_stats(sc)
49546492Swpaul	struct wi_softc		*sc;
49646492Swpaul{
49746492Swpaul	struct wi_ltv_gen	gen;
49846492Swpaul	u_int16_t		id;
49946492Swpaul	struct ifnet		*ifp;
50046492Swpaul	u_int32_t		*ptr;
50146492Swpaul	int			i;
50246492Swpaul	u_int16_t		t;
50346492Swpaul
50446492Swpaul	ifp = &sc->arpcom.ac_if;
50546492Swpaul
50646492Swpaul	id = CSR_READ_2(sc, WI_INFO_FID);
50746492Swpaul
50846492Swpaul	wi_read_data(sc, id, 0, (char *)&gen, 4);
50946492Swpaul
51046492Swpaul	if (gen.wi_type != WI_INFO_COUNTERS ||
51146492Swpaul	    gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1)
51246492Swpaul		return;
51346492Swpaul
51446492Swpaul	ptr = (u_int32_t *)&sc->wi_stats;
51546492Swpaul
51646492Swpaul	for (i = 0; i < gen.wi_len - 1; i++) {
51746492Swpaul		t = CSR_READ_2(sc, WI_DATA1);
51846492Swpaul#ifdef WI_HERMES_STATS_WAR
51946492Swpaul		if (t > 0xF000)
52046492Swpaul			t = ~t & 0xFFFF;
52146492Swpaul#endif
52246492Swpaul		ptr[i] += t;
52346492Swpaul	}
52446492Swpaul
52546492Swpaul	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
52646492Swpaul	    sc->wi_stats.wi_tx_multi_retries +
52746492Swpaul	    sc->wi_stats.wi_tx_retry_limit;
52846492Swpaul
52946492Swpaul	return;
53046492Swpaul}
53146492Swpaul
53246492Swpaulvoid wi_intr(unit)
53346492Swpaul	int			unit;
53446492Swpaul{
53546492Swpaul	struct wi_softc		*sc;
53646492Swpaul	struct ifnet		*ifp;
53746492Swpaul	u_int16_t		status;
53846492Swpaul
53946492Swpaul	sc = &wi_softc[unit];
54046492Swpaul	ifp = &sc->arpcom.ac_if;
54146492Swpaul
54246492Swpaul	if (!(ifp->if_flags & IFF_UP)) {
54346492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
54446492Swpaul		CSR_WRITE_2(sc, WI_INT_EN, 0);
54546492Swpaul		return;
54646492Swpaul	}
54746492Swpaul
54846492Swpaul	/* Disable interrupts. */
54946492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
55046492Swpaul
55146492Swpaul	status = CSR_READ_2(sc, WI_EVENT_STAT);
55246492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS);
55346492Swpaul
55446492Swpaul	if (status & WI_EV_RX) {
55546492Swpaul		wi_rxeof(sc);
55646492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
55746492Swpaul	}
55846492Swpaul
55946492Swpaul	if (status & WI_EV_TX) {
56046492Swpaul		wi_txeof(sc, status);
56146492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX);
56246492Swpaul	}
56346492Swpaul
56446492Swpaul	if (status & WI_EV_ALLOC) {
56546492Swpaul		int			id;
56646492Swpaul		id = CSR_READ_2(sc, WI_ALLOC_FID);
56746492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
56846492Swpaul		if (id == sc->wi_tx_data_id)
56946492Swpaul			wi_txeof(sc, status);
57046492Swpaul	}
57146492Swpaul
57246492Swpaul	if (status & WI_EV_INFO) {
57346492Swpaul		wi_update_stats(sc);
57446492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
57546492Swpaul	}
57646492Swpaul
57746492Swpaul	if (status & WI_EV_TX_EXC) {
57846492Swpaul		wi_txeof(sc, status);
57946492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
58046492Swpaul	}
58146492Swpaul
58246492Swpaul	if (status & WI_EV_INFO_DROP) {
58346492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP);
58446492Swpaul	}
58546492Swpaul
58646492Swpaul	/* Re-enable interrupts. */
58746492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
58846492Swpaul
58946492Swpaul	if (ifp->if_snd.ifq_head != NULL)
59046492Swpaul		wi_start(ifp);
59146492Swpaul
59246492Swpaul	return;
59346492Swpaul}
59446492Swpaul
59546492Swpaulstatic int wi_cmd(sc, cmd, val)
59646492Swpaul	struct wi_softc		*sc;
59746492Swpaul	int			cmd;
59846492Swpaul	int			val;
59946492Swpaul{
60046492Swpaul	int			i, s = 0;
60146492Swpaul
60246492Swpaul	CSR_WRITE_2(sc, WI_PARAM0, val);
60346492Swpaul	CSR_WRITE_2(sc, WI_COMMAND, cmd);
60446492Swpaul
60546492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
60646492Swpaul		/*
60746492Swpaul		 * Wait for 'command complete' bit to be
60846492Swpaul		 * set in the event status register.
60946492Swpaul		 */
61046492Swpaul		s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD;
61146492Swpaul		if (s) {
61246492Swpaul			/* Ack the event and read result code. */
61346492Swpaul			s = CSR_READ_2(sc, WI_STATUS);
61446492Swpaul			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
61546492Swpaul#ifdef foo
61646492Swpaul			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
61746492Swpaul				return(EIO);
61846492Swpaul#endif
61946492Swpaul			if (s & WI_STAT_CMD_RESULT)
62046492Swpaul				return(EIO);
62146492Swpaul			break;
62246492Swpaul		}
62346492Swpaul	}
62446492Swpaul
62546492Swpaul	if (i == WI_TIMEOUT)
62646492Swpaul		return(ETIMEDOUT);
62746492Swpaul
62846492Swpaul	return(0);
62946492Swpaul}
63046492Swpaul
63146492Swpaulstatic void wi_reset(sc)
63246492Swpaul	struct wi_softc		*sc;
63346492Swpaul{
63446492Swpaul	if (wi_cmd(sc, WI_CMD_INI, 0))
63546492Swpaul		printf("wi%d: init failed\n", sc->wi_unit);
63646492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
63746492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
63846492Swpaul
63946492Swpaul	/* Calibrate timer. */
64046492Swpaul	WI_SETVAL(WI_RID_TICK_TIME, 8);
64146492Swpaul
64246492Swpaul	return;
64346492Swpaul}
64446492Swpaul
64546492Swpaul/*
64646492Swpaul * Read an LTV record from the NIC.
64746492Swpaul */
64846492Swpaulstatic int wi_read_record(sc, ltv)
64946492Swpaul	struct wi_softc		*sc;
65046492Swpaul	struct wi_ltv_gen	*ltv;
65146492Swpaul{
65246492Swpaul	u_int16_t		*ptr;
65346492Swpaul	int			i, len, code;
65446492Swpaul
65546492Swpaul	/* Tell the NIC to enter record read mode. */
65646492Swpaul	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type))
65746492Swpaul		return(EIO);
65846492Swpaul
65946492Swpaul	/* Select the record we want to read. */
66046492Swpaul	CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type);
66146492Swpaul
66246492Swpaul	/* Specify offset -- we always read the whole record. */
66346492Swpaul	CSR_WRITE_2(sc, WI_OFF1, 0);
66446492Swpaul
66546492Swpaul	/* Wait for NIC to acknowledge */
66646492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
66746492Swpaul		if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR)))
66846492Swpaul			break;
66946492Swpaul	}
67046492Swpaul
67146492Swpaul	if (i == WI_TIMEOUT)
67246492Swpaul		return(ETIMEDOUT);
67346492Swpaul
67446492Swpaul	/*
67546492Swpaul	 * Read the length and record type and make sure they
67646492Swpaul	 * match what we expect (this verifies that we have enough
67746492Swpaul	 * room to hold all of the returned data.
67846492Swpaul	 */
67946492Swpaul	len = CSR_READ_2(sc, WI_DATA1);
68046492Swpaul	if (len > ltv->wi_len)
68146492Swpaul		return(ENOSPC);
68246492Swpaul	code = CSR_READ_2(sc, WI_DATA1);
68346492Swpaul	if (code != ltv->wi_type)
68446492Swpaul		return(EIO);
68546492Swpaul
68646492Swpaul	ltv->wi_len = len;
68746492Swpaul	ltv->wi_type = code;
68846492Swpaul
68946492Swpaul	/* Now read the data. */
69046492Swpaul	ptr = &ltv->wi_val;
69146492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
69246492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
69346492Swpaul
69446492Swpaul	return(0);
69546492Swpaul}
69646492Swpaul
69746492Swpaul/*
69846492Swpaul * Same as read, except we inject data instead of reading it.
69946492Swpaul */
70046492Swpaulstatic int wi_write_record(sc, ltv)
70146492Swpaul	struct wi_softc		*sc;
70246492Swpaul	struct wi_ltv_gen	*ltv;
70346492Swpaul{
70446492Swpaul	u_int16_t		*ptr;
70546492Swpaul	int			i;
70646492Swpaul
70746492Swpaul	CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type);
70846492Swpaul	CSR_WRITE_2(sc, WI_OFF1, 0);
70946492Swpaul
71046492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
71146492Swpaul		if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR)))
71246492Swpaul			break;
71346492Swpaul	}
71446492Swpaul
71546492Swpaul	if (i == WI_TIMEOUT)
71646492Swpaul		return(ETIMEDOUT);
71746492Swpaul
71846492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len);
71946492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type);
72046492Swpaul
72146492Swpaul	ptr = &ltv->wi_val;
72246492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
72346492Swpaul		CSR_WRITE_2(sc, WI_DATA1, ptr[i]);
72446492Swpaul
72546492Swpaul	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type))
72646492Swpaul		return(EIO);
72746492Swpaul
72846492Swpaul	return(0);
72946492Swpaul}
73046492Swpaul
73146492Swpaulstatic int wi_seek(sc, id, off, chan)
73246492Swpaul	struct wi_softc		*sc;
73346492Swpaul	int			id, off, chan;
73446492Swpaul{
73546492Swpaul	int			i;
73646492Swpaul	int			selreg, offreg;
73746492Swpaul
73846492Swpaul	switch (chan) {
73946492Swpaul	case WI_BAP0:
74046492Swpaul		selreg = WI_SEL0;
74146492Swpaul		offreg = WI_OFF0;
74246492Swpaul		break;
74346492Swpaul	case WI_BAP1:
74446492Swpaul		selreg = WI_SEL1;
74546492Swpaul		offreg = WI_OFF1;
74646492Swpaul		break;
74746492Swpaul	default:
74846492Swpaul		printf("wi%d: invalid data path: %x\n", sc->wi_unit, chan);
74946492Swpaul		return(EIO);
75046492Swpaul	}
75146492Swpaul
75246492Swpaul	CSR_WRITE_2(sc, selreg, id);
75346492Swpaul	CSR_WRITE_2(sc, offreg, off);
75446492Swpaul
75546492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
75646492Swpaul		if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR)))
75746492Swpaul			break;
75846492Swpaul	}
75946492Swpaul
76046492Swpaul	if (i == WI_TIMEOUT)
76146492Swpaul		return(ETIMEDOUT);
76246492Swpaul
76346492Swpaul	return(0);
76446492Swpaul}
76546492Swpaul
76646492Swpaulstatic int wi_read_data(sc, id, off, buf, len)
76746492Swpaul	struct wi_softc		*sc;
76846492Swpaul	int			id, off;
76946492Swpaul	caddr_t			buf;
77046492Swpaul	int			len;
77146492Swpaul{
77246492Swpaul	int			i;
77346492Swpaul	u_int16_t		*ptr;
77446492Swpaul
77546492Swpaul	if (wi_seek(sc, id, off, WI_BAP1))
77646492Swpaul		return(EIO);
77746492Swpaul
77846492Swpaul	ptr = (u_int16_t *)buf;
77946492Swpaul	for (i = 0; i < len / 2; i++)
78046492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
78146492Swpaul
78246492Swpaul	return(0);
78346492Swpaul}
78446492Swpaul
78546492Swpaul/*
78646492Swpaul * According to the comments in the HCF Light code, there is a bug in
78746492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where
78846492Swpaul * the chip's internal autoincrement counter gets thrown off during
78946492Swpaul * data writes: the autoincrement is missed, causing one data word to
79046492Swpaul * be overwritten and subsequent words to be written to the wrong memory
79146492Swpaul * locations. The end result is that we could end up transmitting bogus
79246492Swpaul * frames without realizing it. The workaround for this is to write a
79346492Swpaul * couple of extra guard words after the end of the transfer, then
79446492Swpaul * attempt to read then back. If we fail to locate the guard words where
79546492Swpaul * we expect them, we preform the transfer over again.
79646492Swpaul */
79746492Swpaulstatic int wi_write_data(sc, id, off, buf, len)
79846492Swpaul	struct wi_softc		*sc;
79946492Swpaul	int			id, off;
80046492Swpaul	caddr_t			buf;
80146492Swpaul	int			len;
80246492Swpaul{
80346492Swpaul	int			i;
80446492Swpaul	u_int16_t		*ptr;
80546492Swpaul
80646492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
80746492Swpaulagain:
80846492Swpaul#endif
80946492Swpaul
81046492Swpaul	if (wi_seek(sc, id, off, WI_BAP0))
81146492Swpaul		return(EIO);
81246492Swpaul
81346492Swpaul	ptr = (u_int16_t *)buf;
81446492Swpaul	for (i = 0; i < (len / 2); i++)
81546492Swpaul		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
81646492Swpaul
81746492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
81846492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x1234);
81946492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x5678);
82046492Swpaul
82146492Swpaul	if (wi_seek(sc, id, off + len, WI_BAP0))
82246492Swpaul		return(EIO);
82346492Swpaul
82446492Swpaul	if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
82546492Swpaul	    CSR_READ_2(sc, WI_DATA0) != 0x5678)
82646492Swpaul		goto again;
82746492Swpaul#endif
82846492Swpaul
82946492Swpaul	return(0);
83046492Swpaul}
83146492Swpaul
83246492Swpaul/*
83346492Swpaul * Allocate a region of memory inside the NIC and zero
83446492Swpaul * it out.
83546492Swpaul */
83646492Swpaulstatic int wi_alloc_nicmem(sc, len, id)
83746492Swpaul	struct wi_softc		*sc;
83846492Swpaul	int			len;
83946492Swpaul	int			*id;
84046492Swpaul{
84146492Swpaul	int			i;
84246492Swpaul
84346492Swpaul	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) {
84446492Swpaul		printf("wi%d: failed to allocate %d bytes on NIC\n",
84546492Swpaul		    sc->wi_unit, len);
84646492Swpaul		return(ENOMEM);
84746492Swpaul	}
84846492Swpaul
84946492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
85046492Swpaul		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
85146492Swpaul			break;
85246492Swpaul	}
85346492Swpaul
85446492Swpaul	if (i == WI_TIMEOUT)
85546492Swpaul		return(ETIMEDOUT);
85646492Swpaul
85746492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
85846492Swpaul	*id = CSR_READ_2(sc, WI_ALLOC_FID);
85946492Swpaul
86046492Swpaul	wi_seek(sc, *id, 0, WI_BAP0);
86146492Swpaul
86246492Swpaul	for (i = 0; i < len / 2; i++)
86346492Swpaul		CSR_WRITE_2(sc, WI_DATA0, 0);
86446492Swpaul
86546492Swpaul	return(0);
86646492Swpaul}
86746492Swpaul
86846492Swpaulstatic void wi_setmulti(sc)
86946492Swpaul	struct wi_softc		*sc;
87046492Swpaul{
87146492Swpaul	struct ifnet		*ifp;
87246492Swpaul	int			i = 0;
87346492Swpaul	struct ifmultiaddr	*ifma;
87446492Swpaul	struct wi_ltv_mcast	mcast;
87546492Swpaul
87646492Swpaul	ifp = &sc->arpcom.ac_if;
87746492Swpaul
87846492Swpaul	bzero((char *)&mcast, sizeof(mcast));
87946492Swpaul
88046492Swpaul	mcast.wi_type = WI_RID_MCAST;
88146492Swpaul	mcast.wi_len = (3 * 16) + 1;
88246492Swpaul
88346492Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
88446492Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
88546492Swpaul		return;
88646492Swpaul	}
88746492Swpaul
88846492Swpaul	for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
88946492Swpaul				ifma = ifma->ifma_link.le_next) {
89046492Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
89146492Swpaul			continue;
89246492Swpaul		if (i < 16) {
89346492Swpaul			bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
89446492Swpaul			    (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN);
89546492Swpaul			i++;
89646492Swpaul		} else {
89746492Swpaul			bzero((char *)&mcast, sizeof(mcast));
89846492Swpaul			break;
89946492Swpaul		}
90046492Swpaul	}
90146492Swpaul
90246492Swpaul	mcast.wi_len = (i * 3) + 1;
90346492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
90446492Swpaul
90546492Swpaul	return;
90646492Swpaul}
90746492Swpaul
90846492Swpaulstatic void wi_setdef(sc, wreq)
90946492Swpaul	struct wi_softc		*sc;
91046492Swpaul	struct wi_req		*wreq;
91146492Swpaul{
91246492Swpaul	struct sockaddr_dl	*sdl;
91346492Swpaul	struct ifaddr		*ifa;
91446492Swpaul	struct ifnet		*ifp;
91546492Swpaul
91646492Swpaul	ifp = &sc->arpcom.ac_if;
91746492Swpaul
91846492Swpaul	switch(wreq->wi_type) {
91946492Swpaul	case WI_RID_MAC_NODE:
92046492Swpaul		ifa = ifnet_addrs[ifp->if_index - 1];
92146492Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
92246492Swpaul		bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr,
92346492Swpaul		   ETHER_ADDR_LEN);
92446492Swpaul		bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN);
92546492Swpaul		break;
92646492Swpaul	case WI_RID_PORTTYPE:
92746492Swpaul		sc->wi_ptype = wreq->wi_val[0];
92846492Swpaul		break;
92946492Swpaul	case WI_RID_TX_RATE:
93046492Swpaul		sc->wi_tx_rate = wreq->wi_val[0];
93146492Swpaul		break;
93246492Swpaul	case WI_RID_MAX_DATALEN:
93346492Swpaul		sc->wi_max_data_len = wreq->wi_val[0];
93446492Swpaul		break;
93546492Swpaul	case WI_RID_RTS_THRESH:
93646492Swpaul		sc->wi_rts_thresh = wreq->wi_val[0];
93746492Swpaul		break;
93846492Swpaul	case WI_RID_SYSTEM_SCALE:
93946492Swpaul		sc->wi_ap_density = wreq->wi_val[0];
94046492Swpaul		break;
94146492Swpaul	case WI_RID_CREATE_IBSS:
94246492Swpaul		sc->wi_create_ibss = wreq->wi_val[0];
94346492Swpaul		break;
94446492Swpaul	case WI_RID_NODENAME:
94546492Swpaul		bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
94646492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30);
94746492Swpaul		break;
94846492Swpaul	case WI_RID_DESIRED_SSID:
94946492Swpaul		bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
95046492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30);
95146492Swpaul		break;
95246492Swpaul	case WI_RID_OWN_SSID:
95346492Swpaul		bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
95446492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30);
95546492Swpaul		break;
95646492Swpaul	default:
95746492Swpaul		break;
95846492Swpaul	}
95946492Swpaul
96046492Swpaul	return;
96146492Swpaul}
96246492Swpaul
96346492Swpaulstatic int wi_ioctl(ifp, command, data)
96446492Swpaul	struct ifnet		*ifp;
96546492Swpaul	u_long			command;
96646492Swpaul	caddr_t			data;
96746492Swpaul{
96846492Swpaul	int			s, error = 0;
96946492Swpaul	struct wi_softc		*sc;
97046492Swpaul	struct wi_req		wreq;
97146492Swpaul	struct ifreq		*ifr;
97246492Swpaul
97346492Swpaul	s = splimp();
97446492Swpaul
97546492Swpaul	sc = ifp->if_softc;
97646492Swpaul	ifr = (struct ifreq *)data;
97746492Swpaul
97846492Swpaul	if (sc->wi_gone)
97946492Swpaul		return(ENODEV);
98046492Swpaul
98146492Swpaul	switch(command) {
98246492Swpaul	case SIOCSIFADDR:
98346492Swpaul	case SIOCGIFADDR:
98446492Swpaul	case SIOCSIFMTU:
98546492Swpaul		error = ether_ioctl(ifp, command, data);
98646492Swpaul		break;
98746492Swpaul	case SIOCSIFFLAGS:
98846492Swpaul		if (ifp->if_flags & IFF_UP) {
98946492Swpaul			if (ifp->if_flags & IFF_RUNNING &&
99046492Swpaul			    ifp->if_flags & IFF_PROMISC &&
99146492Swpaul			    !(sc->wi_if_flags & IFF_PROMISC)) {
99246492Swpaul				WI_SETVAL(WI_RID_PROMISC, 1);
99346492Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
99446492Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
99546492Swpaul			    sc->wi_if_flags & IFF_PROMISC) {
99646492Swpaul				WI_SETVAL(WI_RID_PROMISC, 0);
99746492Swpaul			} else
99846492Swpaul				wi_init(sc);
99946492Swpaul		} else {
100046492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
100146492Swpaul				wi_stop(sc);
100246492Swpaul			}
100346492Swpaul		}
100446492Swpaul		sc->wi_if_flags = ifp->if_flags;
100546492Swpaul		error = 0;
100646492Swpaul		break;
100746492Swpaul	case SIOCADDMULTI:
100846492Swpaul	case SIOCDELMULTI:
100946492Swpaul		wi_setmulti(sc);
101046492Swpaul		error = 0;
101146492Swpaul		break;
101246492Swpaul	case SIOCGWAVELAN:
101346492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
101446492Swpaul		if (error)
101546492Swpaul			break;
101646492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
101746492Swpaul			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
101846492Swpaul			    sizeof(sc->wi_stats));
101946492Swpaul			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
102046492Swpaul		} else {
102146492Swpaul			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
102246492Swpaul				error = EINVAL;
102346492Swpaul				break;
102446492Swpaul			}
102546492Swpaul		}
102646492Swpaul		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
102746492Swpaul		break;
102846492Swpaul	case SIOCSWAVELAN:
102946492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
103046492Swpaul		if (error)
103146492Swpaul			break;
103246492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
103346492Swpaul			error = EINVAL;
103446492Swpaul			break;
103546492Swpaul		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
103646492Swpaul			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
103746492Swpaul			    wreq.wi_len);
103846492Swpaul		} else {
103946492Swpaul			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
104046492Swpaul			if (!error)
104146492Swpaul				wi_setdef(sc, &wreq);
104246492Swpaul		}
104346492Swpaul		break;
104446492Swpaul	default:
104546492Swpaul		error = EINVAL;
104646492Swpaul		break;
104746492Swpaul	}
104846492Swpaul
104946492Swpaul	splx(s);
105046492Swpaul
105146492Swpaul	return(error);
105246492Swpaul}
105346492Swpaul
105446492Swpaulstatic void wi_init(xsc)
105546492Swpaul	void			*xsc;
105646492Swpaul{
105746492Swpaul	struct wi_softc		*sc = xsc;
105846492Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
105946492Swpaul	int			s;
106046492Swpaul	struct wi_ltv_macaddr	mac;
106146492Swpaul	int			id = 0;
106246492Swpaul
106346492Swpaul	if (sc->wi_gone)
106446492Swpaul		return;
106546492Swpaul
106646492Swpaul	s = splimp();
106746492Swpaul
106846492Swpaul	if (ifp->if_flags & IFF_RUNNING)
106946492Swpaul		wi_stop(sc);
107046492Swpaul
107146492Swpaul	wi_reset(sc);
107246492Swpaul
107346492Swpaul	/* Program max data length. */
107446492Swpaul	WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len);
107546492Swpaul
107646492Swpaul	/* Enable/disable IBSS ctration. */
107746492Swpaul	WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss);
107846492Swpaul
107946492Swpaul	/* Set the port type. */
108046492Swpaul	WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype);
108146492Swpaul
108246492Swpaul	/* Program the RTS/CTS threshold. */
108346492Swpaul	WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh);
108446492Swpaul
108546492Swpaul	/* Program the TX rate */
108646492Swpaul	WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate);
108746492Swpaul
108846492Swpaul	/* Access point density */
108946492Swpaul	WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density);
109046492Swpaul
109146492Swpaul	/* Specify the IBSS name */
109246492Swpaul	WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name);
109346492Swpaul
109446492Swpaul	/* Specify the network name */
109546492Swpaul	WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name);
109646492Swpaul
109746492Swpaul	/* Program the nodename. */
109846492Swpaul	WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name);
109946492Swpaul
110046492Swpaul	/* Set our MAC address. */
110146492Swpaul	mac.wi_len = 4;
110246492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
110346492Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
110446492Swpaul	   (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN);
110546492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mac);
110646492Swpaul
110746492Swpaul	/* Initialize promisc mode. */
110846492Swpaul	if (ifp->if_flags & IFF_PROMISC) {
110946492Swpaul		WI_SETVAL(WI_RID_PROMISC, 1);
111046492Swpaul	} else {
111146492Swpaul		WI_SETVAL(WI_RID_PROMISC, 0);
111246492Swpaul	}
111346492Swpaul
111446492Swpaul	/* Set multicast filter. */
111546492Swpaul	wi_setmulti(sc);
111646492Swpaul
111746492Swpaul	/* Enable desired port */
111846492Swpaul	wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0);
111946492Swpaul
112046492Swpaul	if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id))
112146492Swpaul		printf("wi%d: mem allocation failed...\n", sc->wi_unit);
112246492Swpaul	sc->wi_tx_data_id = id;
112346492Swpaul
112446492Swpaul	if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id))
112546492Swpaul		printf("wi%d: mem allocation failed...\n", sc->wi_unit);
112646492Swpaul	sc->wi_tx_mgmt_id = id;
112746492Swpaul
112846492Swpaul	/* enable interrupts */
112946492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
113046492Swpaul
113146492Swpaul	splx(s);
113246492Swpaul
113346492Swpaul	ifp->if_flags |= IFF_RUNNING;
113446492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
113546492Swpaul
113646492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
113746492Swpaul
113846492Swpaul	return;
113946492Swpaul}
114046492Swpaul
114146492Swpaulstatic void wi_start(ifp)
114246492Swpaul	struct ifnet		*ifp;
114346492Swpaul{
114446492Swpaul	struct wi_softc		*sc;
114546492Swpaul	struct mbuf		*m0;
114646492Swpaul	struct wi_frame		tx_frame;
114746492Swpaul	struct ether_header	*eh;
114846492Swpaul	int			id;
114946492Swpaul
115046492Swpaul	sc = ifp->if_softc;
115146492Swpaul
115246492Swpaul	if (sc->wi_gone)
115346492Swpaul		return;
115446492Swpaul
115546492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
115646492Swpaul		return;
115746492Swpaul
115846492Swpaul	IF_DEQUEUE(&ifp->if_snd, m0);
115946492Swpaul	if (m0 == NULL)
116046492Swpaul		return;
116146492Swpaul
116246492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
116346492Swpaul	id = sc->wi_tx_data_id;
116446492Swpaul	eh = mtod(m0, struct ether_header *);
116546492Swpaul
116646492Swpaul	/*
116746492Swpaul	 * Use RFC1042 encoding for IP and ARP datagrames,
116846492Swpaul	 * 802.3 for anything else.
116946492Swpaul	 */
117046492Swpaul	if (ntohs(eh->ether_type) == ETHERTYPE_IP ||
117146492Swpaul	    ntohs(eh->ether_type) == ETHERTYPE_ARP ||
117246492Swpaul	    ntohs(eh->ether_type) == ETHERTYPE_REVARP) {
117346492Swpaul		bcopy((char *)&eh->ether_dhost,
117446492Swpaul		    (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN);
117546492Swpaul		bcopy((char *)&eh->ether_shost,
117646492Swpaul		    (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
117746492Swpaul		bcopy((char *)&eh->ether_dhost,
117846492Swpaul		    (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN);
117946492Swpaul		bcopy((char *)&eh->ether_shost,
118046492Swpaul		    (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN);
118146492Swpaul
118246492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN;
118346492Swpaul		tx_frame.wi_frame_ctl = WI_FTYPE_DATA;
118446492Swpaul		tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0);
118546492Swpaul		tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1);
118646492Swpaul		tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
118746492Swpaul		tx_frame.wi_type = eh->ether_type;
118846492Swpaul
118946492Swpaul		m_copydata(m0, sizeof(struct ether_header),
119046492Swpaul		    m0->m_pkthdr.len - sizeof(struct ether_header),
119146492Swpaul		    (caddr_t)&sc->wi_txbuf);
119246492Swpaul
119346492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
119446492Swpaul		    sizeof(struct wi_frame));
119546492Swpaul		wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf,
119646492Swpaul		    (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2);
119746492Swpaul	} else {
119846492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len;
119946492Swpaul
120046492Swpaul		m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf);
120146492Swpaul
120246492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
120346492Swpaul		    sizeof(struct wi_frame));
120446492Swpaul		wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf,
120546492Swpaul		    m0->m_pkthdr.len + 2);
120646492Swpaul	}
120746492Swpaul
120846492Swpaul#if NBPFILTER > 0
120946492Swpaul	/*
121046492Swpaul	 * If there's a BPF listner, bounce a copy of
121146492Swpaul	 * this frame to him.
121246492Swpaul	 */
121346492Swpaul	if (ifp->if_bpf)
121446492Swpaul		bpf_mtap(ifp, m0);
121546492Swpaul#endif
121646492Swpaul
121746492Swpaul	m_freem(m0);
121846492Swpaul
121946492Swpaul	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id))
122046492Swpaul		printf("wi%d: xmit failed\n", sc->wi_unit);
122146492Swpaul
122246492Swpaul	ifp->if_flags |= IFF_OACTIVE;
122346492Swpaul
122446492Swpaul	/*
122546492Swpaul	 * Set a timeout in case the chip goes out to lunch.
122646492Swpaul	 */
122746492Swpaul	ifp->if_timer = 5;
122846492Swpaul
122946492Swpaul	return;
123046492Swpaul}
123146492Swpaul
123246492Swpaulstatic int wi_mgmt_xmit(sc, data, len)
123346492Swpaul	struct wi_softc		*sc;
123446492Swpaul	caddr_t			data;
123546492Swpaul	int			len;
123646492Swpaul{
123746492Swpaul	struct wi_frame		tx_frame;
123846492Swpaul	int			id;
123946492Swpaul	struct wi_80211_hdr	*hdr;
124046492Swpaul	caddr_t			dptr;
124146492Swpaul
124246492Swpaul	if (sc->wi_gone)
124346492Swpaul		return(ENODEV);
124446492Swpaul
124546492Swpaul	hdr = (struct wi_80211_hdr *)data;
124646492Swpaul	dptr = data + sizeof(struct wi_80211_hdr);
124746492Swpaul
124846492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
124946492Swpaul	id = sc->wi_tx_mgmt_id;
125046492Swpaul
125146492Swpaul	bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl,
125246492Swpaul	   sizeof(struct wi_80211_hdr));
125346492Swpaul
125446492Swpaul	tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN;
125546492Swpaul	tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN);
125646492Swpaul
125746492Swpaul	wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame));
125846492Swpaul	wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr,
125946492Swpaul	    (len - sizeof(struct wi_80211_hdr)) + 2);
126046492Swpaul
126146492Swpaul	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) {
126246492Swpaul		printf("wi%d: xmit failed\n", sc->wi_unit);
126346492Swpaul		return(EIO);
126446492Swpaul	}
126546492Swpaul
126646492Swpaul	return(0);
126746492Swpaul}
126846492Swpaul
126946492Swpaulstatic void wi_stop(sc)
127046492Swpaul	struct wi_softc		*sc;
127146492Swpaul{
127246492Swpaul	struct ifnet		*ifp;
127346492Swpaul
127446492Swpaul	if (sc->wi_gone)
127546492Swpaul		return;
127646492Swpaul
127746492Swpaul	ifp = &sc->arpcom.ac_if;
127846492Swpaul
127946492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
128046492Swpaul	wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0);
128146492Swpaul
128246492Swpaul	untimeout(wi_inquire, sc, sc->wi_stat_ch);
128346492Swpaul
128446492Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
128546492Swpaul
128646492Swpaul	return;
128746492Swpaul}
128846492Swpaul
128946492Swpaulstatic void wi_watchdog(ifp)
129046492Swpaul	struct ifnet		*ifp;
129146492Swpaul{
129246492Swpaul	struct wi_softc		*sc;
129346492Swpaul
129446492Swpaul	sc = ifp->if_softc;
129546492Swpaul
129646492Swpaul	printf("wi%d: device timeout\n", sc->wi_unit);
129746492Swpaul
129846492Swpaul	wi_init(sc);
129946492Swpaul
130046492Swpaul	ifp->if_oerrors++;
130146492Swpaul
130246492Swpaul	return;
130346492Swpaul}
130446492Swpaul
130546492Swpaulstatic void wi_shutdown(howto, arg)
130646492Swpaul	int			howto;
130746492Swpaul	void			*arg;
130846492Swpaul{
130946492Swpaul	struct wi_softc		*sc;
131046492Swpaul
131146492Swpaul	sc = arg;
131246492Swpaul	wi_stop(sc);
131346492Swpaul
131446492Swpaul	return;
131546492Swpaul}
1316