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