if_wi.c revision 93825
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
3346492Swpaul/*
3446492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD.
3546492Swpaul *
3646492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
3746492Swpaul * Electrical Engineering Department
3846492Swpaul * Columbia University, New York City
3946492Swpaul */
4046492Swpaul
4146492Swpaul/*
4247401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN
4346492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed
4446492Swpaul * entirely via a firmware-driven controller called the Hermes.
4546492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual
4646492Swpaul * without an NDA (if at all). What they do release is an API library
4746492Swpaul * called the HCF (Hardware Control Functions) which is supposed to
4846492Swpaul * do the device-specific operations of a device driver for you. The
4946492Swpaul * publically available version of the HCF library (the 'HCF Light') is
5047401Swpaul * a) extremely gross, b) lacks certain features, particularly support
5146492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License.
5246492Swpaul *
5346492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it
5446492Swpaul * programs the Hermes controller directly, using information gleaned
5546492Swpaul * from the HCF Light code and corresponding documentation.
5646492Swpaul *
5746492Swpaul * This driver supports both the PCMCIA and ISA versions of the
5846492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really
5946492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter
6046492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is
6146492Swpaul * inserted. Consequently, you need to use the pccard support for
6246492Swpaul * both the ISA and PCMCIA adapters.
6346492Swpaul */
6446492Swpaul
6546492Swpaul#include <sys/param.h>
6646492Swpaul#include <sys/systm.h>
6746492Swpaul#include <sys/sockio.h>
6846492Swpaul#include <sys/mbuf.h>
6983366Sjulian#include <sys/proc.h>
7046492Swpaul#include <sys/kernel.h>
7146492Swpaul#include <sys/socket.h>
7253702Swpaul#include <sys/module.h>
7353702Swpaul#include <sys/bus.h>
7453702Swpaul#include <sys/syslog.h>
7553702Swpaul#include <sys/sysctl.h>
7646492Swpaul
7753702Swpaul#include <machine/bus.h>
7853702Swpaul#include <machine/resource.h>
7953702Swpaul#include <sys/rman.h>
8053702Swpaul
8146492Swpaul#include <net/if.h>
8246492Swpaul#include <net/if_arp.h>
8346492Swpaul#include <net/ethernet.h>
8446492Swpaul#include <net/if_dl.h>
8546492Swpaul#include <net/if_media.h>
8646492Swpaul#include <net/if_types.h>
8777217Sphk#include <net/if_ieee80211.h>
8846492Swpaul
8946492Swpaul#include <netinet/in.h>
9046492Swpaul#include <netinet/in_systm.h>
9146492Swpaul#include <netinet/in_var.h>
9246492Swpaul#include <netinet/ip.h>
9346492Swpaul#include <netinet/if_ether.h>
9446492Swpaul
9546492Swpaul#include <net/bpf.h>
9646492Swpaul
9770808Speter#include <dev/wi/if_wavelan_ieee.h>
9893611Simp#include <dev/wi/if_wivar.h>
9970808Speter#include <dev/wi/if_wireg.h>
10046492Swpaul
10146492Swpaul#if !defined(lint)
10246492Swpaulstatic const char rcsid[] =
10350477Speter  "$FreeBSD: head/sys/dev/wi/if_wi.c 93825 2002-04-04 21:40:37Z imp $";
10446492Swpaul#endif
10546492Swpaul
10691693Simpstatic void wi_intr(void *);
10791693Simpstatic void wi_reset(struct wi_softc *);
10891693Simpstatic int wi_ioctl(struct ifnet *, u_long, caddr_t);
10991693Simpstatic void wi_init(void *);
11091693Simpstatic void wi_start(struct ifnet *);
11191693Simpstatic void wi_stop(struct wi_softc *);
11291693Simpstatic void wi_watchdog(struct ifnet *);
11391693Simpstatic void wi_rxeof(struct wi_softc *);
11491693Simpstatic void wi_txeof(struct wi_softc *, int);
11591693Simpstatic void wi_update_stats(struct wi_softc *);
11691693Simpstatic void wi_setmulti(struct wi_softc *);
11746492Swpaul
11892457Simpstatic int wi_cmd(struct wi_softc *, int, int, int, int);
11991693Simpstatic int wi_read_record(struct wi_softc *, struct wi_ltv_gen *);
12091693Simpstatic int wi_write_record(struct wi_softc *, struct wi_ltv_gen *);
12191693Simpstatic int wi_read_data(struct wi_softc *, int, int, caddr_t, int);
12291693Simpstatic int wi_write_data(struct wi_softc *, int, int, caddr_t, int);
12391693Simpstatic int wi_seek(struct wi_softc *, int, int, int);
12491693Simpstatic int wi_alloc_nicmem(struct wi_softc *, int, int *);
12591693Simpstatic void wi_inquire(void *);
12691693Simpstatic void wi_setdef(struct wi_softc *, struct wi_req *);
12791693Simpstatic int wi_mgmt_xmit(struct wi_softc *, caddr_t, int);
12846492Swpaul
12953702Swpaul#ifdef WICACHE
13053702Swpaulstatic
13191693Simpvoid wi_cache_store(struct wi_softc *, struct ether_header *,
13291693Simp	struct mbuf *, unsigned short);
13353702Swpaul#endif
13453702Swpaul
13591693Simpstatic int wi_get_cur_ssid(struct wi_softc *, char *, int *);
13693825Simpstatic void wi_get_id(struct wi_softc *);
13791693Simpstatic int wi_media_change(struct ifnet *);
13891693Simpstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
13977217Sphk
14093359Simpstatic int wi_get_debug(struct wi_softc *, struct wi_req *);
14193359Simpstatic int wi_set_debug(struct wi_softc *, struct wi_req *);
14293359Simp
14393611Simpdevclass_t wi_devclass;
14453702Swpaul
14593825Simpstruct wi_card_ident wi_card_ident[] = {
14693825Simp	/* CARD_ID			CARD_NAME		FIRM_TYPE */
14793825Simp	{ WI_NIC_LUCENT_ID,		WI_NIC_LUCENT_STR,	WI_LUCENT },
14893825Simp	{ WI_NIC_SONY_ID,		WI_NIC_SONY_STR,	WI_LUCENT },
14993825Simp	{ WI_NIC_LUCENT_EMB_ID,		WI_NIC_LUCENT_EMB_STR,	WI_LUCENT },
15093825Simp	{ WI_NIC_EVB2_ID,		WI_NIC_EVB2_STR,	WI_INTERSIL },
15193825Simp	{ WI_NIC_HWB3763_ID,		WI_NIC_HWB3763_STR,	WI_INTERSIL },
15293825Simp	{ WI_NIC_HWB3163_ID,		WI_NIC_HWB3163_STR,	WI_INTERSIL },
15393825Simp	{ WI_NIC_HWB3163B_ID,		WI_NIC_HWB3163B_STR,	WI_INTERSIL },
15493825Simp	{ WI_NIC_EVB3_ID,		WI_NIC_EVB3_STR,	WI_INTERSIL },
15593825Simp	{ WI_NIC_HWB1153_ID,		WI_NIC_HWB1153_STR,	WI_INTERSIL },
15693825Simp	{ WI_NIC_P2_SST_ID,		WI_NIC_P2_SST_STR,	WI_INTERSIL },
15793825Simp	{ WI_NIC_EVB2_SST_ID,		WI_NIC_EVB2_SST_STR,	WI_INTERSIL },
15893825Simp	{ WI_NIC_3842_EVA_ID,		WI_NIC_3842_EVA_STR,	WI_INTERSIL },
15993825Simp	{ WI_NIC_3842_PCMCIA_AMD_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16093825Simp	{ WI_NIC_3842_PCMCIA_SST_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16193825Simp	{ WI_NIC_3842_PCMCIA_ATM_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16293825Simp	{ WI_NIC_3842_MINI_AMD_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16393825Simp	{ WI_NIC_3842_MINI_SST_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16493825Simp	{ WI_NIC_3842_MINI_ATM_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16593825Simp	{ WI_NIC_3842_PCI_AMD_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
16693825Simp	{ WI_NIC_3842_PCI_SST_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
16793825Simp	{ WI_NIC_3842_PCI_ATM_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
16893825Simp	{ WI_NIC_P3_PCMCIA_AMD_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
16993825Simp	{ WI_NIC_P3_PCMCIA_SST_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
17093825Simp	{ WI_NIC_P3_MINI_AMD_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
17193825Simp	{ WI_NIC_P3_MINI_SST_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
17293825Simp	{ 0,	NULL,	0 },
17393825Simp};
17493825Simp
17593611Simpint
17693611Simpwi_generic_detach(dev)
17753702Swpaul	device_t		dev;
17846492Swpaul{
17946492Swpaul	struct wi_softc		*sc;
18046492Swpaul	struct ifnet		*ifp;
18146492Swpaul
18253702Swpaul	sc = device_get_softc(dev);
18367092Swpaul	WI_LOCK(sc);
18446492Swpaul	ifp = &sc->arpcom.ac_if;
18546492Swpaul
18646492Swpaul	if (sc->wi_gone) {
18753702Swpaul		device_printf(dev, "already unloaded\n");
18867092Swpaul		WI_UNLOCK(sc);
18953702Swpaul		return(ENODEV);
19046492Swpaul	}
19146492Swpaul
19253702Swpaul	wi_stop(sc);
19358274Srwatson
19477217Sphk	/* Delete all remaining media. */
19577217Sphk	ifmedia_removeall(&sc->ifmedia);
19677217Sphk
19763090Sarchie	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
19854277Swpaul	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
19953702Swpaul	wi_free(dev);
20046492Swpaul	sc->wi_gone = 1;
20146492Swpaul
20267092Swpaul	WI_UNLOCK(sc);
20367092Swpaul	mtx_destroy(&sc->wi_mtx);
20446492Swpaul
20546492Swpaul	return(0);
20646492Swpaul}
20746492Swpaul
20893611Simpint
20974906Salfredwi_generic_attach(device_t dev)
21074906Salfred{
21174906Salfred	struct wi_softc		*sc;
21274906Salfred	struct wi_ltv_macaddr	mac;
21374906Salfred	struct wi_ltv_gen	gen;
21474906Salfred	struct ifnet		*ifp;
21574906Salfred	int			error;
21674906Salfred
21774906Salfred	sc = device_get_softc(dev);
21874906Salfred	ifp = &sc->arpcom.ac_if;
21974906Salfred
22053702Swpaul	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
22174998Swpaul	    wi_intr, sc, &sc->wi_intrhand);
22253702Swpaul
22353702Swpaul	if (error) {
22453702Swpaul		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
22553702Swpaul		wi_free(dev);
22653702Swpaul		return (error);
22753702Swpaul	}
22853702Swpaul
22993818Sjhb	mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
23093818Sjhb	    MTX_DEF | MTX_RECURSE);
23167092Swpaul	WI_LOCK(sc);
23267092Swpaul
23346492Swpaul	/* Reset the NIC. */
23446492Swpaul	wi_reset(sc);
23546492Swpaul
23676438Swpaul	/*
23776438Swpaul	 * Read the station address.
23876438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
23976438Swpaul	 * an error when trying to read it the first time, which causes
24076438Swpaul	 * the probe to fail.
24176438Swpaul	 */
24246492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
24346492Swpaul	mac.wi_len = 4;
24476457Sgrog	wi_read_record(sc, (struct wi_ltv_gen *)&mac);
24575150Simp	if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) {
24675149Simp		device_printf(dev, "mac read failed %d\n", error);
24775149Simp		wi_free(dev);
24875149Simp		return (error);
24975149Simp	}
25046492Swpaul	bcopy((char *)&mac.wi_mac_addr,
25146492Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
25246492Swpaul
25387383Simp	device_printf(dev, "802.11 address: %6D\n", sc->arpcom.ac_enaddr, ":");
25446492Swpaul
25593825Simp	wi_get_id(sc);
25687383Simp
25746492Swpaul	ifp->if_softc = sc;
25846492Swpaul	ifp->if_unit = sc->wi_unit;
25946492Swpaul	ifp->if_name = "wi";
26046492Swpaul	ifp->if_mtu = ETHERMTU;
26146492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
26246492Swpaul	ifp->if_ioctl = wi_ioctl;
26346492Swpaul	ifp->if_output = ether_output;
26446492Swpaul	ifp->if_start = wi_start;
26546492Swpaul	ifp->if_watchdog = wi_watchdog;
26646492Swpaul	ifp->if_init = wi_init;
26746492Swpaul	ifp->if_baudrate = 10000000;
26846492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
26946492Swpaul
27046492Swpaul	bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
27146492Swpaul	bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name,
27246492Swpaul	    sizeof(WI_DEFAULT_NODENAME) - 1);
27346492Swpaul
27446492Swpaul	bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
27546492Swpaul	bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name,
27646492Swpaul	    sizeof(WI_DEFAULT_NETNAME) - 1);
27746492Swpaul
27846492Swpaul	bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
27946492Swpaul	bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name,
28046492Swpaul	    sizeof(WI_DEFAULT_IBSS) - 1);
28146492Swpaul
28246492Swpaul	sc->wi_portnum = WI_DEFAULT_PORT;
28374139Sassar	sc->wi_ptype = WI_PORTTYPE_BSS;
28446492Swpaul	sc->wi_ap_density = WI_DEFAULT_AP_DENSITY;
28546492Swpaul	sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH;
28646492Swpaul	sc->wi_tx_rate = WI_DEFAULT_TX_RATE;
28746492Swpaul	sc->wi_max_data_len = WI_DEFAULT_DATALEN;
28846492Swpaul	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
28946611Swpaul	sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED;
29046611Swpaul	sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP;
29191695Simp	sc->wi_roaming = WI_DEFAULT_ROAMING;
29291695Simp	sc->wi_authtype = WI_DEFAULT_AUTHTYPE;
29346492Swpaul
29446563Swpaul	/*
29546563Swpaul	 * Read the default channel from the NIC. This may vary
29646563Swpaul	 * depending on the country where the NIC was purchased, so
29746563Swpaul	 * we can't hard-code a default and expect it to work for
29846563Swpaul	 * everyone.
29946563Swpaul	 */
30046563Swpaul	gen.wi_type = WI_RID_OWN_CHNL;
30146563Swpaul	gen.wi_len = 2;
30246563Swpaul	wi_read_record(sc, &gen);
30346563Swpaul	sc->wi_channel = gen.wi_val;
30446563Swpaul
30556965Swpaul	/*
30656965Swpaul	 * Find out if we support WEP on this card.
30756965Swpaul	 */
30856965Swpaul	gen.wi_type = WI_RID_WEP_AVAIL;
30956965Swpaul	gen.wi_len = 2;
31056965Swpaul	wi_read_record(sc, &gen);
31156965Swpaul	sc->wi_has_wep = gen.wi_val;
31256965Swpaul
31370073Swpaul	if (bootverbose) {
31470073Swpaul		device_printf(sc->dev,
31587599Sobrien				"%s:wi_has_wep = %d\n",
31687599Sobrien				__func__, sc->wi_has_wep);
31770073Swpaul	}
31870073Swpaul
31946492Swpaul	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
32046492Swpaul
32146492Swpaul	wi_init(sc);
32246492Swpaul	wi_stop(sc);
32346492Swpaul
32477217Sphk	ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status);
32577217Sphk	/* XXX: Should read from card capabilities */
32677217Sphk#define ADD(m, c)       ifmedia_add(&sc->ifmedia, (m), (c), NULL)
32777217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1,
32877217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
32977217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0);
33077217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2,
33177217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
33277217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0);
33377217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5,
33477217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
33577217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0);
33677217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11,
33777217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
33877217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0);
33977217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
34077217Sphk		IFM_IEEE80211_ADHOC, 0), 0);
34177217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0);
34277217Sphk#undef	ADD
34377217Sphk	ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
34477217Sphk	    0, 0));
34577217Sphk
34677217Sphk
34746492Swpaul	/*
34863090Sarchie	 * Call MI attach routine.
34946492Swpaul	 */
35063090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
35153702Swpaul	callout_handle_init(&sc->wi_stat_ch);
35267092Swpaul	WI_UNLOCK(sc);
35346492Swpaul
35446492Swpaul	return(0);
35546492Swpaul}
35646492Swpaul
35787383Simpstatic void
35893825Simpwi_get_id(sc)
35987383Simp	struct wi_softc *sc;
36087383Simp{
36187383Simp	struct wi_ltv_ver       ver;
36293825Simp	struct wi_card_ident	*id;
36387383Simp
36487383Simp	/* getting chip identity */
36587383Simp	memset(&ver, 0, sizeof(ver));
36693567Simp	ver.wi_type = WI_RID_CARD_ID;
36787383Simp	ver.wi_len = 5;
36887383Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
36993733Simp	device_printf(sc->dev, "using ");
37093825Simp	sc->sc_firmware_type = WI_NOTYPE;
37193825Simp	for (id = wi_card_ident; id->card_name != NULL; id++) {
37293825Simp		if (le16toh(ver.wi_ver[0]) == id->card_id) {
37393825Simp			printf("%s", id->card_name);
37493825Simp			sc->sc_firmware_type = id->firm_type;
37593825Simp			break;
37693825Simp		}
37793825Simp	}
37893825Simp	if (sc->sc_firmware_type == WI_NOTYPE) {
37993825Simp		if (le16toh(ver.wi_ver[0]) & 0x8000) {
38093733Simp			printf("Unknown PRISM2 chip");
38193825Simp			sc->sc_firmware_type = WI_INTERSIL;
38293825Simp		} else {
38393733Simp			printf("Unknown Lucent chip");
38493825Simp			sc->sc_firmware_type = WI_LUCENT;
38593825Simp		}
38687383Simp	}
38787383Simp
38893733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
38993733Simp		/* get primary firmware version */
39093733Simp		memset(&ver, 0, sizeof(ver));
39193733Simp		ver.wi_type = WI_RID_PRI_IDENTITY;
39293733Simp		ver.wi_len = 5;
39393733Simp		wi_read_record(sc, (struct wi_ltv_gen *)&ver);
39493733Simp		ver.wi_ver[1] = le16toh(ver.wi_ver[1]);
39593733Simp		ver.wi_ver[2] = le16toh(ver.wi_ver[2]);
39693733Simp		ver.wi_ver[3] = le16toh(ver.wi_ver[3]);
39793733Simp		sc->sc_pri_firmware_ver = ver.wi_ver[2] * 10000 +
39893733Simp		    ver.wi_ver[3] * 100 + ver.wi_ver[1];
39993733Simp	}
40093733Simp
40193733Simp	/* get station firmware version */
40292457Simp	memset(&ver, 0, sizeof(ver));
40392457Simp	ver.wi_type = WI_RID_STA_IDENTITY;
40492457Simp	ver.wi_len = 5;
40592457Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
40692457Simp	ver.wi_ver[1] = le16toh(ver.wi_ver[1]);
40792457Simp	ver.wi_ver[2] = le16toh(ver.wi_ver[2]);
40892457Simp	ver.wi_ver[3] = le16toh(ver.wi_ver[3]);
40993733Simp	sc->sc_sta_firmware_ver = ver.wi_ver[2] * 10000 +
41093733Simp	    ver.wi_ver[3] * 100 + ver.wi_ver[1];
41193733Simp	if (sc->sc_firmware_type == WI_INTERSIL &&
41293733Simp	    (sc->sc_sta_firmware_ver == 10102 ||
41393733Simp	     sc->sc_sta_firmware_ver == 20102)) {
41493733Simp		struct wi_ltv_str sver;
41593733Simp		char *p;
41687383Simp
41793733Simp		memset(&sver, 0, sizeof(sver));
41893733Simp		sver.wi_type = WI_RID_SYMBOL_IDENTITY;
41993733Simp		sver.wi_len = 7;
42093733Simp		/* value should be "V2.00-11" */
42193733Simp		if (wi_read_record(sc, (struct wi_ltv_gen *)&sver) == 0 &&
42293733Simp		    *(p = (char *)sver.wi_str) == 'V' &&
42393733Simp		    p[2] == '.' && p[5] == '-' && p[8] == '\0') {
42493733Simp			sc->sc_firmware_type = WI_SYMBOL;
42593733Simp			sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 +
42693733Simp			    (p[3] - '0') * 1000 + (p[4] - '0') * 100 +
42793733Simp			    (p[6] - '0') * 10 + (p[7] - '0');
42893733Simp		}
42993733Simp	}
43093733Simp	printf("\n");
43193733Simp	device_printf(sc->dev, "%s Firmware: ",
43293733Simp	     sc->sc_firmware_type == WI_LUCENT ? "Lucent" :
43393733Simp	    (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil"));
43493733Simp
43593733Simp	/*
43693733Simp	 * The primary firmware is only valid on Prism based chipsets
43793733Simp	 * (INTERSIL or SYMBOL).
43893733Simp	 */
43993733Simp	if (sc->sc_firmware_type != WI_LUCENT)
44093733Simp	    printf("Primary %u.%02u.%02u, ", sc->sc_pri_firmware_ver / 10000,
44193733Simp		    (sc->sc_pri_firmware_ver % 10000) / 100,
44293733Simp		    sc->sc_pri_firmware_ver % 100);
44393733Simp	printf("Station %u.%02u.%02u\n",
44493733Simp	    sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100,
44593733Simp	    sc->sc_sta_firmware_ver % 100);
44687383Simp	return;
44787383Simp}
44887383Simp
44988546Salfredstatic void
45088546Salfredwi_rxeof(sc)
45146492Swpaul	struct wi_softc		*sc;
45246492Swpaul{
45346492Swpaul	struct ifnet		*ifp;
45446492Swpaul	struct ether_header	*eh;
45546492Swpaul	struct mbuf		*m;
45646492Swpaul	int			id;
45746492Swpaul
45846492Swpaul	ifp = &sc->arpcom.ac_if;
45946492Swpaul
46046492Swpaul	id = CSR_READ_2(sc, WI_RX_FID);
46146492Swpaul
46293359Simp	/*
46393359Simp	 * if we have the procframe flag set, disregard all this and just
46493359Simp	 * read the data from the device.
46593359Simp	 */
46693359Simp	if (sc->wi_procframe || sc->wi_debug.wi_monitor) {
46793359Simp		struct wi_frame		*rx_frame;
46893359Simp		int			datlen, hdrlen;
46946492Swpaul
47093359Simp		/* first allocate mbuf for packet storage */
47193359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
47293359Simp		if (m == NULL) {
47393359Simp			ifp->if_ierrors++;
47493359Simp			return;
47593359Simp		}
47693359Simp		MCLGET(m, M_DONTWAIT);
47793359Simp		if (!(m->m_flags & M_EXT)) {
47893359Simp			m_freem(m);
47993359Simp			ifp->if_ierrors++;
48093359Simp			return;
48193359Simp		}
48246492Swpaul
48393359Simp		m->m_pkthdr.rcvif = ifp;
48446492Swpaul
48593359Simp		/* now read wi_frame first so we know how much data to read */
48693359Simp		if (wi_read_data(sc, id, 0, mtod(m, caddr_t),
48793359Simp		    sizeof(struct wi_frame))) {
48893359Simp			m_freem(m);
48993359Simp			ifp->if_ierrors++;
49093359Simp			return;
49193359Simp		}
49246492Swpaul
49393359Simp		rx_frame = mtod(m, struct wi_frame *);
49493359Simp
49593359Simp		switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) {
49693359Simp		case 7:
49793359Simp			switch (rx_frame->wi_frame_ctl & WI_FCTL_FTYPE) {
49893359Simp			case WI_FTYPE_DATA:
49993359Simp				hdrlen = WI_DATA_HDRLEN;
50093359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
50193359Simp				break;
50293359Simp			case WI_FTYPE_MGMT:
50393359Simp				hdrlen = WI_MGMT_HDRLEN;
50493359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
50593359Simp				break;
50693359Simp			case WI_FTYPE_CTL:
50793359Simp				/*
50893359Simp				 * prism2 cards don't pass control packets
50993359Simp				 * down properly or consistently, so we'll only
51093359Simp				 * pass down the header.
51193359Simp				 */
51293359Simp				hdrlen = WI_CTL_HDRLEN;
51393359Simp				datlen = 0;
51493359Simp				break;
51593359Simp			default:
51693359Simp				device_printf(sc->dev, "received packet of "
51793359Simp				    "unknown type on port 7\n");
51893359Simp				m_freem(m);
51993359Simp				ifp->if_ierrors++;
52093359Simp				return;
52193359Simp			}
52293359Simp			break;
52393359Simp		case 0:
52493359Simp			hdrlen = WI_DATA_HDRLEN;
52593359Simp			datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
52693359Simp			break;
52793359Simp		default:
52893359Simp			device_printf(sc->dev, "received packet on invalid "
52993359Simp			    "port (wi_status=0x%x)\n", rx_frame->wi_status);
53093359Simp			m_freem(m);
53193359Simp			ifp->if_ierrors++;
53293359Simp			return;
53393359Simp		}
53493359Simp
53593359Simp		if ((hdrlen + datlen + 2) > MCLBYTES) {
53653702Swpaul			device_printf(sc->dev, "oversized packet received "
53753702Swpaul			    "(wi_dat_len=%d, wi_status=0x%x)\n",
53893359Simp			    datlen, rx_frame->wi_status);
53948553Swpaul			m_freem(m);
54048553Swpaul			ifp->if_ierrors++;
54148553Swpaul			return;
54248553Swpaul		}
54346492Swpaul
54493359Simp		if (wi_read_data(sc, id, hdrlen, mtod(m, caddr_t) + hdrlen,
54593359Simp		    datlen + 2)) {
54693359Simp			m_freem(m);
54793359Simp			ifp->if_ierrors++;
54893359Simp			return;
54959328Swpaul		}
55075373Salfred
55193359Simp		m->m_pkthdr.len = m->m_len = hdrlen + datlen;
55246492Swpaul
55393359Simp		ifp->if_ipackets++;
55493359Simp
55593359Simp		/* Handle BPF listeners. */
55693359Simp		if (ifp->if_bpf)
55793359Simp			bpf_mtap(ifp, m);
55893359Simp
55993359Simp		m_freem(m);
56093359Simp	} else {
56193359Simp		struct wi_frame		rx_frame;
56293359Simp
56393359Simp		/* First read in the frame header */
56493359Simp		if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame,
56593359Simp		    sizeof(rx_frame))) {
56646492Swpaul			ifp->if_ierrors++;
56746492Swpaul			return;
56846492Swpaul		}
56993359Simp
57093359Simp		if (rx_frame.wi_status & WI_STAT_ERRSTAT) {
57148553Swpaul			ifp->if_ierrors++;
57248553Swpaul			return;
57348553Swpaul		}
57446492Swpaul
57593359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
57693359Simp		if (m == NULL) {
57793359Simp			ifp->if_ierrors++;
57893359Simp			return;
57993359Simp		}
58093359Simp		MCLGET(m, M_DONTWAIT);
58193359Simp		if (!(m->m_flags & M_EXT)) {
58246492Swpaul			m_freem(m);
58346492Swpaul			ifp->if_ierrors++;
58446492Swpaul			return;
58546492Swpaul		}
58646492Swpaul
58793359Simp		eh = mtod(m, struct ether_header *);
58893359Simp		m->m_pkthdr.rcvif = ifp;
58946492Swpaul
59093359Simp		if (rx_frame.wi_status == WI_STAT_1042 ||
59193359Simp		    rx_frame.wi_status == WI_STAT_TUNNEL ||
59293359Simp		    rx_frame.wi_status == WI_STAT_WMP_MSG) {
59393359Simp			if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) {
59493359Simp				device_printf(sc->dev,
59593359Simp				    "oversized packet received "
59693359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
59793359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
59893359Simp				m_freem(m);
59993359Simp				ifp->if_ierrors++;
60093359Simp				return;
60193359Simp			}
60293359Simp			m->m_pkthdr.len = m->m_len =
60393359Simp			    rx_frame.wi_dat_len + WI_SNAPHDR_LEN;
60493359Simp
60593359Simp#if 0
60693359Simp			bcopy((char *)&rx_frame.wi_addr1,
60793359Simp			    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
60893359Simp			if (sc->wi_ptype == WI_PORTTYPE_ADHOC) {
60993359Simp				bcopy((char *)&rx_frame.wi_addr2,
61093359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
61193359Simp			} else {
61293359Simp				bcopy((char *)&rx_frame.wi_addr3,
61393359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
61493359Simp			}
61593359Simp#else
61693359Simp			bcopy((char *)&rx_frame.wi_dst_addr,
61793359Simp				(char *)&eh->ether_dhost, ETHER_ADDR_LEN);
61893359Simp			bcopy((char *)&rx_frame.wi_src_addr,
61993359Simp				(char *)&eh->ether_shost, ETHER_ADDR_LEN);
62093359Simp#endif
62193359Simp
62293359Simp			bcopy((char *)&rx_frame.wi_type,
62393359Simp			    (char *)&eh->ether_type, ETHER_TYPE_LEN);
62493359Simp
62593359Simp			if (wi_read_data(sc, id, WI_802_11_OFFSET,
62693359Simp			    mtod(m, caddr_t) + sizeof(struct ether_header),
62793359Simp			    m->m_len + 2)) {
62893359Simp				m_freem(m);
62993359Simp				ifp->if_ierrors++;
63093359Simp				return;
63193359Simp			}
63293359Simp		} else {
63393359Simp			if((rx_frame.wi_dat_len +
63493359Simp			    sizeof(struct ether_header)) > MCLBYTES) {
63593359Simp				device_printf(sc->dev,
63693359Simp				    "oversized packet received "
63793359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
63893359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
63993359Simp				m_freem(m);
64093359Simp				ifp->if_ierrors++;
64193359Simp				return;
64293359Simp			}
64393359Simp			m->m_pkthdr.len = m->m_len =
64493359Simp			    rx_frame.wi_dat_len + sizeof(struct ether_header);
64593359Simp
64693359Simp			if (wi_read_data(sc, id, WI_802_3_OFFSET,
64793359Simp			    mtod(m, caddr_t), m->m_len + 2)) {
64893359Simp				m_freem(m);
64993359Simp				ifp->if_ierrors++;
65093359Simp				return;
65193359Simp			}
65293359Simp		}
65393359Simp
65493359Simp		ifp->if_ipackets++;
65593359Simp
65693359Simp		/* Receive packet. */
65793359Simp		m_adj(m, sizeof(struct ether_header));
65853702Swpaul#ifdef WICACHE
65993359Simp		wi_cache_store(sc, eh, m, rx_frame.wi_q_info);
66053702Swpaul#endif
66193359Simp		ether_input(ifp, eh, m);
66293359Simp	}
66346492Swpaul}
66446492Swpaul
66588546Salfredstatic void
66688546Salfredwi_txeof(sc, status)
66746492Swpaul	struct wi_softc		*sc;
66846492Swpaul	int			status;
66946492Swpaul{
67046492Swpaul	struct ifnet		*ifp;
67146492Swpaul
67246492Swpaul	ifp = &sc->arpcom.ac_if;
67346492Swpaul
67446492Swpaul	ifp->if_timer = 0;
67546492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
67646492Swpaul
67746492Swpaul	if (status & WI_EV_TX_EXC)
67846492Swpaul		ifp->if_oerrors++;
67946492Swpaul	else
68046492Swpaul		ifp->if_opackets++;
68146492Swpaul
68246492Swpaul	return;
68346492Swpaul}
68446492Swpaul
68588546Salfredvoid
68688546Salfredwi_inquire(xsc)
68746492Swpaul	void			*xsc;
68846492Swpaul{
68946492Swpaul	struct wi_softc		*sc;
69046492Swpaul	struct ifnet		*ifp;
69146492Swpaul
69246492Swpaul	sc = xsc;
69346492Swpaul	ifp = &sc->arpcom.ac_if;
69446492Swpaul
69546492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
69646492Swpaul
69746492Swpaul	/* Don't do this while we're transmitting */
69846492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
69946492Swpaul		return;
70046492Swpaul
70192457Simp	wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0);
70246492Swpaul
70346492Swpaul	return;
70446492Swpaul}
70546492Swpaul
70688546Salfredvoid
70788546Salfredwi_update_stats(sc)
70846492Swpaul	struct wi_softc		*sc;
70946492Swpaul{
71046492Swpaul	struct wi_ltv_gen	gen;
71146492Swpaul	u_int16_t		id;
71246492Swpaul	struct ifnet		*ifp;
71346492Swpaul	u_int32_t		*ptr;
71475373Salfred	int			len, i;
71546492Swpaul	u_int16_t		t;
71646492Swpaul
71746492Swpaul	ifp = &sc->arpcom.ac_if;
71846492Swpaul
71946492Swpaul	id = CSR_READ_2(sc, WI_INFO_FID);
72046492Swpaul
72146492Swpaul	wi_read_data(sc, id, 0, (char *)&gen, 4);
72246492Swpaul
72393359Simp	/*
72493359Simp	 * if we just got our scan results, copy it over into the scan buffer
72593359Simp	 * so we can return it to anyone that asks for it. (add a little
72693359Simp	 * compatibility with the prism2 scanning mechanism)
72793359Simp	 */
72893359Simp	if (gen.wi_type == WI_INFO_SCAN_RESULTS)
72993359Simp	{
73093359Simp		sc->wi_scanbuf_len = gen.wi_len;
73193359Simp		wi_read_data(sc, id, 4, (char *)sc->wi_scanbuf,
73293359Simp		    sc->wi_scanbuf_len * 2);
73393359Simp
73446492Swpaul		return;
73593359Simp	}
73693359Simp	else if (gen.wi_type != WI_INFO_COUNTERS)
73793359Simp		return;
73846492Swpaul
73975373Salfred	len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ?
74075373Salfred		gen.wi_len - 1 : sizeof(sc->wi_stats) / 4;
74146492Swpaul	ptr = (u_int32_t *)&sc->wi_stats;
74246492Swpaul
74375373Salfred	for (i = 0; i < len - 1; i++) {
74446492Swpaul		t = CSR_READ_2(sc, WI_DATA1);
74546492Swpaul#ifdef WI_HERMES_STATS_WAR
74646492Swpaul		if (t > 0xF000)
74746492Swpaul			t = ~t & 0xFFFF;
74846492Swpaul#endif
74946492Swpaul		ptr[i] += t;
75046492Swpaul	}
75146492Swpaul
75246492Swpaul	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
75346492Swpaul	    sc->wi_stats.wi_tx_multi_retries +
75446492Swpaul	    sc->wi_stats.wi_tx_retry_limit;
75546492Swpaul
75646492Swpaul	return;
75746492Swpaul}
75846492Swpaul
75988546Salfredstatic void
76088546Salfredwi_intr(xsc)
76153702Swpaul	void		*xsc;
76246492Swpaul{
76353702Swpaul	struct wi_softc		*sc = xsc;
76446492Swpaul	struct ifnet		*ifp;
76546492Swpaul	u_int16_t		status;
76646492Swpaul
76767092Swpaul	WI_LOCK(sc);
76867092Swpaul
76946492Swpaul	ifp = &sc->arpcom.ac_if;
77046492Swpaul
77175373Salfred	if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) {
77246492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
77346492Swpaul		CSR_WRITE_2(sc, WI_INT_EN, 0);
77467092Swpaul		WI_UNLOCK(sc);
77546492Swpaul		return;
77646492Swpaul	}
77746492Swpaul
77846492Swpaul	/* Disable interrupts. */
77946492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
78046492Swpaul
78146492Swpaul	status = CSR_READ_2(sc, WI_EVENT_STAT);
78246492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS);
78346492Swpaul
78446492Swpaul	if (status & WI_EV_RX) {
78546492Swpaul		wi_rxeof(sc);
78646492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
78746492Swpaul	}
78846492Swpaul
78946492Swpaul	if (status & WI_EV_TX) {
79046492Swpaul		wi_txeof(sc, status);
79146492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX);
79246492Swpaul	}
79346492Swpaul
79446492Swpaul	if (status & WI_EV_ALLOC) {
79546492Swpaul		int			id;
79675373Salfred
79746492Swpaul		id = CSR_READ_2(sc, WI_ALLOC_FID);
79846492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
79946492Swpaul		if (id == sc->wi_tx_data_id)
80046492Swpaul			wi_txeof(sc, status);
80146492Swpaul	}
80246492Swpaul
80346492Swpaul	if (status & WI_EV_INFO) {
80446492Swpaul		wi_update_stats(sc);
80546492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
80646492Swpaul	}
80746492Swpaul
80846492Swpaul	if (status & WI_EV_TX_EXC) {
80946492Swpaul		wi_txeof(sc, status);
81046492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
81146492Swpaul	}
81246492Swpaul
81346492Swpaul	if (status & WI_EV_INFO_DROP) {
81446492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP);
81546492Swpaul	}
81646492Swpaul
81746492Swpaul	/* Re-enable interrupts. */
81846492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
81946492Swpaul
82075373Salfred	if (ifp->if_snd.ifq_head != NULL) {
82146492Swpaul		wi_start(ifp);
82275373Salfred	}
82346492Swpaul
82467092Swpaul	WI_UNLOCK(sc);
82567092Swpaul
82646492Swpaul	return;
82746492Swpaul}
82846492Swpaul
82988546Salfredstatic int
83092457Simpwi_cmd(sc, cmd, val0, val1, val2)
83146492Swpaul	struct wi_softc		*sc;
83246492Swpaul	int			cmd;
83392457Simp	int			val0;
83492457Simp	int			val1;
83592457Simp	int			val2;
83646492Swpaul{
83746492Swpaul	int			i, s = 0;
83846492Swpaul
83970073Swpaul	/* wait for the busy bit to clear */
84075331Simp	for (i = 500; i > 0; i--) {	/* 5s */
84170073Swpaul		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) {
84270073Swpaul			break;
84370073Swpaul		}
84470073Swpaul		DELAY(10*1000);	/* 10 m sec */
84570073Swpaul	}
84675229Salfred	if (i == 0) {
84790580Sbrooks		device_printf(sc->dev, "wi_cmd: busy bit won't clear.\n" );
84870073Swpaul		return(ETIMEDOUT);
84970073Swpaul	}
85070073Swpaul
85192457Simp	CSR_WRITE_2(sc, WI_PARAM0, val0);
85292457Simp	CSR_WRITE_2(sc, WI_PARAM1, val1);
85392457Simp	CSR_WRITE_2(sc, WI_PARAM2, val2);
85446492Swpaul	CSR_WRITE_2(sc, WI_COMMAND, cmd);
85546492Swpaul
85646492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
85746492Swpaul		/*
85846492Swpaul		 * Wait for 'command complete' bit to be
85946492Swpaul		 * set in the event status register.
86046492Swpaul		 */
86190580Sbrooks		s = CSR_READ_2(sc, WI_EVENT_STAT);
86290580Sbrooks		if (s & WI_EV_CMD) {
86346492Swpaul			/* Ack the event and read result code. */
86446492Swpaul			s = CSR_READ_2(sc, WI_STATUS);
86546492Swpaul			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
86646492Swpaul#ifdef foo
86746492Swpaul			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
86846492Swpaul				return(EIO);
86946492Swpaul#endif
87046492Swpaul			if (s & WI_STAT_CMD_RESULT)
87146492Swpaul				return(EIO);
87246492Swpaul			break;
87346492Swpaul		}
87490580Sbrooks		DELAY(WI_DELAY);
87546492Swpaul	}
87646492Swpaul
87790580Sbrooks	if (i == WI_TIMEOUT) {
87890580Sbrooks		device_printf(sc->dev,
87992457Simp		    "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
88046492Swpaul		return(ETIMEDOUT);
88190580Sbrooks	}
88246492Swpaul
88346492Swpaul	return(0);
88446492Swpaul}
88546492Swpaul
88688546Salfredstatic void
88788546Salfredwi_reset(sc)
88846492Swpaul	struct wi_softc		*sc;
88946492Swpaul{
89075331Simp#define WI_INIT_TRIES 5
89175150Simp	int i;
89293733Simp	int tries;
89375149Simp
89493733Simp	/* Symbol firmware cannot be initialized more than once */
89593733Simp	if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_enabled)
89693733Simp		return;
89793733Simp	if (sc->sc_firmware_type == WI_SYMBOL)
89893733Simp		tries = 1;
89993733Simp	else
90093733Simp		tries = WI_INIT_TRIES;
90193733Simp
90293733Simp	for (i = 0; i < tries; i++) {
90392457Simp		if (wi_cmd(sc, WI_CMD_INI, 0, 0, 0) == 0)
90475149Simp			break;
90590580Sbrooks		DELAY(WI_DELAY * 1000);
90675149Simp	}
90775331Simp	if (i == WI_INIT_TRIES)
90853702Swpaul		device_printf(sc->dev, "init failed\n");
90975373Salfred
91046492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
91146492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
91246492Swpaul
91346492Swpaul	/* Calibrate timer. */
91446492Swpaul	WI_SETVAL(WI_RID_TICK_TIME, 8);
91570073Swpaul
91693733Simp	sc->sc_enabled = 1;
91793733Simp
91846492Swpaul	return;
91946492Swpaul}
92046492Swpaul
92146492Swpaul/*
92246492Swpaul * Read an LTV record from the NIC.
92346492Swpaul */
92488546Salfredstatic int
92588546Salfredwi_read_record(sc, ltv)
92646492Swpaul	struct wi_softc		*sc;
92746492Swpaul	struct wi_ltv_gen	*ltv;
92846492Swpaul{
92946492Swpaul	u_int16_t		*ptr;
93046492Swpaul	int			i, len, code;
93170073Swpaul	struct wi_ltv_gen	*oltv, p2ltv;
93246492Swpaul
93370073Swpaul	oltv = ltv;
93493733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
93570073Swpaul		switch (ltv->wi_type) {
93670073Swpaul		case WI_RID_ENCRYPTION:
93770073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
93870073Swpaul			p2ltv.wi_len = 2;
93970073Swpaul			ltv = &p2ltv;
94070073Swpaul			break;
94170073Swpaul		case WI_RID_TX_CRYPT_KEY:
94270073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
94370073Swpaul			p2ltv.wi_len = 2;
94470073Swpaul			ltv = &p2ltv;
94570073Swpaul			break;
94670073Swpaul		}
94770073Swpaul	}
94870073Swpaul
94946492Swpaul	/* Tell the NIC to enter record read mode. */
95092457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type, 0, 0))
95146492Swpaul		return(EIO);
95246492Swpaul
95347789Swpaul	/* Seek to the record. */
95447789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
95547789Swpaul		return(EIO);
95646492Swpaul
95746492Swpaul	/*
95846492Swpaul	 * Read the length and record type and make sure they
95946492Swpaul	 * match what we expect (this verifies that we have enough
96047401Swpaul	 * room to hold all of the returned data).
96146492Swpaul	 */
96246492Swpaul	len = CSR_READ_2(sc, WI_DATA1);
96346492Swpaul	if (len > ltv->wi_len)
96446492Swpaul		return(ENOSPC);
96546492Swpaul	code = CSR_READ_2(sc, WI_DATA1);
96646492Swpaul	if (code != ltv->wi_type)
96746492Swpaul		return(EIO);
96846492Swpaul
96946492Swpaul	ltv->wi_len = len;
97046492Swpaul	ltv->wi_type = code;
97146492Swpaul
97246492Swpaul	/* Now read the data. */
97346492Swpaul	ptr = &ltv->wi_val;
97446492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
97546492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
97646492Swpaul
97793733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
97870073Swpaul		switch (oltv->wi_type) {
97970073Swpaul		case WI_RID_TX_RATE:
98070073Swpaul		case WI_RID_CUR_TX_RATE:
98170073Swpaul			switch (ltv->wi_val) {
98270073Swpaul			case 1: oltv->wi_val = 1; break;
98370073Swpaul			case 2: oltv->wi_val = 2; break;
98470073Swpaul			case 3:	oltv->wi_val = 6; break;
98570073Swpaul			case 4: oltv->wi_val = 5; break;
98670073Swpaul			case 7: oltv->wi_val = 7; break;
98770073Swpaul			case 8: oltv->wi_val = 11; break;
98870073Swpaul			case 15: oltv->wi_val = 3; break;
98970073Swpaul			default: oltv->wi_val = 0x100 + ltv->wi_val; break;
99070073Swpaul			}
99170073Swpaul			break;
99270073Swpaul		case WI_RID_ENCRYPTION:
99370073Swpaul			oltv->wi_len = 2;
99470073Swpaul			if (ltv->wi_val & 0x01)
99570073Swpaul				oltv->wi_val = 1;
99670073Swpaul			else
99770073Swpaul				oltv->wi_val = 0;
99870073Swpaul			break;
99970073Swpaul		case WI_RID_TX_CRYPT_KEY:
100070073Swpaul			oltv->wi_len = 2;
100170073Swpaul			oltv->wi_val = ltv->wi_val;
100270073Swpaul			break;
100391695Simp		case WI_RID_AUTH_CNTL:
100491695Simp                        oltv->wi_len = 2;
100591695Simp			if (le16toh(ltv->wi_val) & 0x01)
100691695Simp				oltv->wi_val = htole16(1);
100791695Simp			else if (le16toh(ltv->wi_val) & 0x02)
100891695Simp				oltv->wi_val = htole16(2);
100991695Simp			break;
101070073Swpaul		}
101170073Swpaul	}
101270073Swpaul
101346492Swpaul	return(0);
101446492Swpaul}
101546492Swpaul
101646492Swpaul/*
101746492Swpaul * Same as read, except we inject data instead of reading it.
101846492Swpaul */
101988546Salfredstatic int
102088546Salfredwi_write_record(sc, ltv)
102146492Swpaul	struct wi_softc		*sc;
102246492Swpaul	struct wi_ltv_gen	*ltv;
102346492Swpaul{
102446492Swpaul	u_int16_t		*ptr;
102546492Swpaul	int			i;
102670073Swpaul	struct wi_ltv_gen	p2ltv;
102746492Swpaul
102893733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
102970073Swpaul		switch (ltv->wi_type) {
103070073Swpaul		case WI_RID_TX_RATE:
103170073Swpaul			p2ltv.wi_type = WI_RID_TX_RATE;
103270073Swpaul			p2ltv.wi_len = 2;
103370073Swpaul			switch (ltv->wi_val) {
103470073Swpaul			case 1: p2ltv.wi_val = 1; break;
103570073Swpaul			case 2: p2ltv.wi_val = 2; break;
103670073Swpaul			case 3:	p2ltv.wi_val = 15; break;
103770073Swpaul			case 5: p2ltv.wi_val = 4; break;
103870073Swpaul			case 6: p2ltv.wi_val = 3; break;
103970073Swpaul			case 7: p2ltv.wi_val = 7; break;
104070073Swpaul			case 11: p2ltv.wi_val = 8; break;
104170073Swpaul			default: return EINVAL;
104270073Swpaul			}
104370073Swpaul			ltv = &p2ltv;
104470073Swpaul			break;
104570073Swpaul		case WI_RID_ENCRYPTION:
104670073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
104770073Swpaul			p2ltv.wi_len = 2;
104893825Simp			if (le16toh(ltv->wi_val))
104993825Simp				p2ltv.wi_val =htole16(PRIVACY_INVOKED |
105093825Simp				    EXCLUDE_UNENCRYPTED);
105170073Swpaul			else
105293825Simp				p2ltv.wi_val =
105393825Simp				    htole16(HOST_ENCRYPT | HOST_DECRYPT);
105470073Swpaul			ltv = &p2ltv;
105570073Swpaul			break;
105670073Swpaul		case WI_RID_TX_CRYPT_KEY:
105770073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
105870073Swpaul			p2ltv.wi_len = 2;
105970073Swpaul			p2ltv.wi_val = ltv->wi_val;
106070073Swpaul			ltv = &p2ltv;
106170073Swpaul			break;
106270073Swpaul		case WI_RID_DEFLT_CRYPT_KEYS:
106370073Swpaul		    {
106470073Swpaul			int error;
106591548Sbrooks			int keylen;
106670073Swpaul			struct wi_ltv_str	ws;
106774998Swpaul			struct wi_ltv_keys	*wk =
106874998Swpaul			    (struct wi_ltv_keys *)ltv;
106974998Swpaul
107091548Sbrooks			keylen = wk->wi_keys[sc->wi_tx_key].wi_keylen;
107191548Sbrooks
107270073Swpaul			for (i = 0; i < 4; i++) {
107391548Sbrooks				bzero(&ws, sizeof(ws));
107491548Sbrooks				ws.wi_len = (keylen > 5) ? 8 : 4;
107570073Swpaul				ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i;
107674998Swpaul				memcpy(ws.wi_str,
107791548Sbrooks				    &wk->wi_keys[i].wi_keydat, keylen);
107870073Swpaul				error = wi_write_record(sc,
107970073Swpaul				    (struct wi_ltv_gen *)&ws);
108070073Swpaul				if (error)
108170073Swpaul					return error;
108270073Swpaul			}
108370073Swpaul			return 0;
108470073Swpaul		    }
108591695Simp		case WI_RID_AUTH_CNTL:
108691695Simp			p2ltv.wi_type = WI_RID_AUTH_CNTL;
108791695Simp			p2ltv.wi_len = 2;
108891695Simp			if (le16toh(ltv->wi_val) == 1)
108991695Simp				p2ltv.wi_val = htole16(0x01);
109091695Simp			else if (le16toh(ltv->wi_val) == 2)
109191695Simp				p2ltv.wi_val = htole16(0x02);
109291695Simp			ltv = &p2ltv;
109391695Simp			break;
109470073Swpaul		}
109570073Swpaul	}
109670073Swpaul
109747789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
109847789Swpaul		return(EIO);
109946492Swpaul
110046492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len);
110146492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type);
110246492Swpaul
110346492Swpaul	ptr = &ltv->wi_val;
110446492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
110546492Swpaul		CSR_WRITE_2(sc, WI_DATA1, ptr[i]);
110646492Swpaul
110792457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type, 0, 0))
110846492Swpaul		return(EIO);
110946492Swpaul
111046492Swpaul	return(0);
111146492Swpaul}
111246492Swpaul
111388546Salfredstatic int
111488546Salfredwi_seek(sc, id, off, chan)
111546492Swpaul	struct wi_softc		*sc;
111646492Swpaul	int			id, off, chan;
111746492Swpaul{
111846492Swpaul	int			i;
111946492Swpaul	int			selreg, offreg;
112075373Salfred	int			status;
112146492Swpaul
112246492Swpaul	switch (chan) {
112346492Swpaul	case WI_BAP0:
112446492Swpaul		selreg = WI_SEL0;
112546492Swpaul		offreg = WI_OFF0;
112646492Swpaul		break;
112746492Swpaul	case WI_BAP1:
112846492Swpaul		selreg = WI_SEL1;
112946492Swpaul		offreg = WI_OFF1;
113046492Swpaul		break;
113146492Swpaul	default:
113253702Swpaul		device_printf(sc->dev, "invalid data path: %x\n", chan);
113346492Swpaul		return(EIO);
113446492Swpaul	}
113546492Swpaul
113646492Swpaul	CSR_WRITE_2(sc, selreg, id);
113746492Swpaul	CSR_WRITE_2(sc, offreg, off);
113846492Swpaul
113946492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
114075373Salfred		status = CSR_READ_2(sc, offreg);
114175373Salfred		if (!(status & (WI_OFF_BUSY|WI_OFF_ERR)))
114246492Swpaul			break;
114390580Sbrooks		DELAY(WI_DELAY);
114446492Swpaul	}
114546492Swpaul
114675373Salfred	if (i == WI_TIMEOUT) {
114775373Salfred		device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n",
114875373Salfred			id, off, status);
114946492Swpaul		return(ETIMEDOUT);
115075373Salfred	}
115146492Swpaul
115246492Swpaul	return(0);
115346492Swpaul}
115446492Swpaul
115588546Salfredstatic int
115688546Salfredwi_read_data(sc, id, off, buf, len)
115746492Swpaul	struct wi_softc		*sc;
115846492Swpaul	int			id, off;
115946492Swpaul	caddr_t			buf;
116046492Swpaul	int			len;
116146492Swpaul{
116246492Swpaul	int			i;
116346492Swpaul	u_int16_t		*ptr;
116446492Swpaul
116546492Swpaul	if (wi_seek(sc, id, off, WI_BAP1))
116646492Swpaul		return(EIO);
116746492Swpaul
116846492Swpaul	ptr = (u_int16_t *)buf;
116946492Swpaul	for (i = 0; i < len / 2; i++)
117046492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
117146492Swpaul
117246492Swpaul	return(0);
117346492Swpaul}
117446492Swpaul
117546492Swpaul/*
117646492Swpaul * According to the comments in the HCF Light code, there is a bug in
117746492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where
117846492Swpaul * the chip's internal autoincrement counter gets thrown off during
117946492Swpaul * data writes: the autoincrement is missed, causing one data word to
118046492Swpaul * be overwritten and subsequent words to be written to the wrong memory
118146492Swpaul * locations. The end result is that we could end up transmitting bogus
118246492Swpaul * frames without realizing it. The workaround for this is to write a
118346492Swpaul * couple of extra guard words after the end of the transfer, then
118446492Swpaul * attempt to read then back. If we fail to locate the guard words where
118546492Swpaul * we expect them, we preform the transfer over again.
118646492Swpaul */
118788546Salfredstatic int
118888546Salfredwi_write_data(sc, id, off, buf, len)
118946492Swpaul	struct wi_softc		*sc;
119046492Swpaul	int			id, off;
119146492Swpaul	caddr_t			buf;
119246492Swpaul	int			len;
119346492Swpaul{
119446492Swpaul	int			i;
119546492Swpaul	u_int16_t		*ptr;
119674838Salfred#ifdef WI_HERMES_AUTOINC_WAR
119774838Salfred	int			retries;
119846492Swpaul
119975373Salfred	retries = 512;
120046492Swpaulagain:
120146492Swpaul#endif
120246492Swpaul
120346492Swpaul	if (wi_seek(sc, id, off, WI_BAP0))
120446492Swpaul		return(EIO);
120546492Swpaul
120646492Swpaul	ptr = (u_int16_t *)buf;
120746492Swpaul	for (i = 0; i < (len / 2); i++)
120846492Swpaul		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
120946492Swpaul
121046492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
121146492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x1234);
121246492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x5678);
121346492Swpaul
121446492Swpaul	if (wi_seek(sc, id, off + len, WI_BAP0))
121546492Swpaul		return(EIO);
121646492Swpaul
121746492Swpaul	if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
121874998Swpaul	    CSR_READ_2(sc, WI_DATA0) != 0x5678) {
121974838Salfred		if (--retries >= 0)
122074838Salfred			goto again;
122174838Salfred		device_printf(sc->dev, "wi_write_data device timeout\n");
122274838Salfred		return (EIO);
122374838Salfred	}
122446492Swpaul#endif
122546492Swpaul
122646492Swpaul	return(0);
122746492Swpaul}
122846492Swpaul
122946492Swpaul/*
123046492Swpaul * Allocate a region of memory inside the NIC and zero
123146492Swpaul * it out.
123246492Swpaul */
123388546Salfredstatic int
123488546Salfredwi_alloc_nicmem(sc, len, id)
123546492Swpaul	struct wi_softc		*sc;
123646492Swpaul	int			len;
123746492Swpaul	int			*id;
123846492Swpaul{
123946492Swpaul	int			i;
124046492Swpaul
124192457Simp	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
124274998Swpaul		device_printf(sc->dev,
124374998Swpaul		    "failed to allocate %d bytes on NIC\n", len);
124446492Swpaul		return(ENOMEM);
124546492Swpaul	}
124646492Swpaul
124746492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
124846492Swpaul		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
124946492Swpaul			break;
125090580Sbrooks		DELAY(WI_DELAY);
125146492Swpaul	}
125246492Swpaul
125375373Salfred	if (i == WI_TIMEOUT) {
125475373Salfred		device_printf(sc->dev, "time out allocating memory on card\n");
125546492Swpaul		return(ETIMEDOUT);
125675373Salfred	}
125746492Swpaul
125846492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
125946492Swpaul	*id = CSR_READ_2(sc, WI_ALLOC_FID);
126046492Swpaul
126175373Salfred	if (wi_seek(sc, *id, 0, WI_BAP0)) {
126275373Salfred		device_printf(sc->dev, "seek failed while allocating memory on card\n");
126347789Swpaul		return(EIO);
126475373Salfred	}
126546492Swpaul
126646492Swpaul	for (i = 0; i < len / 2; i++)
126746492Swpaul		CSR_WRITE_2(sc, WI_DATA0, 0);
126846492Swpaul
126946492Swpaul	return(0);
127046492Swpaul}
127146492Swpaul
127288546Salfredstatic void
127388546Salfredwi_setmulti(sc)
127446492Swpaul	struct wi_softc		*sc;
127546492Swpaul{
127646492Swpaul	struct ifnet		*ifp;
127746492Swpaul	int			i = 0;
127846492Swpaul	struct ifmultiaddr	*ifma;
127946492Swpaul	struct wi_ltv_mcast	mcast;
128046492Swpaul
128146492Swpaul	ifp = &sc->arpcom.ac_if;
128246492Swpaul
128346492Swpaul	bzero((char *)&mcast, sizeof(mcast));
128446492Swpaul
128593756Simp	mcast.wi_type = WI_RID_MCAST_LIST;
128646492Swpaul	mcast.wi_len = (3 * 16) + 1;
128746492Swpaul
128846492Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
128946492Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
129046492Swpaul		return;
129146492Swpaul	}
129246492Swpaul
129372084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
129446492Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
129546492Swpaul			continue;
129646492Swpaul		if (i < 16) {
129746492Swpaul			bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
129846492Swpaul			    (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN);
129946492Swpaul			i++;
130046492Swpaul		} else {
130146492Swpaul			bzero((char *)&mcast, sizeof(mcast));
130246492Swpaul			break;
130346492Swpaul		}
130446492Swpaul	}
130546492Swpaul
130646492Swpaul	mcast.wi_len = (i * 3) + 1;
130746492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
130846492Swpaul
130946492Swpaul	return;
131046492Swpaul}
131146492Swpaul
131288546Salfredstatic void
131388546Salfredwi_setdef(sc, wreq)
131446492Swpaul	struct wi_softc		*sc;
131546492Swpaul	struct wi_req		*wreq;
131646492Swpaul{
131746492Swpaul	struct sockaddr_dl	*sdl;
131846492Swpaul	struct ifaddr		*ifa;
131946492Swpaul	struct ifnet		*ifp;
132046492Swpaul
132146492Swpaul	ifp = &sc->arpcom.ac_if;
132246492Swpaul
132346492Swpaul	switch(wreq->wi_type) {
132446492Swpaul	case WI_RID_MAC_NODE:
132583130Sjlemon		ifa = ifaddr_byindex(ifp->if_index);
132646492Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
132746492Swpaul		bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr,
132846492Swpaul		   ETHER_ADDR_LEN);
132946492Swpaul		bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN);
133046492Swpaul		break;
133146492Swpaul	case WI_RID_PORTTYPE:
133291695Simp		sc->wi_ptype = le16toh(wreq->wi_val[0]);
133346492Swpaul		break;
133446492Swpaul	case WI_RID_TX_RATE:
133591695Simp		sc->wi_tx_rate = le16toh(wreq->wi_val[0]);
133646492Swpaul		break;
133746492Swpaul	case WI_RID_MAX_DATALEN:
133891695Simp		sc->wi_max_data_len = le16toh(wreq->wi_val[0]);
133946492Swpaul		break;
134046492Swpaul	case WI_RID_RTS_THRESH:
134191695Simp		sc->wi_rts_thresh = le16toh(wreq->wi_val[0]);
134246492Swpaul		break;
134346492Swpaul	case WI_RID_SYSTEM_SCALE:
134491695Simp		sc->wi_ap_density = le16toh(wreq->wi_val[0]);
134546492Swpaul		break;
134646492Swpaul	case WI_RID_CREATE_IBSS:
134791695Simp		sc->wi_create_ibss = le16toh(wreq->wi_val[0]);
134846492Swpaul		break;
134946563Swpaul	case WI_RID_OWN_CHNL:
135091695Simp		sc->wi_channel = le16toh(wreq->wi_val[0]);
135146563Swpaul		break;
135246492Swpaul	case WI_RID_NODENAME:
135346492Swpaul		bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
135446492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30);
135546492Swpaul		break;
135646492Swpaul	case WI_RID_DESIRED_SSID:
135746492Swpaul		bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
135846492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30);
135946492Swpaul		break;
136046492Swpaul	case WI_RID_OWN_SSID:
136146492Swpaul		bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
136246492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30);
136346492Swpaul		break;
136446611Swpaul	case WI_RID_PM_ENABLED:
136591695Simp		sc->wi_pm_enabled = le16toh(wreq->wi_val[0]);
136646611Swpaul		break;
136791695Simp	case WI_RID_MICROWAVE_OVEN:
136891695Simp		sc->wi_mor_enabled = le16toh(wreq->wi_val[0]);
136991695Simp		break;
137046611Swpaul	case WI_RID_MAX_SLEEP:
137191695Simp		sc->wi_max_sleep = le16toh(wreq->wi_val[0]);
137246611Swpaul		break;
137391695Simp	case WI_RID_AUTH_CNTL:
137491695Simp		sc->wi_authtype = le16toh(wreq->wi_val[0]);
137591695Simp		break;
137691695Simp	case WI_RID_ROAMING_MODE:
137791695Simp		sc->wi_roaming = le16toh(wreq->wi_val[0]);
137891695Simp		break;
137956965Swpaul	case WI_RID_ENCRYPTION:
138091695Simp		sc->wi_use_wep = le16toh(wreq->wi_val[0]);
138156965Swpaul		break;
138256965Swpaul	case WI_RID_TX_CRYPT_KEY:
138391695Simp		sc->wi_tx_key = le16toh(wreq->wi_val[0]);
138456965Swpaul		break;
138556965Swpaul	case WI_RID_DEFLT_CRYPT_KEYS:
138656965Swpaul		bcopy((char *)wreq, (char *)&sc->wi_keys,
138756965Swpaul		    sizeof(struct wi_ltv_keys));
138856965Swpaul		break;
138946492Swpaul	default:
139046492Swpaul		break;
139146492Swpaul	}
139246492Swpaul
139346563Swpaul	/* Reinitialize WaveLAN. */
139446563Swpaul	wi_init(sc);
139546563Swpaul
139646492Swpaul	return;
139746492Swpaul}
139846492Swpaul
139988546Salfredstatic int
140088546Salfredwi_ioctl(ifp, command, data)
140146492Swpaul	struct ifnet		*ifp;
140246492Swpaul	u_long			command;
140346492Swpaul	caddr_t			data;
140446492Swpaul{
140567092Swpaul	int			error = 0;
140677217Sphk	int			len;
140777217Sphk	u_int8_t		tmpkey[14];
140877217Sphk	char			tmpssid[IEEE80211_NWID_LEN];
140946492Swpaul	struct wi_softc		*sc;
141046492Swpaul	struct wi_req		wreq;
141146492Swpaul	struct ifreq		*ifr;
141277217Sphk	struct ieee80211req	*ireq;
141393593Sjhb	struct thread		*td = curthread;
141446492Swpaul
141546492Swpaul	sc = ifp->if_softc;
141667092Swpaul	WI_LOCK(sc);
141746492Swpaul	ifr = (struct ifreq *)data;
141877217Sphk	ireq = (struct ieee80211req *)data;
141946492Swpaul
142061818Sroberto	if (sc->wi_gone) {
142161818Sroberto		error = ENODEV;
142261818Sroberto		goto out;
142361818Sroberto	}
142446492Swpaul
142546492Swpaul	switch(command) {
142646492Swpaul	case SIOCSIFADDR:
142746492Swpaul	case SIOCGIFADDR:
142846492Swpaul	case SIOCSIFMTU:
142946492Swpaul		error = ether_ioctl(ifp, command, data);
143046492Swpaul		break;
143146492Swpaul	case SIOCSIFFLAGS:
143246492Swpaul		if (ifp->if_flags & IFF_UP) {
143346492Swpaul			if (ifp->if_flags & IFF_RUNNING &&
143446492Swpaul			    ifp->if_flags & IFF_PROMISC &&
143546492Swpaul			    !(sc->wi_if_flags & IFF_PROMISC)) {
143646492Swpaul				WI_SETVAL(WI_RID_PROMISC, 1);
143746492Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
143846492Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
143946492Swpaul			    sc->wi_if_flags & IFF_PROMISC) {
144046492Swpaul				WI_SETVAL(WI_RID_PROMISC, 0);
144146492Swpaul			} else
144246492Swpaul				wi_init(sc);
144346492Swpaul		} else {
144446492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
144546492Swpaul				wi_stop(sc);
144646492Swpaul			}
144746492Swpaul		}
144846492Swpaul		sc->wi_if_flags = ifp->if_flags;
144946492Swpaul		error = 0;
145046492Swpaul		break;
145177217Sphk	case SIOCSIFMEDIA:
145277217Sphk	case SIOCGIFMEDIA:
145377217Sphk		error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
145477217Sphk		break;
145546492Swpaul	case SIOCADDMULTI:
145646492Swpaul	case SIOCDELMULTI:
145746492Swpaul		wi_setmulti(sc);
145846492Swpaul		error = 0;
145946492Swpaul		break;
146046492Swpaul	case SIOCGWAVELAN:
146146492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
146246492Swpaul		if (error)
146346492Swpaul			break;
146465581Swpaul		/* Don't show WEP keys to non-root users. */
146593593Sjhb		if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(td))
146665581Swpaul			break;
146746492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
146846492Swpaul			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
146946492Swpaul			    sizeof(sc->wi_stats));
147046492Swpaul			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
147156965Swpaul		} else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) {
147256965Swpaul			bcopy((char *)&sc->wi_keys, (char *)&wreq,
147356965Swpaul			    sizeof(struct wi_ltv_keys));
147453702Swpaul		}
147553702Swpaul#ifdef WICACHE
147653702Swpaul		else if (wreq.wi_type == WI_RID_ZERO_CACHE) {
147753702Swpaul			sc->wi_sigitems = sc->wi_nextitem = 0;
147853702Swpaul		} else if (wreq.wi_type == WI_RID_READ_CACHE) {
147953702Swpaul			char *pt = (char *)&wreq.wi_val;
148053702Swpaul			bcopy((char *)&sc->wi_sigitems,
148153702Swpaul			    (char *)pt, sizeof(int));
148253702Swpaul			pt += (sizeof (int));
148353702Swpaul			wreq.wi_len = sizeof(int) / 2;
148453702Swpaul			bcopy((char *)&sc->wi_sigcache, (char *)pt,
148553702Swpaul			    sizeof(struct wi_sigcache) * sc->wi_sigitems);
148653702Swpaul			wreq.wi_len += ((sizeof(struct wi_sigcache) *
148753702Swpaul			    sc->wi_sigitems) / 2) + 1;
148853702Swpaul		}
148953702Swpaul#endif
149093359Simp		else if (wreq.wi_type == WI_RID_PROCFRAME) {
149193359Simp			wreq.wi_len = 2;
149293359Simp			wreq.wi_val[0] = sc->wi_procframe;
149393359Simp		} else if (wreq.wi_type == WI_RID_PRISM2) {
149493359Simp			wreq.wi_len = 2;
149593733Simp			wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT;
149693733Simp		} else if (wreq.wi_type == WI_RID_SCAN_RES &&
149793733Simp		    sc->sc_firmware_type == WI_LUCENT) {
149893359Simp			memcpy((char *)wreq.wi_val, (char *)sc->wi_scanbuf,
149993359Simp			    sc->wi_scanbuf_len * 2);
150093359Simp			wreq.wi_len = sc->wi_scanbuf_len;
150193359Simp		} else {
150246492Swpaul			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
150346492Swpaul				error = EINVAL;
150446492Swpaul				break;
150546492Swpaul			}
150646492Swpaul		}
150746492Swpaul		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
150846492Swpaul		break;
150946492Swpaul	case SIOCSWAVELAN:
151093593Sjhb		if ((error = suser(td)))
151161818Sroberto			goto out;
151246492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
151346492Swpaul		if (error)
151446492Swpaul			break;
151546492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
151646492Swpaul			error = EINVAL;
151746492Swpaul			break;
151846492Swpaul		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
151946492Swpaul			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
152046492Swpaul			    wreq.wi_len);
152193359Simp		} else if (wreq.wi_type == WI_RID_PROCFRAME) {
152293359Simp			sc->wi_procframe = wreq.wi_val[0];
152393359Simp		/*
152493359Simp		 * if we're getting a scan request from a wavelan card
152593359Simp		 * (non-prism2), send out a cmd_inquire to the card to scan
152693359Simp		 * results for the scan will be received through the info
152793359Simp		 * interrupt handler. otherwise the scan request can be
152893359Simp		 * directly handled by a prism2 card's rid interface.
152993359Simp		 */
153093733Simp		} else if (wreq.wi_type == WI_RID_SCAN_REQ &&
153193733Simp		    sc->sc_firmware_type == WI_LUCENT) {
153293359Simp			wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
153346492Swpaul		} else {
153446492Swpaul			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
153546492Swpaul			if (!error)
153646492Swpaul				wi_setdef(sc, &wreq);
153746492Swpaul		}
153846492Swpaul		break;
153993359Simp	case SIOCGPRISM2DEBUG:
154093359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
154193359Simp		if (error)
154293359Simp			break;
154393733Simp		if (!(ifp->if_flags & IFF_RUNNING) ||
154493733Simp		    sc->sc_firmware_type == WI_LUCENT) {
154593359Simp			error = EIO;
154693359Simp			break;
154793359Simp		}
154893359Simp		error = wi_get_debug(sc, &wreq);
154993359Simp		if (error == 0)
155093359Simp			error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
155193359Simp		break;
155293359Simp	case SIOCSPRISM2DEBUG:
155393593Sjhb		if ((error = suser(td)))
155493359Simp			goto out;
155593359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
155693359Simp		if (error)
155793359Simp			break;
155893359Simp		error = wi_set_debug(sc, &wreq);
155993359Simp		break;
156077217Sphk	case SIOCG80211:
156177217Sphk		switch(ireq->i_type) {
156277217Sphk		case IEEE80211_IOC_SSID:
156377217Sphk			if(ireq->i_val == -1) {
156477217Sphk				bzero(tmpssid, IEEE80211_NWID_LEN);
156577217Sphk				error = wi_get_cur_ssid(sc, tmpssid, &len);
156677217Sphk				if (error != 0)
156777217Sphk					break;
156877217Sphk				error = copyout(tmpssid, ireq->i_data,
156977217Sphk					IEEE80211_NWID_LEN);
157077217Sphk				ireq->i_len = len;
157177217Sphk			} else if (ireq->i_val == 0) {
157277217Sphk				error = copyout(sc->wi_net_name,
157377217Sphk				    ireq->i_data,
157477217Sphk				    IEEE80211_NWID_LEN);
157577217Sphk				ireq->i_len = IEEE80211_NWID_LEN;
157677217Sphk			} else
157777217Sphk				error = EINVAL;
157877217Sphk			break;
157977217Sphk		case IEEE80211_IOC_NUMSSIDS:
158077217Sphk			ireq->i_val = 1;
158177217Sphk			break;
158277217Sphk		case IEEE80211_IOC_WEP:
158377217Sphk			if(!sc->wi_has_wep) {
158477217Sphk				ireq->i_val = IEEE80211_WEP_NOSUP;
158577217Sphk			} else {
158677217Sphk				if(sc->wi_use_wep) {
158777217Sphk					ireq->i_val =
158877217Sphk					    IEEE80211_WEP_MIXED;
158977217Sphk				} else {
159077217Sphk					ireq->i_val =
159177217Sphk					    IEEE80211_WEP_OFF;
159277217Sphk				}
159377217Sphk			}
159477217Sphk			break;
159577217Sphk		case IEEE80211_IOC_WEPKEY:
159677217Sphk			if(!sc->wi_has_wep ||
159777217Sphk			    ireq->i_val < 0 || ireq->i_val > 3) {
159877217Sphk				error = EINVAL;
159977217Sphk				break;
160077217Sphk			}
160177217Sphk			len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen;
160293593Sjhb			if (suser(td))
160377217Sphk				bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
160477217Sphk				    tmpkey, len);
160577217Sphk			else
160677217Sphk				bzero(tmpkey, len);
160777217Sphk
160877217Sphk			ireq->i_len = len;
160977217Sphk			error = copyout(tmpkey, ireq->i_data, len);
161077217Sphk
161177217Sphk			break;
161277217Sphk		case IEEE80211_IOC_NUMWEPKEYS:
161377217Sphk			if(!sc->wi_has_wep)
161477217Sphk				error = EINVAL;
161577217Sphk			else
161677217Sphk				ireq->i_val = 4;
161777217Sphk			break;
161877217Sphk		case IEEE80211_IOC_WEPTXKEY:
161977217Sphk			if(!sc->wi_has_wep)
162077217Sphk				error = EINVAL;
162177217Sphk			else
162277217Sphk				ireq->i_val = sc->wi_tx_key;
162377217Sphk			break;
162477217Sphk		case IEEE80211_IOC_AUTHMODE:
162577217Sphk			ireq->i_val = IEEE80211_AUTH_NONE;
162677217Sphk			break;
162777217Sphk		case IEEE80211_IOC_STATIONNAME:
162877217Sphk			error = copyout(sc->wi_node_name,
162977217Sphk			    ireq->i_data, IEEE80211_NWID_LEN);
163077217Sphk			ireq->i_len = IEEE80211_NWID_LEN;
163177217Sphk			break;
163277217Sphk		case IEEE80211_IOC_CHANNEL:
163377217Sphk			wreq.wi_type = WI_RID_CURRENT_CHAN;
163477217Sphk			wreq.wi_len = WI_MAX_DATALEN;
163577217Sphk			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq))
163677217Sphk				error = EINVAL;
163777217Sphk			else {
163877217Sphk				ireq->i_val = wreq.wi_val[0];
163977217Sphk			}
164077217Sphk			break;
164177217Sphk		case IEEE80211_IOC_POWERSAVE:
164277217Sphk			if(sc->wi_pm_enabled)
164377217Sphk				ireq->i_val = IEEE80211_POWERSAVE_ON;
164477217Sphk			else
164577217Sphk				ireq->i_val = IEEE80211_POWERSAVE_OFF;
164677217Sphk			break;
164777217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
164877217Sphk			ireq->i_val = sc->wi_max_sleep;
164977217Sphk			break;
165077217Sphk		default:
165177217Sphk			error = EINVAL;
165277217Sphk		}
165377217Sphk		break;
165477217Sphk	case SIOCS80211:
165593593Sjhb		if ((error = suser(td)))
165677217Sphk			goto out;
165777217Sphk		switch(ireq->i_type) {
165877217Sphk		case IEEE80211_IOC_SSID:
165977217Sphk			if (ireq->i_val != 0 ||
166077217Sphk			    ireq->i_len > IEEE80211_NWID_LEN) {
166177217Sphk				error = EINVAL;
166277217Sphk				break;
166377217Sphk			}
166477217Sphk			/* We set both of them */
166577217Sphk			bzero(sc->wi_net_name, IEEE80211_NWID_LEN);
166677217Sphk			error = copyin(ireq->i_data,
166777217Sphk			    sc->wi_net_name, ireq->i_len);
166877217Sphk			bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN);
166977217Sphk			break;
167077217Sphk		case IEEE80211_IOC_WEP:
167177217Sphk			/*
167277217Sphk			 * These cards only support one mode so
167377217Sphk			 * we just turn wep on what ever is
167477217Sphk			 * passed in if it's not OFF.
167577217Sphk			 */
167677217Sphk			if (ireq->i_val == IEEE80211_WEP_OFF) {
167777217Sphk				sc->wi_use_wep = 0;
167877217Sphk			} else {
167977217Sphk				sc->wi_use_wep = 1;
168077217Sphk			}
168177217Sphk			break;
168277217Sphk		case IEEE80211_IOC_WEPKEY:
168377217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3 ||
168477217Sphk				ireq->i_len > 13) {
168577217Sphk				error = EINVAL;
168677217Sphk				break;
168777217Sphk			}
168877217Sphk			bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13);
168977217Sphk			error = copyin(ireq->i_data,
169077217Sphk			    sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
169177217Sphk			    ireq->i_len);
169277217Sphk			if(error)
169377217Sphk				break;
169477217Sphk			sc->wi_keys.wi_keys[ireq->i_val].wi_keylen =
169577217Sphk				    ireq->i_len;
169677217Sphk			break;
169777217Sphk		case IEEE80211_IOC_WEPTXKEY:
169877217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3) {
169977217Sphk				error = EINVAL;
170077217Sphk				break;
170177217Sphk			}
170277217Sphk			sc->wi_tx_key = ireq->i_val;
170377217Sphk			break;
170477217Sphk		case IEEE80211_IOC_AUTHMODE:
170577217Sphk			error = EINVAL;
170677217Sphk			break;
170777217Sphk		case IEEE80211_IOC_STATIONNAME:
170877217Sphk			if (ireq->i_len > 32) {
170977217Sphk				error = EINVAL;
171077217Sphk				break;
171177217Sphk			}
171277217Sphk			bzero(sc->wi_node_name, 32);
171377217Sphk			error = copyin(ireq->i_data,
171477217Sphk			    sc->wi_node_name, ireq->i_len);
171577217Sphk			break;
171677217Sphk		case IEEE80211_IOC_CHANNEL:
171777217Sphk			/*
171877217Sphk			 * The actual range is 1-14, but if you
171977217Sphk			 * set it to 0 you get the default. So
172077217Sphk			 * we let that work too.
172177217Sphk			 */
172277217Sphk			if (ireq->i_val < 0 || ireq->i_val > 14) {
172377217Sphk				error = EINVAL;
172477217Sphk				break;
172577217Sphk			}
172677217Sphk			sc->wi_channel = ireq->i_val;
172777217Sphk			break;
172877217Sphk		case IEEE80211_IOC_POWERSAVE:
172977217Sphk			switch (ireq->i_val) {
173077217Sphk			case IEEE80211_POWERSAVE_OFF:
173177217Sphk				sc->wi_pm_enabled = 0;
173277217Sphk				break;
173377217Sphk			case IEEE80211_POWERSAVE_ON:
173477217Sphk				sc->wi_pm_enabled = 1;
173577217Sphk				break;
173677217Sphk			default:
173777217Sphk				error = EINVAL;
173877217Sphk				break;
173977217Sphk			}
174077217Sphk			break;
174177217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
174277217Sphk			if (ireq->i_val < 0) {
174377217Sphk				error = EINVAL;
174477217Sphk				break;
174577217Sphk			}
174677217Sphk			sc->wi_max_sleep = ireq->i_val;
174777217Sphk			break;
174877217Sphk		default:
174977217Sphk			error = EINVAL;
175077217Sphk			break;
175177217Sphk		}
175277217Sphk
175377217Sphk		/* Reinitialize WaveLAN. */
175477217Sphk		wi_init(sc);
175577217Sphk
175677217Sphk		break;
175746492Swpaul	default:
175846492Swpaul		error = EINVAL;
175946492Swpaul		break;
176046492Swpaul	}
176161818Srobertoout:
176267092Swpaul	WI_UNLOCK(sc);
176346492Swpaul
176446492Swpaul	return(error);
176546492Swpaul}
176646492Swpaul
176788546Salfredstatic void
176888546Salfredwi_init(xsc)
176946492Swpaul	void			*xsc;
177046492Swpaul{
177146492Swpaul	struct wi_softc		*sc = xsc;
177246492Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
177346492Swpaul	struct wi_ltv_macaddr	mac;
177446492Swpaul	int			id = 0;
177546492Swpaul
177667092Swpaul	WI_LOCK(sc);
177767092Swpaul
177867092Swpaul	if (sc->wi_gone) {
177967092Swpaul		WI_UNLOCK(sc);
178046492Swpaul		return;
178167092Swpaul	}
178246492Swpaul
178346492Swpaul	if (ifp->if_flags & IFF_RUNNING)
178446492Swpaul		wi_stop(sc);
178546492Swpaul
178646492Swpaul	wi_reset(sc);
178746492Swpaul
178846492Swpaul	/* Program max data length. */
178946492Swpaul	WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len);
179046492Swpaul
179147401Swpaul	/* Enable/disable IBSS creation. */
179246492Swpaul	WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss);
179346492Swpaul
179446492Swpaul	/* Set the port type. */
179546492Swpaul	WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype);
179646492Swpaul
179746492Swpaul	/* Program the RTS/CTS threshold. */
179846492Swpaul	WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh);
179946492Swpaul
180046492Swpaul	/* Program the TX rate */
180146492Swpaul	WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate);
180246492Swpaul
180346492Swpaul	/* Access point density */
180446492Swpaul	WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density);
180546492Swpaul
180646611Swpaul	/* Power Management Enabled */
180746611Swpaul	WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled);
180846611Swpaul
180946611Swpaul	/* Power Managment Max Sleep */
181046611Swpaul	WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep);
181146611Swpaul
181291695Simp	/* Roaming type */
181391695Simp	WI_SETVAL(WI_RID_ROAMING_MODE, sc->wi_roaming);
181491695Simp
181546492Swpaul	/* Specify the IBSS name */
181646492Swpaul	WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name);
181746492Swpaul
181846492Swpaul	/* Specify the network name */
181946492Swpaul	WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name);
182046492Swpaul
182146563Swpaul	/* Specify the frequency to use */
182246563Swpaul	WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel);
182346563Swpaul
182446492Swpaul	/* Program the nodename. */
182546492Swpaul	WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name);
182646492Swpaul
182746492Swpaul	/* Set our MAC address. */
182846492Swpaul	mac.wi_len = 4;
182946492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
183046492Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
183146492Swpaul	   (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN);
183246492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mac);
183346492Swpaul
183456965Swpaul	/* Configure WEP. */
183556965Swpaul	if (sc->wi_has_wep) {
183656965Swpaul		WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep);
183756965Swpaul		WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key);
183856965Swpaul		sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
183956965Swpaul		sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
184056965Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys);
184193733Simp		if (sc->sc_firmware_type != WI_LUCENT && sc->wi_use_wep) {
184291695Simp			/*
184391695Simp			 * ONLY HWB3163 EVAL-CARD Firmware version
184493733Simp			 * less than 0.8 variant2
184591695Simp			 *
184691695Simp			 *   If promiscuous mode disable, Prism2 chip
184791695Simp			 *  does not work with WEP .
184891695Simp			 * It is under investigation for details.
184991695Simp			 * (ichiro@netbsd.org)
185091695Simp			 */
185193733Simp			if (sc->sc_firmware_type == WI_INTERSIL &&
185293733Simp			    sc->sc_sta_firmware_ver < 802 ) {
185393733Simp				/* firm ver < 0.8 variant 2 */
185491695Simp				WI_SETVAL(WI_RID_PROMISC, 1);
185591695Simp			}
185691695Simp			WI_SETVAL(WI_RID_AUTH_CNTL, sc->wi_authtype);
185791695Simp		}
185856965Swpaul	}
185956965Swpaul
186046492Swpaul	/* Initialize promisc mode. */
186146492Swpaul	if (ifp->if_flags & IFF_PROMISC) {
186246492Swpaul		WI_SETVAL(WI_RID_PROMISC, 1);
186346492Swpaul	} else {
186446492Swpaul		WI_SETVAL(WI_RID_PROMISC, 0);
186546492Swpaul	}
186646492Swpaul
186746492Swpaul	/* Set multicast filter. */
186846492Swpaul	wi_setmulti(sc);
186946492Swpaul
187046492Swpaul	/* Enable desired port */
187192457Simp	wi_cmd(sc, WI_CMD_ENABLE | sc->wi_portnum, 0, 0, 0);
187246492Swpaul
187375373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
187453702Swpaul		device_printf(sc->dev, "tx buffer allocation failed\n");
187546492Swpaul	sc->wi_tx_data_id = id;
187646492Swpaul
187775373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
187853702Swpaul		device_printf(sc->dev, "mgmt. buffer allocation failed\n");
187946492Swpaul	sc->wi_tx_mgmt_id = id;
188046492Swpaul
188146492Swpaul	/* enable interrupts */
188246492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
188346492Swpaul
188446492Swpaul	ifp->if_flags |= IFF_RUNNING;
188546492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
188646492Swpaul
188746492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
188867092Swpaul	WI_UNLOCK(sc);
188946492Swpaul
189046492Swpaul	return;
189146492Swpaul}
189246492Swpaul
189388546Salfredstatic void
189488546Salfredwi_start(ifp)
189546492Swpaul	struct ifnet		*ifp;
189646492Swpaul{
189746492Swpaul	struct wi_softc		*sc;
189846492Swpaul	struct mbuf		*m0;
189946492Swpaul	struct wi_frame		tx_frame;
190046492Swpaul	struct ether_header	*eh;
190146492Swpaul	int			id;
190246492Swpaul
190346492Swpaul	sc = ifp->if_softc;
190467092Swpaul	WI_LOCK(sc);
190546492Swpaul
190667092Swpaul	if (sc->wi_gone) {
190767092Swpaul		WI_UNLOCK(sc);
190846492Swpaul		return;
190967092Swpaul	}
191046492Swpaul
191167092Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
191267092Swpaul		WI_UNLOCK(sc);
191346492Swpaul		return;
191467092Swpaul	}
191546492Swpaul
191646492Swpaul	IF_DEQUEUE(&ifp->if_snd, m0);
191767092Swpaul	if (m0 == NULL) {
191867092Swpaul		WI_UNLOCK(sc);
191946492Swpaul		return;
192067092Swpaul	}
192146492Swpaul
192246492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
192346492Swpaul	id = sc->wi_tx_data_id;
192446492Swpaul	eh = mtod(m0, struct ether_header *);
192546492Swpaul
192646492Swpaul	/*
192747401Swpaul	 * Use RFC1042 encoding for IP and ARP datagrams,
192846492Swpaul	 * 802.3 for anything else.
192946492Swpaul	 */
193075275Salfred	if (ntohs(eh->ether_type) > ETHER_MAX_LEN) {
193146492Swpaul		bcopy((char *)&eh->ether_dhost,
193246492Swpaul		    (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN);
193346492Swpaul		bcopy((char *)&eh->ether_shost,
193446492Swpaul		    (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
193546492Swpaul		bcopy((char *)&eh->ether_dhost,
193646492Swpaul		    (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN);
193746492Swpaul		bcopy((char *)&eh->ether_shost,
193846492Swpaul		    (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN);
193946492Swpaul
194046492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN;
194146492Swpaul		tx_frame.wi_frame_ctl = WI_FTYPE_DATA;
194246492Swpaul		tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0);
194346492Swpaul		tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1);
194446492Swpaul		tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
194546492Swpaul		tx_frame.wi_type = eh->ether_type;
194646492Swpaul
194746492Swpaul		m_copydata(m0, sizeof(struct ether_header),
194846492Swpaul		    m0->m_pkthdr.len - sizeof(struct ether_header),
194946492Swpaul		    (caddr_t)&sc->wi_txbuf);
195046492Swpaul
195146492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
195246492Swpaul		    sizeof(struct wi_frame));
195346492Swpaul		wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf,
195446492Swpaul		    (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2);
195546492Swpaul	} else {
195646492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len;
195746492Swpaul
195855831Swpaul		eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
195946492Swpaul		m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf);
196046492Swpaul
196146492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
196246492Swpaul		    sizeof(struct wi_frame));
196346492Swpaul		wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf,
196446492Swpaul		    m0->m_pkthdr.len + 2);
196546492Swpaul	}
196646492Swpaul
196746492Swpaul	/*
196846492Swpaul	 * If there's a BPF listner, bounce a copy of
196993359Simp 	 * this frame to him. Also, don't send this to the bpf sniffer
197093359Simp 	 * if we're in procframe or monitor sniffing mode.
197146492Swpaul	 */
197293359Simp 	if (!(sc->wi_procframe || sc->wi_debug.wi_monitor) && ifp->if_bpf)
197346492Swpaul		bpf_mtap(ifp, m0);
197446492Swpaul
197546492Swpaul	m_freem(m0);
197646492Swpaul
197792457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0))
197853702Swpaul		device_printf(sc->dev, "xmit failed\n");
197946492Swpaul
198046492Swpaul	ifp->if_flags |= IFF_OACTIVE;
198146492Swpaul
198246492Swpaul	/*
198346492Swpaul	 * Set a timeout in case the chip goes out to lunch.
198446492Swpaul	 */
198546492Swpaul	ifp->if_timer = 5;
198646492Swpaul
198767092Swpaul	WI_UNLOCK(sc);
198846492Swpaul	return;
198946492Swpaul}
199046492Swpaul
199188546Salfredstatic int
199288546Salfredwi_mgmt_xmit(sc, data, len)
199346492Swpaul	struct wi_softc		*sc;
199446492Swpaul	caddr_t			data;
199546492Swpaul	int			len;
199646492Swpaul{
199746492Swpaul	struct wi_frame		tx_frame;
199846492Swpaul	int			id;
199946492Swpaul	struct wi_80211_hdr	*hdr;
200046492Swpaul	caddr_t			dptr;
200146492Swpaul
200246492Swpaul	if (sc->wi_gone)
200346492Swpaul		return(ENODEV);
200446492Swpaul
200546492Swpaul	hdr = (struct wi_80211_hdr *)data;
200646492Swpaul	dptr = data + sizeof(struct wi_80211_hdr);
200746492Swpaul
200846492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
200946492Swpaul	id = sc->wi_tx_mgmt_id;
201046492Swpaul
201146492Swpaul	bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl,
201246492Swpaul	   sizeof(struct wi_80211_hdr));
201346492Swpaul
201446492Swpaul	tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN;
201546492Swpaul	tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN);
201646492Swpaul
201746492Swpaul	wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame));
201846492Swpaul	wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr,
201946492Swpaul	    (len - sizeof(struct wi_80211_hdr)) + 2);
202046492Swpaul
202192457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) {
202253702Swpaul		device_printf(sc->dev, "xmit failed\n");
202346492Swpaul		return(EIO);
202446492Swpaul	}
202546492Swpaul
202646492Swpaul	return(0);
202746492Swpaul}
202846492Swpaul
202988546Salfredstatic void
203088546Salfredwi_stop(sc)
203146492Swpaul	struct wi_softc		*sc;
203246492Swpaul{
203346492Swpaul	struct ifnet		*ifp;
203446492Swpaul
203567092Swpaul	WI_LOCK(sc);
203667092Swpaul
203767092Swpaul	if (sc->wi_gone) {
203867092Swpaul		WI_UNLOCK(sc);
203946492Swpaul		return;
204067092Swpaul	}
204146492Swpaul
204246492Swpaul	ifp = &sc->arpcom.ac_if;
204346492Swpaul
204470173Sjhb	/*
204570173Sjhb	 * If the card is gone and the memory port isn't mapped, we will
204670173Sjhb	 * (hopefully) get 0xffff back from the status read, which is not
204770173Sjhb	 * a valid status value.
204870173Sjhb	 */
204970173Sjhb	if (CSR_READ_2(sc, WI_STATUS) != 0xffff) {
205070173Sjhb		CSR_WRITE_2(sc, WI_INT_EN, 0);
205192457Simp		wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0, 0, 0);
205270173Sjhb	}
205346492Swpaul
205446492Swpaul	untimeout(wi_inquire, sc, sc->wi_stat_ch);
205546492Swpaul
205646492Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
205746492Swpaul
205867092Swpaul	WI_UNLOCK(sc);
205946492Swpaul	return;
206046492Swpaul}
206146492Swpaul
206288546Salfredstatic void
206388546Salfredwi_watchdog(ifp)
206446492Swpaul	struct ifnet		*ifp;
206546492Swpaul{
206646492Swpaul	struct wi_softc		*sc;
206746492Swpaul
206846492Swpaul	sc = ifp->if_softc;
206946492Swpaul
207075199Salfred	device_printf(sc->dev, "watchdog timeout\n");
207146492Swpaul
207246492Swpaul	wi_init(sc);
207346492Swpaul
207446492Swpaul	ifp->if_oerrors++;
207546492Swpaul
207646492Swpaul	return;
207746492Swpaul}
207846492Swpaul
207993611Simpint
208090580Sbrookswi_alloc(dev, rid)
208153702Swpaul	device_t		dev;
208290580Sbrooks	int			rid;
208346492Swpaul{
208453702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
208553702Swpaul
208690580Sbrooks	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
208790580Sbrooks		sc->iobase_rid = rid;
208890580Sbrooks		sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
208990580Sbrooks		    &sc->iobase_rid, 0, ~0, (1 << 6),
209090580Sbrooks		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
209190580Sbrooks		if (!sc->iobase) {
209290580Sbrooks			device_printf(dev, "No I/O space?!\n");
209390580Sbrooks			return (ENXIO);
209490580Sbrooks		}
209590580Sbrooks
209690580Sbrooks		sc->wi_io_addr = rman_get_start(sc->iobase);
209790580Sbrooks		sc->wi_btag = rman_get_bustag(sc->iobase);
209890580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
209990580Sbrooks	} else {
210090580Sbrooks		sc->mem_rid = rid;
210190580Sbrooks		sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY,
210290580Sbrooks		    &sc->mem_rid, 0, ~0, 1, RF_ACTIVE);
210390580Sbrooks
210490580Sbrooks		if (!sc->mem) {
210590580Sbrooks			device_printf(dev, "No Mem space on prism2.5?\n");
210690580Sbrooks			return (ENXIO);
210790580Sbrooks		}
210890580Sbrooks
210990580Sbrooks		sc->wi_btag = rman_get_bustag(sc->mem);
211090580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->mem);
211153702Swpaul	}
211253702Swpaul
211390580Sbrooks
211474906Salfred	sc->irq_rid = 0;
211574906Salfred	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
211690580Sbrooks	    0, ~0, 1, RF_ACTIVE |
211790580Sbrooks	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
211890580Sbrooks
211953702Swpaul	if (!sc->irq) {
212075219Salfred		wi_free(dev);
212153702Swpaul		device_printf(dev, "No irq?!\n");
212253702Swpaul		return (ENXIO);
212353702Swpaul	}
212453702Swpaul
212553702Swpaul	sc->dev = dev;
212653702Swpaul	sc->wi_unit = device_get_unit(dev);
212753702Swpaul
212853702Swpaul	return (0);
212953702Swpaul}
213053702Swpaul
213193611Simpvoid
213288546Salfredwi_free(dev)
213353702Swpaul	device_t		dev;
213453702Swpaul{
213553702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
213653702Swpaul
213775219Salfred	if (sc->iobase != NULL) {
213875219Salfred		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
213975219Salfred		sc->iobase = NULL;
214075219Salfred	}
214175219Salfred	if (sc->irq != NULL) {
214275219Salfred		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
214375219Salfred		sc->irq = NULL;
214475219Salfred	}
214575219Salfred	if (sc->mem != NULL) {
214674906Salfred		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
214775219Salfred		sc->mem = NULL;
214875219Salfred	}
214953702Swpaul
215053702Swpaul	return;
215153702Swpaul}
215253702Swpaul
215393611Simpvoid
215488546Salfredwi_shutdown(dev)
215553702Swpaul	device_t		dev;
215653702Swpaul{
215746492Swpaul	struct wi_softc		*sc;
215846492Swpaul
215953702Swpaul	sc = device_get_softc(dev);
216046492Swpaul	wi_stop(sc);
216146492Swpaul
216246492Swpaul	return;
216346492Swpaul}
216453702Swpaul
216553702Swpaul#ifdef WICACHE
216653702Swpaul/* wavelan signal strength cache code.
216753702Swpaul * store signal/noise/quality on per MAC src basis in
216853702Swpaul * a small fixed cache.  The cache wraps if > MAX slots
216953702Swpaul * used.  The cache may be zeroed out to start over.
217053702Swpaul * Two simple filters exist to reduce computation:
217153702Swpaul * 1. ip only (literally 0x800) which may be used
217253702Swpaul * to ignore some packets.  It defaults to ip only.
217353702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons.
217453702Swpaul * 2. multicast/broadcast only.  This may be used to
217553702Swpaul * ignore unicast packets and only cache signal strength
217653702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP
217753702Swpaul * beacons and not unicast traffic.
217853702Swpaul *
217953702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal,
218053702Swpaul *	quality, noise)
218153702Swpaul *
218253702Swpaul * No apologies for storing IP src here.  It's easy and saves much
218353702Swpaul * trouble elsewhere.  The cache is assumed to be INET dependent,
218453702Swpaul * although it need not be.
218553702Swpaul */
218653702Swpaul
218753702Swpaul#ifdef documentation
218853702Swpaul
218953702Swpaulint wi_sigitems;                                /* number of cached entries */
219053702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE];  /*  array of cache entries */
219153702Swpaulint wi_nextitem;                                /*  index/# of entries */
219253702Swpaul
219353702Swpaul
219453702Swpaul#endif
219553702Swpaul
219653702Swpaul/* control variables for cache filtering.  Basic idea is
219753702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons
219853702Swpaul * which are broadcast or multicast).  Still you might
219953702Swpaul * want to measure signal strength with unicast ping packets
220053702Swpaul * on a pt. to pt. ant. setup.
220153702Swpaul */
220253702Swpaul/* set true if you want to limit cache items to broadcast/mcast
220353702Swpaul * only packets (not unicast).  Useful for mobile-ip beacons which
220453702Swpaul * are broadcast/multicast at network layer.  Default is all packets
220553702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup.
220653702Swpaul */
220753702Swpaulstatic int wi_cache_mcastonly = 0;
220853702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW,
220953702Swpaul	&wi_cache_mcastonly, 0, "");
221053702Swpaul
221153702Swpaul/* set true if you want to limit cache items to IP packets only
221253702Swpaul*/
221353702Swpaulstatic int wi_cache_iponly = 1;
221453702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW,
221553702Swpaul	&wi_cache_iponly, 0, "");
221653702Swpaul
221753702Swpaul/*
221853702Swpaul * Original comments:
221953702Swpaul * -----------------
222053702Swpaul * wi_cache_store, per rx packet store signal
222153702Swpaul * strength in MAC (src) indexed cache.
222253702Swpaul *
222353702Swpaul * follows linux driver in how signal strength is computed.
222453702Swpaul * In ad hoc mode, we use the rx_quality field.
222553702Swpaul * signal and noise are trimmed to fit in the range from 47..138.
222653702Swpaul * rx_quality field MSB is signal strength.
222753702Swpaul * rx_quality field LSB is noise.
222853702Swpaul * "quality" is (signal - noise) as is log value.
222953702Swpaul * note: quality CAN be negative.
223053702Swpaul *
223153702Swpaul * In BSS mode, we use the RID for communication quality.
223253702Swpaul * TBD:  BSS mode is currently untested.
223353702Swpaul *
223453702Swpaul * Bill's comments:
223553702Swpaul * ---------------
223653702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc"
223753702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive:
223853702Swpaul * there's a bunch of PIO operations that have to be done to read a record
223953702Swpaul * from the NIC, and reading the comms quality RID each time a packet is
224053702Swpaul * received can really hurt performance. We don't have to do this anyway:
224153702Swpaul * the comms quality field only reflects the values in the rx_quality field
224253702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode,
224353702Swpaul * but the values it contains are updated based on the rx_quality from
224453702Swpaul * frames received from the access point.
224553702Swpaul *
224653702Swpaul * Also, according to Lucent, the signal strength and noise level values
224753702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code
224853702Swpaul * to do that instead of the scaling it did originally.
224953702Swpaul */
225088546Salfredstatic void
225188546Salfredwi_cache_store(struct wi_softc *sc, struct ether_header *eh,
225253702Swpaul                     struct mbuf *m, unsigned short rx_quality)
225353702Swpaul{
225453702Swpaul	struct ip *ip = 0;
225553702Swpaul	int i;
225653702Swpaul	static int cache_slot = 0; 	/* use this cache entry */
225753702Swpaul	static int wrapindex = 0;       /* next "free" cache entry */
225853702Swpaul	int sig, noise;
225953702Swpaul	int sawip=0;
226053702Swpaul
226153702Swpaul	/* filters:
226253702Swpaul	 * 1. ip only
226353702Swpaul	 * 2. configurable filter to throw out unicast packets,
226453702Swpaul	 * keep multicast only.
226553702Swpaul	 */
226653702Swpaul
226775276Salfred	if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) {
226853702Swpaul		sawip = 1;
226953702Swpaul	}
227053702Swpaul
227153702Swpaul	/* filter for ip packets only
227253702Swpaul	*/
227353702Swpaul	if (wi_cache_iponly && !sawip) {
227453702Swpaul		return;
227553702Swpaul	}
227653702Swpaul
227753702Swpaul	/* filter for broadcast/multicast only
227853702Swpaul	 */
227953702Swpaul	if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
228053702Swpaul		return;
228153702Swpaul	}
228253702Swpaul
228353702Swpaul#ifdef SIGDEBUG
228453702Swpaul	printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit,
228553702Swpaul	    rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff);
228653702Swpaul#endif
228753702Swpaul
228853702Swpaul	/* find the ip header.  we want to store the ip_src
228953702Swpaul	 * address.
229053702Swpaul	 */
229153702Swpaul	if (sawip) {
229253702Swpaul		ip = mtod(m, struct ip *);
229353702Swpaul	}
229453702Swpaul
229553702Swpaul	/* do a linear search for a matching MAC address
229653702Swpaul	 * in the cache table
229753702Swpaul	 * . MAC address is 6 bytes,
229853702Swpaul	 * . var w_nextitem holds total number of entries already cached
229953702Swpaul	 */
230053702Swpaul	for(i = 0; i < sc->wi_nextitem; i++) {
230153702Swpaul		if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc,  6 )) {
230253702Swpaul			/* Match!,
230353702Swpaul			 * so we already have this entry,
230453702Swpaul			 * update the data
230553702Swpaul			 */
230653702Swpaul			break;
230753702Swpaul		}
230853702Swpaul	}
230953702Swpaul
231053702Swpaul	/* did we find a matching mac address?
231153702Swpaul	 * if yes, then overwrite a previously existing cache entry
231253702Swpaul	 */
231353702Swpaul	if (i < sc->wi_nextitem )   {
231453702Swpaul		cache_slot = i;
231553702Swpaul	}
231653702Swpaul	/* else, have a new address entry,so
231753702Swpaul	 * add this new entry,
231853702Swpaul	 * if table full, then we need to replace LRU entry
231953702Swpaul	 */
232053702Swpaul	else    {
232153702Swpaul
232253702Swpaul		/* check for space in cache table
232353702Swpaul		 * note: wi_nextitem also holds number of entries
232453702Swpaul		 * added in the cache table
232553702Swpaul		 */
232653702Swpaul		if ( sc->wi_nextitem < MAXWICACHE ) {
232753702Swpaul			cache_slot = sc->wi_nextitem;
232853702Swpaul			sc->wi_nextitem++;
232953702Swpaul			sc->wi_sigitems = sc->wi_nextitem;
233053702Swpaul		}
233153702Swpaul        	/* no space found, so simply wrap with wrap index
233253702Swpaul		 * and "zap" the next entry
233353702Swpaul		 */
233453702Swpaul		else {
233553702Swpaul			if (wrapindex == MAXWICACHE) {
233653702Swpaul				wrapindex = 0;
233753702Swpaul			}
233853702Swpaul			cache_slot = wrapindex++;
233953702Swpaul		}
234053702Swpaul	}
234153702Swpaul
234253702Swpaul	/* invariant: cache_slot now points at some slot
234353702Swpaul	 * in cache.
234453702Swpaul	 */
234553702Swpaul	if (cache_slot < 0 || cache_slot >= MAXWICACHE) {
234653702Swpaul		log(LOG_ERR, "wi_cache_store, bad index: %d of "
234753702Swpaul		    "[0..%d], gross cache error\n",
234853702Swpaul		    cache_slot, MAXWICACHE);
234953702Swpaul		return;
235053702Swpaul	}
235153702Swpaul
235253702Swpaul	/*  store items in cache
235353702Swpaul	 *  .ip source address
235453702Swpaul	 *  .mac src
235553702Swpaul	 *  .signal, etc.
235653702Swpaul	 */
235753702Swpaul	if (sawip) {
235853702Swpaul		sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr;
235953702Swpaul	}
236053702Swpaul	bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc,  6);
236153702Swpaul
236253702Swpaul	sig = (rx_quality >> 8) & 0xFF;
236353702Swpaul	noise = rx_quality & 0xFF;
236453702Swpaul	sc->wi_sigcache[cache_slot].signal = sig - 149;
236553702Swpaul	sc->wi_sigcache[cache_slot].noise = noise - 149;
236653702Swpaul	sc->wi_sigcache[cache_slot].quality = sig - noise;
236753702Swpaul
236853702Swpaul	return;
236953702Swpaul}
237053702Swpaul#endif
237177217Sphk
237288546Salfredstatic int
237388546Salfredwi_get_cur_ssid(sc, ssid, len)
237477217Sphk	struct wi_softc		*sc;
237577217Sphk	char			*ssid;
237677217Sphk	int			*len;
237777217Sphk{
237877217Sphk	int			error = 0;
237977217Sphk	struct wi_req		wreq;
238077217Sphk
238177217Sphk	wreq.wi_len = WI_MAX_DATALEN;
238277217Sphk	switch (sc->wi_ptype) {
238377217Sphk	case WI_PORTTYPE_ADHOC:
238477217Sphk		wreq.wi_type = WI_RID_CURRENT_SSID;
238577217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
238677217Sphk		if (error != 0)
238777217Sphk			break;
238877217Sphk		if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
238977217Sphk			error = EINVAL;
239077217Sphk			break;
239177217Sphk		}
239277217Sphk		*len = wreq.wi_val[0];
239377217Sphk		bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
239477217Sphk		break;
239577217Sphk	case WI_PORTTYPE_BSS:
239677217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
239777217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
239877217Sphk		if (error != 0)
239977217Sphk			break;
240077217Sphk		if (wreq.wi_val[0] != 0) /* associated */ {
240177217Sphk			wreq.wi_type = WI_RID_CURRENT_SSID;
240277217Sphk			wreq.wi_len = WI_MAX_DATALEN;
240377217Sphk			error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
240477217Sphk			if (error != 0)
240577217Sphk				break;
240677217Sphk			if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
240777217Sphk				error = EINVAL;
240877217Sphk				break;
240977217Sphk			}
241077217Sphk			*len = wreq.wi_val[0];
241177217Sphk			bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
241277217Sphk		} else {
241377217Sphk			*len = IEEE80211_NWID_LEN;
241477217Sphk			bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN);
241577217Sphk		}
241677217Sphk		break;
241777217Sphk	default:
241877217Sphk		error = EINVAL;
241977217Sphk		break;
242077217Sphk	}
242177217Sphk
242277217Sphk	return error;
242377217Sphk}
242477217Sphk
242588546Salfredstatic int
242688546Salfredwi_media_change(ifp)
242777217Sphk	struct ifnet		*ifp;
242877217Sphk{
242977217Sphk	struct wi_softc		*sc = ifp->if_softc;
243077217Sphk	int			otype = sc->wi_ptype;
243177217Sphk	int			orate = sc->wi_tx_rate;
243277217Sphk
243377217Sphk	if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0)
243477217Sphk		sc->wi_ptype = WI_PORTTYPE_ADHOC;
243577217Sphk	else
243677217Sphk		sc->wi_ptype = WI_PORTTYPE_BSS;
243777217Sphk
243877217Sphk	switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) {
243977217Sphk	case IFM_IEEE80211_DS1:
244077217Sphk		sc->wi_tx_rate = 1;
244177217Sphk		break;
244277217Sphk	case IFM_IEEE80211_DS2:
244377217Sphk		sc->wi_tx_rate = 2;
244477217Sphk		break;
244577217Sphk	case IFM_IEEE80211_DS5:
244677217Sphk		sc->wi_tx_rate = 5;
244777217Sphk		break;
244877217Sphk	case IFM_IEEE80211_DS11:
244977217Sphk		sc->wi_tx_rate = 11;
245077217Sphk		break;
245177217Sphk	case IFM_AUTO:
245277217Sphk		sc->wi_tx_rate = 3;
245377217Sphk		break;
245477217Sphk	}
245577217Sphk
245677217Sphk	if (otype != sc->wi_ptype ||
245777217Sphk	    orate != sc->wi_tx_rate)
245877217Sphk		wi_init(sc);
245977217Sphk
246077217Sphk	return(0);
246177217Sphk}
246277217Sphk
246388546Salfredstatic void
246488546Salfredwi_media_status(ifp, imr)
246577217Sphk	struct ifnet		*ifp;
246677217Sphk	struct ifmediareq	*imr;
246777217Sphk{
246877217Sphk	struct wi_req		wreq;
246977217Sphk	struct wi_softc		*sc = ifp->if_softc;
247077217Sphk
247177217Sphk	if (sc->wi_tx_rate == 3) {
247277217Sphk		imr->ifm_active = IFM_IEEE80211|IFM_AUTO;
247377217Sphk		if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
247477217Sphk			imr->ifm_active |= IFM_IEEE80211_ADHOC;
247577217Sphk		wreq.wi_type = WI_RID_CUR_TX_RATE;
247677217Sphk		wreq.wi_len = WI_MAX_DATALEN;
247777217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) {
247877217Sphk			switch(wreq.wi_val[0]) {
247977217Sphk			case 1:
248077217Sphk				imr->ifm_active |= IFM_IEEE80211_DS1;
248177217Sphk				break;
248277217Sphk			case 2:
248377217Sphk				imr->ifm_active |= IFM_IEEE80211_DS2;
248477217Sphk				break;
248577217Sphk			case 6:
248677217Sphk				imr->ifm_active |= IFM_IEEE80211_DS5;
248777217Sphk				break;
248877217Sphk			case 11:
248977217Sphk				imr->ifm_active |= IFM_IEEE80211_DS11;
249077217Sphk				break;
249177217Sphk				}
249277217Sphk		}
249377217Sphk	} else {
249477217Sphk		imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media;
249577217Sphk	}
249677217Sphk
249777217Sphk	imr->ifm_status = IFM_AVALID;
249877217Sphk	if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
249977217Sphk		/*
250077217Sphk		 * XXX: It would be nice if we could give some actually
250177217Sphk		 * useful status like whether we joined another IBSS or
250277217Sphk		 * created one ourselves.
250377217Sphk		 */
250477217Sphk		imr->ifm_status |= IFM_ACTIVE;
250577217Sphk	else {
250677217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
250777217Sphk		wreq.wi_len = WI_MAX_DATALEN;
250877217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 &&
250977217Sphk		    wreq.wi_val[0] != 0)
251077217Sphk			imr->ifm_status |= IFM_ACTIVE;
251177217Sphk	}
251277217Sphk}
251393359Simp
251493359Simpstatic int
251593359Simpwi_get_debug(sc, wreq)
251693359Simp	struct wi_softc		*sc;
251793359Simp	struct wi_req		*wreq;
251893359Simp{
251993359Simp	int			error = 0;
252093359Simp
252193359Simp	wreq->wi_len = 1;
252293359Simp
252393359Simp	switch (wreq->wi_type) {
252493359Simp	case WI_DEBUG_SLEEP:
252593359Simp		wreq->wi_len++;
252693359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sleep;
252793359Simp		break;
252893359Simp	case WI_DEBUG_DELAYSUPP:
252993359Simp		wreq->wi_len++;
253093359Simp		wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
253193359Simp		break;
253293359Simp	case WI_DEBUG_TXSUPP:
253393359Simp		wreq->wi_len++;
253493359Simp		wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
253593359Simp		break;
253693359Simp	case WI_DEBUG_MONITOR:
253793359Simp		wreq->wi_len++;
253893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_monitor;
253993359Simp		break;
254093359Simp	case WI_DEBUG_LEDTEST:
254193359Simp		wreq->wi_len += 3;
254293359Simp		wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
254393359Simp		wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
254493359Simp		wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
254593359Simp		break;
254693359Simp	case WI_DEBUG_CONTTX:
254793359Simp		wreq->wi_len += 2;
254893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_conttx;
254993359Simp		wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
255093359Simp		break;
255193359Simp	case WI_DEBUG_CONTRX:
255293359Simp		wreq->wi_len++;
255393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_contrx;
255493359Simp		break;
255593359Simp	case WI_DEBUG_SIGSTATE:
255693359Simp		wreq->wi_len += 2;
255793359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
255893359Simp		wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
255993359Simp		break;
256093359Simp	case WI_DEBUG_CONFBITS:
256193359Simp		wreq->wi_len += 2;
256293359Simp		wreq->wi_val[0] = sc->wi_debug.wi_confbits;
256393359Simp		wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
256493359Simp		break;
256593359Simp	default:
256693359Simp		error = EIO;
256793359Simp		break;
256893359Simp	}
256993359Simp
257093359Simp	return (error);
257193359Simp}
257293359Simp
257393359Simpstatic int
257493359Simpwi_set_debug(sc, wreq)
257593359Simp	struct wi_softc		*sc;
257693359Simp	struct wi_req		*wreq;
257793359Simp{
257893359Simp	int			error = 0;
257993359Simp	u_int16_t		cmd, param0 = 0, param1 = 0;
258093359Simp
258193359Simp	switch (wreq->wi_type) {
258293359Simp	case WI_DEBUG_RESET:
258393359Simp	case WI_DEBUG_INIT:
258493359Simp	case WI_DEBUG_CALENABLE:
258593359Simp		break;
258693359Simp	case WI_DEBUG_SLEEP:
258793359Simp		sc->wi_debug.wi_sleep = 1;
258893359Simp		break;
258993359Simp	case WI_DEBUG_WAKE:
259093359Simp		sc->wi_debug.wi_sleep = 0;
259193359Simp		break;
259293359Simp	case WI_DEBUG_CHAN:
259393359Simp		param0 = wreq->wi_val[0];
259493359Simp		break;
259593359Simp	case WI_DEBUG_DELAYSUPP:
259693359Simp		sc->wi_debug.wi_delaysupp = 1;
259793359Simp		break;
259893359Simp	case WI_DEBUG_TXSUPP:
259993359Simp		sc->wi_debug.wi_txsupp = 1;
260093359Simp		break;
260193359Simp	case WI_DEBUG_MONITOR:
260293359Simp		sc->wi_debug.wi_monitor = 1;
260393359Simp		break;
260493359Simp	case WI_DEBUG_LEDTEST:
260593359Simp		param0 = wreq->wi_val[0];
260693359Simp		param1 = wreq->wi_val[1];
260793359Simp		sc->wi_debug.wi_ledtest = 1;
260893359Simp		sc->wi_debug.wi_ledtest_param0 = param0;
260993359Simp		sc->wi_debug.wi_ledtest_param1 = param1;
261093359Simp		break;
261193359Simp	case WI_DEBUG_CONTTX:
261293359Simp		param0 = wreq->wi_val[0];
261393359Simp		sc->wi_debug.wi_conttx = 1;
261493359Simp		sc->wi_debug.wi_conttx_param0 = param0;
261593359Simp		break;
261693359Simp	case WI_DEBUG_STOPTEST:
261793359Simp		sc->wi_debug.wi_delaysupp = 0;
261893359Simp		sc->wi_debug.wi_txsupp = 0;
261993359Simp		sc->wi_debug.wi_monitor = 0;
262093359Simp		sc->wi_debug.wi_ledtest = 0;
262193359Simp		sc->wi_debug.wi_ledtest_param0 = 0;
262293359Simp		sc->wi_debug.wi_ledtest_param1 = 0;
262393359Simp		sc->wi_debug.wi_conttx = 0;
262493359Simp		sc->wi_debug.wi_conttx_param0 = 0;
262593359Simp		sc->wi_debug.wi_contrx = 0;
262693359Simp		sc->wi_debug.wi_sigstate = 0;
262793359Simp		sc->wi_debug.wi_sigstate_param0 = 0;
262893359Simp		break;
262993359Simp	case WI_DEBUG_CONTRX:
263093359Simp		sc->wi_debug.wi_contrx = 1;
263193359Simp		break;
263293359Simp	case WI_DEBUG_SIGSTATE:
263393359Simp		param0 = wreq->wi_val[0];
263493359Simp		sc->wi_debug.wi_sigstate = 1;
263593359Simp		sc->wi_debug.wi_sigstate_param0 = param0;
263693359Simp		break;
263793359Simp	case WI_DEBUG_CONFBITS:
263893359Simp		param0 = wreq->wi_val[0];
263993359Simp		param1 = wreq->wi_val[1];
264093359Simp		sc->wi_debug.wi_confbits = param0;
264193359Simp		sc->wi_debug.wi_confbits_param0 = param1;
264293359Simp		break;
264393359Simp	default:
264493359Simp		error = EIO;
264593359Simp		break;
264693359Simp	}
264793359Simp
264893359Simp	if (error)
264993359Simp		return (error);
265093359Simp
265193359Simp	cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
265293359Simp	error = wi_cmd(sc, cmd, param0, param1, 0);
265393359Simp
265493359Simp	return (error);
265593359Simp}
2656