if_wi.c revision 94472
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>
9894405Simp#ifdef WI_HOSTAP
9994405Simp#include <dev/wi/wi_hostap.h>
10094405Simp#include <sys/random.h>
10194405Simp#endif
10293611Simp#include <dev/wi/if_wivar.h>
10370808Speter#include <dev/wi/if_wireg.h>
10446492Swpaul
10546492Swpaul#if !defined(lint)
10646492Swpaulstatic const char rcsid[] =
10750477Speter  "$FreeBSD: head/sys/dev/wi/if_wi.c 94472 2002-04-12 03:42:37Z imp $";
10846492Swpaul#endif
10946492Swpaul
11091693Simpstatic void wi_intr(void *);
11191693Simpstatic void wi_reset(struct wi_softc *);
11291693Simpstatic int wi_ioctl(struct ifnet *, u_long, caddr_t);
11391693Simpstatic void wi_init(void *);
11491693Simpstatic void wi_start(struct ifnet *);
11591693Simpstatic void wi_stop(struct wi_softc *);
11691693Simpstatic void wi_watchdog(struct ifnet *);
11791693Simpstatic void wi_rxeof(struct wi_softc *);
11891693Simpstatic void wi_txeof(struct wi_softc *, int);
11991693Simpstatic void wi_update_stats(struct wi_softc *);
12091693Simpstatic void wi_setmulti(struct wi_softc *);
12146492Swpaul
12292457Simpstatic int wi_cmd(struct wi_softc *, int, int, int, int);
12391693Simpstatic int wi_read_record(struct wi_softc *, struct wi_ltv_gen *);
12491693Simpstatic int wi_write_record(struct wi_softc *, struct wi_ltv_gen *);
12591693Simpstatic int wi_read_data(struct wi_softc *, int, int, caddr_t, int);
12691693Simpstatic int wi_write_data(struct wi_softc *, int, int, caddr_t, int);
12791693Simpstatic int wi_seek(struct wi_softc *, int, int, int);
12891693Simpstatic int wi_alloc_nicmem(struct wi_softc *, int, int *);
12991693Simpstatic void wi_inquire(void *);
13091693Simpstatic void wi_setdef(struct wi_softc *, struct wi_req *);
13146492Swpaul
13253702Swpaul#ifdef WICACHE
13353702Swpaulstatic
13491693Simpvoid wi_cache_store(struct wi_softc *, struct ether_header *,
13591693Simp	struct mbuf *, unsigned short);
13653702Swpaul#endif
13753702Swpaul
13891693Simpstatic int wi_get_cur_ssid(struct wi_softc *, char *, int *);
13993825Simpstatic void wi_get_id(struct wi_softc *);
14091693Simpstatic int wi_media_change(struct ifnet *);
14191693Simpstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
14277217Sphk
14393359Simpstatic int wi_get_debug(struct wi_softc *, struct wi_req *);
14493359Simpstatic int wi_set_debug(struct wi_softc *, struct wi_req *);
14593359Simp
14693611Simpdevclass_t wi_devclass;
14753702Swpaul
14893825Simpstruct wi_card_ident wi_card_ident[] = {
14993825Simp	/* CARD_ID			CARD_NAME		FIRM_TYPE */
15093825Simp	{ WI_NIC_LUCENT_ID,		WI_NIC_LUCENT_STR,	WI_LUCENT },
15193825Simp	{ WI_NIC_SONY_ID,		WI_NIC_SONY_STR,	WI_LUCENT },
15293825Simp	{ WI_NIC_LUCENT_EMB_ID,		WI_NIC_LUCENT_EMB_STR,	WI_LUCENT },
15393825Simp	{ WI_NIC_EVB2_ID,		WI_NIC_EVB2_STR,	WI_INTERSIL },
15493825Simp	{ WI_NIC_HWB3763_ID,		WI_NIC_HWB3763_STR,	WI_INTERSIL },
15593825Simp	{ WI_NIC_HWB3163_ID,		WI_NIC_HWB3163_STR,	WI_INTERSIL },
15693825Simp	{ WI_NIC_HWB3163B_ID,		WI_NIC_HWB3163B_STR,	WI_INTERSIL },
15793825Simp	{ WI_NIC_EVB3_ID,		WI_NIC_EVB3_STR,	WI_INTERSIL },
15893825Simp	{ WI_NIC_HWB1153_ID,		WI_NIC_HWB1153_STR,	WI_INTERSIL },
15993825Simp	{ WI_NIC_P2_SST_ID,		WI_NIC_P2_SST_STR,	WI_INTERSIL },
16093825Simp	{ WI_NIC_EVB2_SST_ID,		WI_NIC_EVB2_SST_STR,	WI_INTERSIL },
16193825Simp	{ WI_NIC_3842_EVA_ID,		WI_NIC_3842_EVA_STR,	WI_INTERSIL },
16293825Simp	{ WI_NIC_3842_PCMCIA_AMD_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16393825Simp	{ WI_NIC_3842_PCMCIA_SST_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16493825Simp	{ WI_NIC_3842_PCMCIA_ATM_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
16593825Simp	{ WI_NIC_3842_MINI_AMD_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16693825Simp	{ WI_NIC_3842_MINI_SST_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16793825Simp	{ WI_NIC_3842_MINI_ATM_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
16893825Simp	{ WI_NIC_3842_PCI_AMD_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
16993825Simp	{ WI_NIC_3842_PCI_SST_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
17093825Simp	{ WI_NIC_3842_PCI_ATM_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
17193825Simp	{ WI_NIC_P3_PCMCIA_AMD_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
17293825Simp	{ WI_NIC_P3_PCMCIA_SST_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
17393825Simp	{ WI_NIC_P3_MINI_AMD_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
17493825Simp	{ WI_NIC_P3_MINI_SST_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
17593825Simp	{ 0,	NULL,	0 },
17693825Simp};
17793825Simp
17893611Simpint
17993611Simpwi_generic_detach(dev)
18053702Swpaul	device_t		dev;
18146492Swpaul{
18246492Swpaul	struct wi_softc		*sc;
18346492Swpaul	struct ifnet		*ifp;
18446492Swpaul
18553702Swpaul	sc = device_get_softc(dev);
18667092Swpaul	WI_LOCK(sc);
18746492Swpaul	ifp = &sc->arpcom.ac_if;
18846492Swpaul
18946492Swpaul	if (sc->wi_gone) {
19053702Swpaul		device_printf(dev, "already unloaded\n");
19167092Swpaul		WI_UNLOCK(sc);
19253702Swpaul		return(ENODEV);
19346492Swpaul	}
19446492Swpaul
19553702Swpaul	wi_stop(sc);
19658274Srwatson
19777217Sphk	/* Delete all remaining media. */
19877217Sphk	ifmedia_removeall(&sc->ifmedia);
19977217Sphk
20063090Sarchie	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
20154277Swpaul	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
20253702Swpaul	wi_free(dev);
20346492Swpaul	sc->wi_gone = 1;
20446492Swpaul
20567092Swpaul	WI_UNLOCK(sc);
20667092Swpaul	mtx_destroy(&sc->wi_mtx);
20746492Swpaul
20846492Swpaul	return(0);
20946492Swpaul}
21046492Swpaul
21193611Simpint
21274906Salfredwi_generic_attach(device_t dev)
21374906Salfred{
21474906Salfred	struct wi_softc		*sc;
21574906Salfred	struct wi_ltv_macaddr	mac;
21674906Salfred	struct wi_ltv_gen	gen;
21774906Salfred	struct ifnet		*ifp;
21874906Salfred	int			error;
21974906Salfred
22094405Simp	/* XXX maybe we need the splimp stuff here XXX */
22174906Salfred	sc = device_get_softc(dev);
22274906Salfred	ifp = &sc->arpcom.ac_if;
22374906Salfred
22453702Swpaul	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
22574998Swpaul	    wi_intr, sc, &sc->wi_intrhand);
22653702Swpaul
22753702Swpaul	if (error) {
22853702Swpaul		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
22953702Swpaul		wi_free(dev);
23053702Swpaul		return (error);
23153702Swpaul	}
23253702Swpaul
23393818Sjhb	mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
23493818Sjhb	    MTX_DEF | MTX_RECURSE);
23567092Swpaul	WI_LOCK(sc);
23667092Swpaul
23746492Swpaul	/* Reset the NIC. */
23846492Swpaul	wi_reset(sc);
23946492Swpaul
24076438Swpaul	/*
24176438Swpaul	 * Read the station address.
24276438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
24376438Swpaul	 * an error when trying to read it the first time, which causes
24476438Swpaul	 * the probe to fail.
24576438Swpaul	 */
24646492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
24746492Swpaul	mac.wi_len = 4;
24876457Sgrog	wi_read_record(sc, (struct wi_ltv_gen *)&mac);
24975150Simp	if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) {
25075149Simp		device_printf(dev, "mac read failed %d\n", error);
25175149Simp		wi_free(dev);
25275149Simp		return (error);
25375149Simp	}
25446492Swpaul	bcopy((char *)&mac.wi_mac_addr,
25546492Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
25646492Swpaul
25787383Simp	device_printf(dev, "802.11 address: %6D\n", sc->arpcom.ac_enaddr, ":");
25846492Swpaul
25993825Simp	wi_get_id(sc);
26087383Simp
26146492Swpaul	ifp->if_softc = sc;
26246492Swpaul	ifp->if_unit = sc->wi_unit;
26346492Swpaul	ifp->if_name = "wi";
26446492Swpaul	ifp->if_mtu = ETHERMTU;
26546492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
26646492Swpaul	ifp->if_ioctl = wi_ioctl;
26746492Swpaul	ifp->if_output = ether_output;
26846492Swpaul	ifp->if_start = wi_start;
26946492Swpaul	ifp->if_watchdog = wi_watchdog;
27046492Swpaul	ifp->if_init = wi_init;
27146492Swpaul	ifp->if_baudrate = 10000000;
27246492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
27346492Swpaul
27446492Swpaul	bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
27546492Swpaul	bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name,
27646492Swpaul	    sizeof(WI_DEFAULT_NODENAME) - 1);
27746492Swpaul
27846492Swpaul	bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
27946492Swpaul	bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name,
28046492Swpaul	    sizeof(WI_DEFAULT_NETNAME) - 1);
28146492Swpaul
28246492Swpaul	bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
28346492Swpaul	bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name,
28446492Swpaul	    sizeof(WI_DEFAULT_IBSS) - 1);
28546492Swpaul
28646492Swpaul	sc->wi_portnum = WI_DEFAULT_PORT;
28774139Sassar	sc->wi_ptype = WI_PORTTYPE_BSS;
28846492Swpaul	sc->wi_ap_density = WI_DEFAULT_AP_DENSITY;
28946492Swpaul	sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH;
29046492Swpaul	sc->wi_tx_rate = WI_DEFAULT_TX_RATE;
29146492Swpaul	sc->wi_max_data_len = WI_DEFAULT_DATALEN;
29246492Swpaul	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
29346611Swpaul	sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED;
29446611Swpaul	sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP;
29591695Simp	sc->wi_roaming = WI_DEFAULT_ROAMING;
29691695Simp	sc->wi_authtype = WI_DEFAULT_AUTHTYPE;
29794405Simp	sc->wi_authmode = IEEE80211_AUTH_OPEN;
29846492Swpaul
29946563Swpaul	/*
30046563Swpaul	 * Read the default channel from the NIC. This may vary
30146563Swpaul	 * depending on the country where the NIC was purchased, so
30246563Swpaul	 * we can't hard-code a default and expect it to work for
30346563Swpaul	 * everyone.
30446563Swpaul	 */
30546563Swpaul	gen.wi_type = WI_RID_OWN_CHNL;
30646563Swpaul	gen.wi_len = 2;
30746563Swpaul	wi_read_record(sc, &gen);
30846563Swpaul	sc->wi_channel = gen.wi_val;
30946563Swpaul
31056965Swpaul	/*
31156965Swpaul	 * Find out if we support WEP on this card.
31256965Swpaul	 */
31356965Swpaul	gen.wi_type = WI_RID_WEP_AVAIL;
31456965Swpaul	gen.wi_len = 2;
31556965Swpaul	wi_read_record(sc, &gen);
31656965Swpaul	sc->wi_has_wep = gen.wi_val;
31756965Swpaul
31870073Swpaul	if (bootverbose) {
31994405Simp		device_printf(sc->dev, "%s:wi_has_wep = %d\n",
32094405Simp		  __func__, sc->wi_has_wep);
32170073Swpaul	}
32270073Swpaul
32394405Simp	/*
32494405Simp	 * Find supported rates.
32594405Simp	 */
32694405Simp	gen.wi_type = WI_RID_TX_RATE;
32794405Simp	gen.wi_len = 2;
32894405Simp	wi_read_record(sc, &gen);
32994405Simp	sc->wi_supprates = gen.wi_val;
33094405Simp
33146492Swpaul	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
33246492Swpaul
33346492Swpaul	wi_init(sc);
33446492Swpaul	wi_stop(sc);
33546492Swpaul
33677217Sphk	ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status);
33777217Sphk	/* XXX: Should read from card capabilities */
33877217Sphk#define ADD(m, c)       ifmedia_add(&sc->ifmedia, (m), (c), NULL)
33977217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1,
34077217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
34177217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0);
34277217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2,
34377217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
34477217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0);
34577217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5,
34677217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
34777217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0);
34877217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11,
34977217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
35077217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0);
35177217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
35277217Sphk		IFM_IEEE80211_ADHOC, 0), 0);
35377217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0);
35494405Simp#ifdef IFM_IEEE80211_HOSTAP
35594405Simp	if (sc->sc_firmware_type == WI_INTERSIL) {
35694405Simp		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1,
35794405Simp		    IFM_IEEE80211_HOSTAP, 0), 0);
35894405Simp		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2,
35994405Simp		    IFM_IEEE80211_HOSTAP, 0), 0);
36094405Simp		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5,
36194405Simp		    IFM_IEEE80211_HOSTAP, 0), 0);
36294405Simp		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11,
36394405Simp		    IFM_IEEE80211_HOSTAP, 0), 0);
36494405Simp		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
36594405Simp		    IFM_IEEE80211_HOSTAP, 0), 0);
36694405Simp	}
36794405Simp#endif
36877217Sphk#undef	ADD
36977217Sphk	ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
37077217Sphk	    0, 0));
37177217Sphk
37277217Sphk
37346492Swpaul	/*
37463090Sarchie	 * Call MI attach routine.
37546492Swpaul	 */
37663090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
37753702Swpaul	callout_handle_init(&sc->wi_stat_ch);
37867092Swpaul	WI_UNLOCK(sc);
37946492Swpaul
38046492Swpaul	return(0);
38146492Swpaul}
38246492Swpaul
38387383Simpstatic void
38493825Simpwi_get_id(sc)
38587383Simp	struct wi_softc *sc;
38687383Simp{
38787383Simp	struct wi_ltv_ver       ver;
38893825Simp	struct wi_card_ident	*id;
38987383Simp
39087383Simp	/* getting chip identity */
39187383Simp	memset(&ver, 0, sizeof(ver));
39293567Simp	ver.wi_type = WI_RID_CARD_ID;
39387383Simp	ver.wi_len = 5;
39487383Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
39593733Simp	device_printf(sc->dev, "using ");
39693825Simp	sc->sc_firmware_type = WI_NOTYPE;
39793825Simp	for (id = wi_card_ident; id->card_name != NULL; id++) {
39893825Simp		if (le16toh(ver.wi_ver[0]) == id->card_id) {
39993825Simp			printf("%s", id->card_name);
40093825Simp			sc->sc_firmware_type = id->firm_type;
40193825Simp			break;
40293825Simp		}
40393825Simp	}
40493825Simp	if (sc->sc_firmware_type == WI_NOTYPE) {
40593825Simp		if (le16toh(ver.wi_ver[0]) & 0x8000) {
40693733Simp			printf("Unknown PRISM2 chip");
40793825Simp			sc->sc_firmware_type = WI_INTERSIL;
40893825Simp		} else {
40993733Simp			printf("Unknown Lucent chip");
41093825Simp			sc->sc_firmware_type = WI_LUCENT;
41193825Simp		}
41287383Simp	}
41387383Simp
41493733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
41593733Simp		/* get primary firmware version */
41693733Simp		memset(&ver, 0, sizeof(ver));
41793733Simp		ver.wi_type = WI_RID_PRI_IDENTITY;
41893733Simp		ver.wi_len = 5;
41993733Simp		wi_read_record(sc, (struct wi_ltv_gen *)&ver);
42093733Simp		ver.wi_ver[1] = le16toh(ver.wi_ver[1]);
42193733Simp		ver.wi_ver[2] = le16toh(ver.wi_ver[2]);
42293733Simp		ver.wi_ver[3] = le16toh(ver.wi_ver[3]);
42393733Simp		sc->sc_pri_firmware_ver = ver.wi_ver[2] * 10000 +
42493733Simp		    ver.wi_ver[3] * 100 + ver.wi_ver[1];
42593733Simp	}
42693733Simp
42793733Simp	/* get station firmware version */
42892457Simp	memset(&ver, 0, sizeof(ver));
42992457Simp	ver.wi_type = WI_RID_STA_IDENTITY;
43092457Simp	ver.wi_len = 5;
43192457Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
43292457Simp	ver.wi_ver[1] = le16toh(ver.wi_ver[1]);
43392457Simp	ver.wi_ver[2] = le16toh(ver.wi_ver[2]);
43492457Simp	ver.wi_ver[3] = le16toh(ver.wi_ver[3]);
43593733Simp	sc->sc_sta_firmware_ver = ver.wi_ver[2] * 10000 +
43693733Simp	    ver.wi_ver[3] * 100 + ver.wi_ver[1];
43793733Simp	if (sc->sc_firmware_type == WI_INTERSIL &&
43893733Simp	    (sc->sc_sta_firmware_ver == 10102 ||
43993733Simp	     sc->sc_sta_firmware_ver == 20102)) {
44093733Simp		struct wi_ltv_str sver;
44193733Simp		char *p;
44287383Simp
44393733Simp		memset(&sver, 0, sizeof(sver));
44493733Simp		sver.wi_type = WI_RID_SYMBOL_IDENTITY;
44593733Simp		sver.wi_len = 7;
44693733Simp		/* value should be "V2.00-11" */
44793733Simp		if (wi_read_record(sc, (struct wi_ltv_gen *)&sver) == 0 &&
44893733Simp		    *(p = (char *)sver.wi_str) == 'V' &&
44993733Simp		    p[2] == '.' && p[5] == '-' && p[8] == '\0') {
45093733Simp			sc->sc_firmware_type = WI_SYMBOL;
45193733Simp			sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 +
45293733Simp			    (p[3] - '0') * 1000 + (p[4] - '0') * 100 +
45393733Simp			    (p[6] - '0') * 10 + (p[7] - '0');
45493733Simp		}
45593733Simp	}
45693733Simp	printf("\n");
45793733Simp	device_printf(sc->dev, "%s Firmware: ",
45893733Simp	     sc->sc_firmware_type == WI_LUCENT ? "Lucent" :
45993733Simp	    (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil"));
46093733Simp
46193733Simp	/*
46293733Simp	 * The primary firmware is only valid on Prism based chipsets
46393733Simp	 * (INTERSIL or SYMBOL).
46493733Simp	 */
46593733Simp	if (sc->sc_firmware_type != WI_LUCENT)
46693733Simp	    printf("Primary %u.%02u.%02u, ", sc->sc_pri_firmware_ver / 10000,
46793733Simp		    (sc->sc_pri_firmware_ver % 10000) / 100,
46893733Simp		    sc->sc_pri_firmware_ver % 100);
46993733Simp	printf("Station %u.%02u.%02u\n",
47093733Simp	    sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100,
47193733Simp	    sc->sc_sta_firmware_ver % 100);
47287383Simp	return;
47387383Simp}
47487383Simp
47588546Salfredstatic void
47688546Salfredwi_rxeof(sc)
47746492Swpaul	struct wi_softc		*sc;
47846492Swpaul{
47946492Swpaul	struct ifnet		*ifp;
48046492Swpaul	struct ether_header	*eh;
48146492Swpaul	struct mbuf		*m;
48246492Swpaul	int			id;
48346492Swpaul
48446492Swpaul	ifp = &sc->arpcom.ac_if;
48546492Swpaul
48646492Swpaul	id = CSR_READ_2(sc, WI_RX_FID);
48746492Swpaul
48893359Simp	/*
48993359Simp	 * if we have the procframe flag set, disregard all this and just
49093359Simp	 * read the data from the device.
49193359Simp	 */
49293359Simp	if (sc->wi_procframe || sc->wi_debug.wi_monitor) {
49393359Simp		struct wi_frame		*rx_frame;
49493359Simp		int			datlen, hdrlen;
49546492Swpaul
49693359Simp		/* first allocate mbuf for packet storage */
49793359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
49893359Simp		if (m == NULL) {
49993359Simp			ifp->if_ierrors++;
50093359Simp			return;
50193359Simp		}
50293359Simp		MCLGET(m, M_DONTWAIT);
50393359Simp		if (!(m->m_flags & M_EXT)) {
50493359Simp			m_freem(m);
50593359Simp			ifp->if_ierrors++;
50693359Simp			return;
50793359Simp		}
50846492Swpaul
50993359Simp		m->m_pkthdr.rcvif = ifp;
51046492Swpaul
51193359Simp		/* now read wi_frame first so we know how much data to read */
51293359Simp		if (wi_read_data(sc, id, 0, mtod(m, caddr_t),
51393359Simp		    sizeof(struct wi_frame))) {
51493359Simp			m_freem(m);
51593359Simp			ifp->if_ierrors++;
51693359Simp			return;
51793359Simp		}
51846492Swpaul
51993359Simp		rx_frame = mtod(m, struct wi_frame *);
52093359Simp
52193359Simp		switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) {
52293359Simp		case 7:
52393359Simp			switch (rx_frame->wi_frame_ctl & WI_FCTL_FTYPE) {
52493359Simp			case WI_FTYPE_DATA:
52593359Simp				hdrlen = WI_DATA_HDRLEN;
52693359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
52793359Simp				break;
52893359Simp			case WI_FTYPE_MGMT:
52993359Simp				hdrlen = WI_MGMT_HDRLEN;
53093359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
53193359Simp				break;
53293359Simp			case WI_FTYPE_CTL:
53393359Simp				/*
53493359Simp				 * prism2 cards don't pass control packets
53593359Simp				 * down properly or consistently, so we'll only
53693359Simp				 * pass down the header.
53793359Simp				 */
53893359Simp				hdrlen = WI_CTL_HDRLEN;
53993359Simp				datlen = 0;
54093359Simp				break;
54193359Simp			default:
54293359Simp				device_printf(sc->dev, "received packet of "
54393359Simp				    "unknown type on port 7\n");
54493359Simp				m_freem(m);
54593359Simp				ifp->if_ierrors++;
54693359Simp				return;
54793359Simp			}
54893359Simp			break;
54993359Simp		case 0:
55093359Simp			hdrlen = WI_DATA_HDRLEN;
55193359Simp			datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
55293359Simp			break;
55393359Simp		default:
55493359Simp			device_printf(sc->dev, "received packet on invalid "
55593359Simp			    "port (wi_status=0x%x)\n", rx_frame->wi_status);
55693359Simp			m_freem(m);
55793359Simp			ifp->if_ierrors++;
55893359Simp			return;
55993359Simp		}
56093359Simp
56193359Simp		if ((hdrlen + datlen + 2) > MCLBYTES) {
56253702Swpaul			device_printf(sc->dev, "oversized packet received "
56353702Swpaul			    "(wi_dat_len=%d, wi_status=0x%x)\n",
56493359Simp			    datlen, rx_frame->wi_status);
56548553Swpaul			m_freem(m);
56648553Swpaul			ifp->if_ierrors++;
56748553Swpaul			return;
56848553Swpaul		}
56946492Swpaul
57093359Simp		if (wi_read_data(sc, id, hdrlen, mtod(m, caddr_t) + hdrlen,
57193359Simp		    datlen + 2)) {
57293359Simp			m_freem(m);
57393359Simp			ifp->if_ierrors++;
57493359Simp			return;
57559328Swpaul		}
57675373Salfred
57793359Simp		m->m_pkthdr.len = m->m_len = hdrlen + datlen;
57846492Swpaul
57993359Simp		ifp->if_ipackets++;
58093359Simp
58193359Simp		/* Handle BPF listeners. */
58293359Simp		if (ifp->if_bpf)
58393359Simp			bpf_mtap(ifp, m);
58493359Simp
58593359Simp		m_freem(m);
58693359Simp	} else {
58793359Simp		struct wi_frame		rx_frame;
58893359Simp
58993359Simp		/* First read in the frame header */
59093359Simp		if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame,
59193359Simp		    sizeof(rx_frame))) {
59246492Swpaul			ifp->if_ierrors++;
59346492Swpaul			return;
59446492Swpaul		}
59593359Simp
59693359Simp		if (rx_frame.wi_status & WI_STAT_ERRSTAT) {
59748553Swpaul			ifp->if_ierrors++;
59848553Swpaul			return;
59948553Swpaul		}
60046492Swpaul
60193359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
60293359Simp		if (m == NULL) {
60393359Simp			ifp->if_ierrors++;
60493359Simp			return;
60593359Simp		}
60693359Simp		MCLGET(m, M_DONTWAIT);
60793359Simp		if (!(m->m_flags & M_EXT)) {
60846492Swpaul			m_freem(m);
60946492Swpaul			ifp->if_ierrors++;
61046492Swpaul			return;
61146492Swpaul		}
61246492Swpaul
61393359Simp		eh = mtod(m, struct ether_header *);
61493359Simp		m->m_pkthdr.rcvif = ifp;
61546492Swpaul
61694405Simp#ifdef WI_HOSTAP
61794405Simp		if (rx_frame.wi_status == WI_STAT_MGMT &&
61894405Simp		    sc->wi_ptype == WI_PORTTYPE_AP) {
61994405Simp			if ((WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len + 2) >
62094405Simp			    MCLBYTES) {
62194405Simp				device_printf(sc->dev, "oversized mgmt packet "
62294405Simp				    "received in hostap mode "
62394405Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
62494405Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
62594405Simp				m_freem(m);
62694405Simp				ifp->if_ierrors++;
62794405Simp				return;
62894405Simp			}
62994405Simp
63094405Simp			/* Put the whole header in there. */
63194405Simp			bcopy(&rx_frame, mtod(m, void *),
63294405Simp			    sizeof(struct wi_frame));
63394405Simp			if (wi_read_data(sc, id, WI_802_11_OFFSET_RAW,
63494405Simp			    mtod(m, caddr_t) + WI_802_11_OFFSET_RAW,
63594405Simp			    rx_frame.wi_dat_len + 2)) {
63694405Simp				m_freem(m);
63794405Simp				ifp->if_ierrors++;
63894405Simp				return;
63994405Simp			}
64094405Simp			m->m_pkthdr.len = m->m_len =
64194405Simp			    WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len;
64294405Simp			/* XXX: consider giving packet to bhp? */
64394405Simp			wihap_mgmt_input(sc, &rx_frame, m);
64494405Simp			return;
64594405Simp		}
64694405Simp#endif /* WI_HOSTAP */
64794405Simp
64893359Simp		if (rx_frame.wi_status == WI_STAT_1042 ||
64993359Simp		    rx_frame.wi_status == WI_STAT_TUNNEL ||
65093359Simp		    rx_frame.wi_status == WI_STAT_WMP_MSG) {
65193359Simp			if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) {
65293359Simp				device_printf(sc->dev,
65393359Simp				    "oversized packet received "
65493359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
65593359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
65693359Simp				m_freem(m);
65793359Simp				ifp->if_ierrors++;
65893359Simp				return;
65993359Simp			}
66093359Simp			m->m_pkthdr.len = m->m_len =
66193359Simp			    rx_frame.wi_dat_len + WI_SNAPHDR_LEN;
66293359Simp
66393359Simp#if 0
66493359Simp			bcopy((char *)&rx_frame.wi_addr1,
66593359Simp			    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
66693359Simp			if (sc->wi_ptype == WI_PORTTYPE_ADHOC) {
66793359Simp				bcopy((char *)&rx_frame.wi_addr2,
66893359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
66993359Simp			} else {
67093359Simp				bcopy((char *)&rx_frame.wi_addr3,
67193359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
67293359Simp			}
67393359Simp#else
67493359Simp			bcopy((char *)&rx_frame.wi_dst_addr,
67593359Simp				(char *)&eh->ether_dhost, ETHER_ADDR_LEN);
67693359Simp			bcopy((char *)&rx_frame.wi_src_addr,
67793359Simp				(char *)&eh->ether_shost, ETHER_ADDR_LEN);
67893359Simp#endif
67993359Simp
68093359Simp			bcopy((char *)&rx_frame.wi_type,
68193359Simp			    (char *)&eh->ether_type, ETHER_TYPE_LEN);
68293359Simp
68393359Simp			if (wi_read_data(sc, id, WI_802_11_OFFSET,
68493359Simp			    mtod(m, caddr_t) + sizeof(struct ether_header),
68593359Simp			    m->m_len + 2)) {
68693359Simp				m_freem(m);
68793359Simp				ifp->if_ierrors++;
68893359Simp				return;
68993359Simp			}
69093359Simp		} else {
69193359Simp			if((rx_frame.wi_dat_len +
69293359Simp			    sizeof(struct ether_header)) > MCLBYTES) {
69393359Simp				device_printf(sc->dev,
69493359Simp				    "oversized packet received "
69593359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
69693359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
69793359Simp				m_freem(m);
69893359Simp				ifp->if_ierrors++;
69993359Simp				return;
70093359Simp			}
70193359Simp			m->m_pkthdr.len = m->m_len =
70293359Simp			    rx_frame.wi_dat_len + sizeof(struct ether_header);
70393359Simp
70493359Simp			if (wi_read_data(sc, id, WI_802_3_OFFSET,
70593359Simp			    mtod(m, caddr_t), m->m_len + 2)) {
70693359Simp				m_freem(m);
70793359Simp				ifp->if_ierrors++;
70893359Simp				return;
70993359Simp			}
71093359Simp		}
71193359Simp
71293359Simp		ifp->if_ipackets++;
71393359Simp
71494405Simp#ifdef WI_HOSTAP
71594405Simp		if (sc->wi_ptype == WI_PORTTYPE_AP) {
71694405Simp			/*
71794405Simp			 * Give host AP code first crack at data
71894405Simp			 * packets.  If it decides to handle it (or
71994405Simp			 * drop it), it will return a non-zero.
72094405Simp			 * Otherwise, it is destined for this host.
72194405Simp			 */
72294405Simp			if (wihap_data_input(sc, &rx_frame, m))
72394405Simp				return;
72494405Simp		}
72594405Simp#endif
72693359Simp		/* Receive packet. */
72793359Simp		m_adj(m, sizeof(struct ether_header));
72853702Swpaul#ifdef WICACHE
72993359Simp		wi_cache_store(sc, eh, m, rx_frame.wi_q_info);
73053702Swpaul#endif
73193359Simp		ether_input(ifp, eh, m);
73293359Simp	}
73346492Swpaul}
73446492Swpaul
73588546Salfredstatic void
73688546Salfredwi_txeof(sc, status)
73746492Swpaul	struct wi_softc		*sc;
73846492Swpaul	int			status;
73946492Swpaul{
74046492Swpaul	struct ifnet		*ifp;
74146492Swpaul
74246492Swpaul	ifp = &sc->arpcom.ac_if;
74346492Swpaul
74446492Swpaul	ifp->if_timer = 0;
74546492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
74646492Swpaul
74746492Swpaul	if (status & WI_EV_TX_EXC)
74846492Swpaul		ifp->if_oerrors++;
74946492Swpaul	else
75046492Swpaul		ifp->if_opackets++;
75146492Swpaul
75246492Swpaul	return;
75346492Swpaul}
75446492Swpaul
75588546Salfredvoid
75688546Salfredwi_inquire(xsc)
75746492Swpaul	void			*xsc;
75846492Swpaul{
75946492Swpaul	struct wi_softc		*sc;
76046492Swpaul	struct ifnet		*ifp;
76194405Simp	int s;
76246492Swpaul
76346492Swpaul	sc = xsc;
76446492Swpaul	ifp = &sc->arpcom.ac_if;
76546492Swpaul
76646492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
76746492Swpaul
76846492Swpaul	/* Don't do this while we're transmitting */
76946492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
77046492Swpaul		return;
77146492Swpaul
77294405Simp	s = splimp();
77392457Simp	wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0);
77494405Simp	splx(s);
77546492Swpaul
77646492Swpaul	return;
77746492Swpaul}
77846492Swpaul
77988546Salfredvoid
78088546Salfredwi_update_stats(sc)
78146492Swpaul	struct wi_softc		*sc;
78246492Swpaul{
78346492Swpaul	struct wi_ltv_gen	gen;
78446492Swpaul	u_int16_t		id;
78546492Swpaul	struct ifnet		*ifp;
78646492Swpaul	u_int32_t		*ptr;
78775373Salfred	int			len, i;
78846492Swpaul	u_int16_t		t;
78946492Swpaul
79046492Swpaul	ifp = &sc->arpcom.ac_if;
79146492Swpaul
79246492Swpaul	id = CSR_READ_2(sc, WI_INFO_FID);
79346492Swpaul
79446492Swpaul	wi_read_data(sc, id, 0, (char *)&gen, 4);
79546492Swpaul
79693359Simp	/*
79793359Simp	 * if we just got our scan results, copy it over into the scan buffer
79893359Simp	 * so we can return it to anyone that asks for it. (add a little
79993359Simp	 * compatibility with the prism2 scanning mechanism)
80093359Simp	 */
80193359Simp	if (gen.wi_type == WI_INFO_SCAN_RESULTS)
80293359Simp	{
80393359Simp		sc->wi_scanbuf_len = gen.wi_len;
80493359Simp		wi_read_data(sc, id, 4, (char *)sc->wi_scanbuf,
80593359Simp		    sc->wi_scanbuf_len * 2);
80693359Simp
80746492Swpaul		return;
80893359Simp	}
80993359Simp	else if (gen.wi_type != WI_INFO_COUNTERS)
81093359Simp		return;
81146492Swpaul
81275373Salfred	len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ?
81375373Salfred		gen.wi_len - 1 : sizeof(sc->wi_stats) / 4;
81446492Swpaul	ptr = (u_int32_t *)&sc->wi_stats;
81546492Swpaul
81675373Salfred	for (i = 0; i < len - 1; i++) {
81746492Swpaul		t = CSR_READ_2(sc, WI_DATA1);
81846492Swpaul#ifdef WI_HERMES_STATS_WAR
81946492Swpaul		if (t > 0xF000)
82046492Swpaul			t = ~t & 0xFFFF;
82146492Swpaul#endif
82246492Swpaul		ptr[i] += t;
82346492Swpaul	}
82446492Swpaul
82546492Swpaul	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
82646492Swpaul	    sc->wi_stats.wi_tx_multi_retries +
82746492Swpaul	    sc->wi_stats.wi_tx_retry_limit;
82846492Swpaul
82946492Swpaul	return;
83046492Swpaul}
83146492Swpaul
83288546Salfredstatic void
83388546Salfredwi_intr(xsc)
83453702Swpaul	void		*xsc;
83546492Swpaul{
83653702Swpaul	struct wi_softc		*sc = xsc;
83746492Swpaul	struct ifnet		*ifp;
83846492Swpaul	u_int16_t		status;
83946492Swpaul
84067092Swpaul	WI_LOCK(sc);
84167092Swpaul
84246492Swpaul	ifp = &sc->arpcom.ac_if;
84346492Swpaul
84475373Salfred	if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) {
84546492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
84646492Swpaul		CSR_WRITE_2(sc, WI_INT_EN, 0);
84767092Swpaul		WI_UNLOCK(sc);
84846492Swpaul		return;
84946492Swpaul	}
85046492Swpaul
85146492Swpaul	/* Disable interrupts. */
85246492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
85346492Swpaul
85446492Swpaul	status = CSR_READ_2(sc, WI_EVENT_STAT);
85546492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS);
85646492Swpaul
85746492Swpaul	if (status & WI_EV_RX) {
85846492Swpaul		wi_rxeof(sc);
85946492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
86046492Swpaul	}
86146492Swpaul
86246492Swpaul	if (status & WI_EV_TX) {
86346492Swpaul		wi_txeof(sc, status);
86446492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX);
86546492Swpaul	}
86646492Swpaul
86746492Swpaul	if (status & WI_EV_ALLOC) {
86846492Swpaul		int			id;
86975373Salfred
87046492Swpaul		id = CSR_READ_2(sc, WI_ALLOC_FID);
87146492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
87246492Swpaul		if (id == sc->wi_tx_data_id)
87346492Swpaul			wi_txeof(sc, status);
87446492Swpaul	}
87546492Swpaul
87646492Swpaul	if (status & WI_EV_INFO) {
87746492Swpaul		wi_update_stats(sc);
87846492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
87946492Swpaul	}
88046492Swpaul
88146492Swpaul	if (status & WI_EV_TX_EXC) {
88246492Swpaul		wi_txeof(sc, status);
88346492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
88446492Swpaul	}
88546492Swpaul
88646492Swpaul	if (status & WI_EV_INFO_DROP) {
88746492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP);
88846492Swpaul	}
88946492Swpaul
89046492Swpaul	/* Re-enable interrupts. */
89146492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
89246492Swpaul
89375373Salfred	if (ifp->if_snd.ifq_head != NULL) {
89446492Swpaul		wi_start(ifp);
89575373Salfred	}
89646492Swpaul
89767092Swpaul	WI_UNLOCK(sc);
89867092Swpaul
89946492Swpaul	return;
90046492Swpaul}
90146492Swpaul
90288546Salfredstatic int
90392457Simpwi_cmd(sc, cmd, val0, val1, val2)
90446492Swpaul	struct wi_softc		*sc;
90546492Swpaul	int			cmd;
90692457Simp	int			val0;
90792457Simp	int			val1;
90892457Simp	int			val2;
90946492Swpaul{
91046492Swpaul	int			i, s = 0;
91146492Swpaul
91270073Swpaul	/* wait for the busy bit to clear */
91375331Simp	for (i = 500; i > 0; i--) {	/* 5s */
91470073Swpaul		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) {
91570073Swpaul			break;
91670073Swpaul		}
91770073Swpaul		DELAY(10*1000);	/* 10 m sec */
91870073Swpaul	}
91975229Salfred	if (i == 0) {
92090580Sbrooks		device_printf(sc->dev, "wi_cmd: busy bit won't clear.\n" );
92170073Swpaul		return(ETIMEDOUT);
92270073Swpaul	}
92370073Swpaul
92492457Simp	CSR_WRITE_2(sc, WI_PARAM0, val0);
92592457Simp	CSR_WRITE_2(sc, WI_PARAM1, val1);
92692457Simp	CSR_WRITE_2(sc, WI_PARAM2, val2);
92746492Swpaul	CSR_WRITE_2(sc, WI_COMMAND, cmd);
92846492Swpaul
92946492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
93046492Swpaul		/*
93146492Swpaul		 * Wait for 'command complete' bit to be
93246492Swpaul		 * set in the event status register.
93346492Swpaul		 */
93490580Sbrooks		s = CSR_READ_2(sc, WI_EVENT_STAT);
93590580Sbrooks		if (s & WI_EV_CMD) {
93646492Swpaul			/* Ack the event and read result code. */
93746492Swpaul			s = CSR_READ_2(sc, WI_STATUS);
93846492Swpaul			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
93946492Swpaul#ifdef foo
94046492Swpaul			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
94146492Swpaul				return(EIO);
94246492Swpaul#endif
94346492Swpaul			if (s & WI_STAT_CMD_RESULT)
94446492Swpaul				return(EIO);
94546492Swpaul			break;
94646492Swpaul		}
94790580Sbrooks		DELAY(WI_DELAY);
94846492Swpaul	}
94946492Swpaul
95090580Sbrooks	if (i == WI_TIMEOUT) {
95190580Sbrooks		device_printf(sc->dev,
95292457Simp		    "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
95346492Swpaul		return(ETIMEDOUT);
95490580Sbrooks	}
95546492Swpaul
95646492Swpaul	return(0);
95746492Swpaul}
95846492Swpaul
95988546Salfredstatic void
96088546Salfredwi_reset(sc)
96146492Swpaul	struct wi_softc		*sc;
96246492Swpaul{
96394397Simp#define WI_INIT_TRIES 3
96475150Simp	int i;
96593733Simp	int tries;
96675149Simp
96793733Simp	/* Symbol firmware cannot be initialized more than once */
96893733Simp	if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_enabled)
96993733Simp		return;
97093733Simp	if (sc->sc_firmware_type == WI_SYMBOL)
97193733Simp		tries = 1;
97293733Simp	else
97393733Simp		tries = WI_INIT_TRIES;
97493733Simp
97593733Simp	for (i = 0; i < tries; i++) {
97692457Simp		if (wi_cmd(sc, WI_CMD_INI, 0, 0, 0) == 0)
97775149Simp			break;
97890580Sbrooks		DELAY(WI_DELAY * 1000);
97975149Simp	}
98094397Simp	sc->sc_enabled = 1;
98194397Simp
98294397Simp	if (i == tries) {
98353702Swpaul		device_printf(sc->dev, "init failed\n");
98494397Simp		return;
98594397Simp	}
98675373Salfred
98746492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
98846492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
98946492Swpaul
99046492Swpaul	/* Calibrate timer. */
99146492Swpaul	WI_SETVAL(WI_RID_TICK_TIME, 8);
99270073Swpaul
99346492Swpaul	return;
99446492Swpaul}
99546492Swpaul
99646492Swpaul/*
99746492Swpaul * Read an LTV record from the NIC.
99846492Swpaul */
99988546Salfredstatic int
100088546Salfredwi_read_record(sc, ltv)
100146492Swpaul	struct wi_softc		*sc;
100246492Swpaul	struct wi_ltv_gen	*ltv;
100346492Swpaul{
100446492Swpaul	u_int16_t		*ptr;
100546492Swpaul	int			i, len, code;
100670073Swpaul	struct wi_ltv_gen	*oltv, p2ltv;
100746492Swpaul
100870073Swpaul	oltv = ltv;
100993733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
101070073Swpaul		switch (ltv->wi_type) {
101170073Swpaul		case WI_RID_ENCRYPTION:
101270073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
101370073Swpaul			p2ltv.wi_len = 2;
101470073Swpaul			ltv = &p2ltv;
101570073Swpaul			break;
101670073Swpaul		case WI_RID_TX_CRYPT_KEY:
101770073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
101870073Swpaul			p2ltv.wi_len = 2;
101970073Swpaul			ltv = &p2ltv;
102070073Swpaul			break;
102170073Swpaul		}
102270073Swpaul	}
102370073Swpaul
102446492Swpaul	/* Tell the NIC to enter record read mode. */
102592457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type, 0, 0))
102646492Swpaul		return(EIO);
102746492Swpaul
102847789Swpaul	/* Seek to the record. */
102947789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
103047789Swpaul		return(EIO);
103146492Swpaul
103246492Swpaul	/*
103346492Swpaul	 * Read the length and record type and make sure they
103446492Swpaul	 * match what we expect (this verifies that we have enough
103547401Swpaul	 * room to hold all of the returned data).
103646492Swpaul	 */
103746492Swpaul	len = CSR_READ_2(sc, WI_DATA1);
103846492Swpaul	if (len > ltv->wi_len)
103946492Swpaul		return(ENOSPC);
104046492Swpaul	code = CSR_READ_2(sc, WI_DATA1);
104146492Swpaul	if (code != ltv->wi_type)
104246492Swpaul		return(EIO);
104346492Swpaul
104446492Swpaul	ltv->wi_len = len;
104546492Swpaul	ltv->wi_type = code;
104646492Swpaul
104746492Swpaul	/* Now read the data. */
104846492Swpaul	ptr = &ltv->wi_val;
104946492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
105046492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
105146492Swpaul
105293733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
105370073Swpaul		switch (oltv->wi_type) {
105470073Swpaul		case WI_RID_TX_RATE:
105570073Swpaul		case WI_RID_CUR_TX_RATE:
105670073Swpaul			switch (ltv->wi_val) {
105770073Swpaul			case 1: oltv->wi_val = 1; break;
105870073Swpaul			case 2: oltv->wi_val = 2; break;
105970073Swpaul			case 3:	oltv->wi_val = 6; break;
106070073Swpaul			case 4: oltv->wi_val = 5; break;
106170073Swpaul			case 7: oltv->wi_val = 7; break;
106270073Swpaul			case 8: oltv->wi_val = 11; break;
106370073Swpaul			case 15: oltv->wi_val = 3; break;
106470073Swpaul			default: oltv->wi_val = 0x100 + ltv->wi_val; break;
106570073Swpaul			}
106670073Swpaul			break;
106770073Swpaul		case WI_RID_ENCRYPTION:
106870073Swpaul			oltv->wi_len = 2;
106970073Swpaul			if (ltv->wi_val & 0x01)
107070073Swpaul				oltv->wi_val = 1;
107170073Swpaul			else
107270073Swpaul				oltv->wi_val = 0;
107370073Swpaul			break;
107470073Swpaul		case WI_RID_TX_CRYPT_KEY:
107570073Swpaul			oltv->wi_len = 2;
107670073Swpaul			oltv->wi_val = ltv->wi_val;
107770073Swpaul			break;
107894405Simp		case WI_RID_CNFAUTHMODE:
107991695Simp                        oltv->wi_len = 2;
108091695Simp			if (le16toh(ltv->wi_val) & 0x01)
108191695Simp				oltv->wi_val = htole16(1);
108291695Simp			else if (le16toh(ltv->wi_val) & 0x02)
108391695Simp				oltv->wi_val = htole16(2);
108491695Simp			break;
108570073Swpaul		}
108670073Swpaul	}
108770073Swpaul
108846492Swpaul	return(0);
108946492Swpaul}
109046492Swpaul
109146492Swpaul/*
109246492Swpaul * Same as read, except we inject data instead of reading it.
109346492Swpaul */
109488546Salfredstatic int
109588546Salfredwi_write_record(sc, ltv)
109646492Swpaul	struct wi_softc		*sc;
109746492Swpaul	struct wi_ltv_gen	*ltv;
109846492Swpaul{
109946492Swpaul	u_int16_t		*ptr;
110046492Swpaul	int			i;
110170073Swpaul	struct wi_ltv_gen	p2ltv;
110246492Swpaul
110393733Simp	if (sc->sc_firmware_type != WI_LUCENT) {
110470073Swpaul		switch (ltv->wi_type) {
110570073Swpaul		case WI_RID_TX_RATE:
110670073Swpaul			p2ltv.wi_type = WI_RID_TX_RATE;
110770073Swpaul			p2ltv.wi_len = 2;
110870073Swpaul			switch (ltv->wi_val) {
110970073Swpaul			case 1: p2ltv.wi_val = 1; break;
111070073Swpaul			case 2: p2ltv.wi_val = 2; break;
111170073Swpaul			case 3:	p2ltv.wi_val = 15; break;
111270073Swpaul			case 5: p2ltv.wi_val = 4; break;
111370073Swpaul			case 6: p2ltv.wi_val = 3; break;
111470073Swpaul			case 7: p2ltv.wi_val = 7; break;
111570073Swpaul			case 11: p2ltv.wi_val = 8; break;
111670073Swpaul			default: return EINVAL;
111770073Swpaul			}
111870073Swpaul			ltv = &p2ltv;
111970073Swpaul			break;
112070073Swpaul		case WI_RID_ENCRYPTION:
112170073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
112270073Swpaul			p2ltv.wi_len = 2;
112394405Simp			if (le16toh(ltv->wi_val)) {
112493825Simp				p2ltv.wi_val =htole16(PRIVACY_INVOKED |
112593825Simp				    EXCLUDE_UNENCRYPTED);
112694405Simp#ifdef WI_HOSTAP
112794405Simp				if (sc->wi_ptype == WI_PORTTYPE_AP)
112894405Simp					/*
112994405Simp					 * Disable tx encryption...
113094405Simp					 * it's broken.
113194405Simp					 */
113294405Simp					p2ltv.wi_val |= htole16(HOST_ENCRYPT);
113394405Simp#endif
113494405Simp			} else
113593825Simp				p2ltv.wi_val =
113693825Simp				    htole16(HOST_ENCRYPT | HOST_DECRYPT);
113770073Swpaul			ltv = &p2ltv;
113870073Swpaul			break;
113970073Swpaul		case WI_RID_TX_CRYPT_KEY:
114070073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
114170073Swpaul			p2ltv.wi_len = 2;
114270073Swpaul			p2ltv.wi_val = ltv->wi_val;
114370073Swpaul			ltv = &p2ltv;
114470073Swpaul			break;
114570073Swpaul		case WI_RID_DEFLT_CRYPT_KEYS:
114670073Swpaul		    {
114770073Swpaul			int error;
114891548Sbrooks			int keylen;
114970073Swpaul			struct wi_ltv_str	ws;
115074998Swpaul			struct wi_ltv_keys	*wk =
115174998Swpaul			    (struct wi_ltv_keys *)ltv;
115274998Swpaul
115391548Sbrooks			keylen = wk->wi_keys[sc->wi_tx_key].wi_keylen;
115491548Sbrooks
115570073Swpaul			for (i = 0; i < 4; i++) {
115691548Sbrooks				bzero(&ws, sizeof(ws));
115791548Sbrooks				ws.wi_len = (keylen > 5) ? 8 : 4;
115870073Swpaul				ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i;
115974998Swpaul				memcpy(ws.wi_str,
116091548Sbrooks				    &wk->wi_keys[i].wi_keydat, keylen);
116170073Swpaul				error = wi_write_record(sc,
116270073Swpaul				    (struct wi_ltv_gen *)&ws);
116370073Swpaul				if (error)
116470073Swpaul					return error;
116570073Swpaul			}
116670073Swpaul			return 0;
116770073Swpaul		    }
116894405Simp		case WI_RID_CNFAUTHMODE:
116994405Simp			p2ltv.wi_type = WI_RID_CNFAUTHMODE;
117091695Simp			p2ltv.wi_len = 2;
117191695Simp			if (le16toh(ltv->wi_val) == 1)
117291695Simp				p2ltv.wi_val = htole16(0x01);
117391695Simp			else if (le16toh(ltv->wi_val) == 2)
117491695Simp				p2ltv.wi_val = htole16(0x02);
117591695Simp			ltv = &p2ltv;
117691695Simp			break;
117770073Swpaul		}
117870073Swpaul	}
117970073Swpaul
118047789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
118147789Swpaul		return(EIO);
118246492Swpaul
118346492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len);
118446492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type);
118546492Swpaul
118646492Swpaul	ptr = &ltv->wi_val;
118746492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
118846492Swpaul		CSR_WRITE_2(sc, WI_DATA1, ptr[i]);
118946492Swpaul
119092457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type, 0, 0))
119146492Swpaul		return(EIO);
119246492Swpaul
119346492Swpaul	return(0);
119446492Swpaul}
119546492Swpaul
119688546Salfredstatic int
119788546Salfredwi_seek(sc, id, off, chan)
119846492Swpaul	struct wi_softc		*sc;
119946492Swpaul	int			id, off, chan;
120046492Swpaul{
120146492Swpaul	int			i;
120246492Swpaul	int			selreg, offreg;
120375373Salfred	int			status;
120446492Swpaul
120546492Swpaul	switch (chan) {
120646492Swpaul	case WI_BAP0:
120746492Swpaul		selreg = WI_SEL0;
120846492Swpaul		offreg = WI_OFF0;
120946492Swpaul		break;
121046492Swpaul	case WI_BAP1:
121146492Swpaul		selreg = WI_SEL1;
121246492Swpaul		offreg = WI_OFF1;
121346492Swpaul		break;
121446492Swpaul	default:
121553702Swpaul		device_printf(sc->dev, "invalid data path: %x\n", chan);
121646492Swpaul		return(EIO);
121746492Swpaul	}
121846492Swpaul
121946492Swpaul	CSR_WRITE_2(sc, selreg, id);
122046492Swpaul	CSR_WRITE_2(sc, offreg, off);
122146492Swpaul
122246492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
122375373Salfred		status = CSR_READ_2(sc, offreg);
122475373Salfred		if (!(status & (WI_OFF_BUSY|WI_OFF_ERR)))
122546492Swpaul			break;
122690580Sbrooks		DELAY(WI_DELAY);
122746492Swpaul	}
122846492Swpaul
122975373Salfred	if (i == WI_TIMEOUT) {
123075373Salfred		device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n",
123175373Salfred			id, off, status);
123246492Swpaul		return(ETIMEDOUT);
123375373Salfred	}
123446492Swpaul
123546492Swpaul	return(0);
123646492Swpaul}
123746492Swpaul
123888546Salfredstatic int
123988546Salfredwi_read_data(sc, id, off, buf, len)
124046492Swpaul	struct wi_softc		*sc;
124146492Swpaul	int			id, off;
124246492Swpaul	caddr_t			buf;
124346492Swpaul	int			len;
124446492Swpaul{
124546492Swpaul	int			i;
124646492Swpaul	u_int16_t		*ptr;
124746492Swpaul
124846492Swpaul	if (wi_seek(sc, id, off, WI_BAP1))
124946492Swpaul		return(EIO);
125046492Swpaul
125146492Swpaul	ptr = (u_int16_t *)buf;
125246492Swpaul	for (i = 0; i < len / 2; i++)
125346492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
125446492Swpaul
125546492Swpaul	return(0);
125646492Swpaul}
125746492Swpaul
125846492Swpaul/*
125946492Swpaul * According to the comments in the HCF Light code, there is a bug in
126046492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where
126146492Swpaul * the chip's internal autoincrement counter gets thrown off during
126246492Swpaul * data writes: the autoincrement is missed, causing one data word to
126346492Swpaul * be overwritten and subsequent words to be written to the wrong memory
126446492Swpaul * locations. The end result is that we could end up transmitting bogus
126546492Swpaul * frames without realizing it. The workaround for this is to write a
126646492Swpaul * couple of extra guard words after the end of the transfer, then
126746492Swpaul * attempt to read then back. If we fail to locate the guard words where
126846492Swpaul * we expect them, we preform the transfer over again.
126946492Swpaul */
127088546Salfredstatic int
127188546Salfredwi_write_data(sc, id, off, buf, len)
127246492Swpaul	struct wi_softc		*sc;
127346492Swpaul	int			id, off;
127446492Swpaul	caddr_t			buf;
127546492Swpaul	int			len;
127646492Swpaul{
127746492Swpaul	int			i;
127846492Swpaul	u_int16_t		*ptr;
127974838Salfred#ifdef WI_HERMES_AUTOINC_WAR
128074838Salfred	int			retries;
128146492Swpaul
128275373Salfred	retries = 512;
128346492Swpaulagain:
128446492Swpaul#endif
128546492Swpaul
128646492Swpaul	if (wi_seek(sc, id, off, WI_BAP0))
128746492Swpaul		return(EIO);
128846492Swpaul
128946492Swpaul	ptr = (u_int16_t *)buf;
129046492Swpaul	for (i = 0; i < (len / 2); i++)
129146492Swpaul		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
129246492Swpaul
129346492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
129446492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x1234);
129546492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x5678);
129646492Swpaul
129746492Swpaul	if (wi_seek(sc, id, off + len, WI_BAP0))
129846492Swpaul		return(EIO);
129946492Swpaul
130046492Swpaul	if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
130174998Swpaul	    CSR_READ_2(sc, WI_DATA0) != 0x5678) {
130274838Salfred		if (--retries >= 0)
130374838Salfred			goto again;
130474838Salfred		device_printf(sc->dev, "wi_write_data device timeout\n");
130574838Salfred		return (EIO);
130674838Salfred	}
130746492Swpaul#endif
130846492Swpaul
130946492Swpaul	return(0);
131046492Swpaul}
131146492Swpaul
131246492Swpaul/*
131346492Swpaul * Allocate a region of memory inside the NIC and zero
131446492Swpaul * it out.
131546492Swpaul */
131688546Salfredstatic int
131788546Salfredwi_alloc_nicmem(sc, len, id)
131846492Swpaul	struct wi_softc		*sc;
131946492Swpaul	int			len;
132046492Swpaul	int			*id;
132146492Swpaul{
132246492Swpaul	int			i;
132346492Swpaul
132492457Simp	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
132574998Swpaul		device_printf(sc->dev,
132674998Swpaul		    "failed to allocate %d bytes on NIC\n", len);
132746492Swpaul		return(ENOMEM);
132846492Swpaul	}
132946492Swpaul
133046492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
133146492Swpaul		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
133246492Swpaul			break;
133390580Sbrooks		DELAY(WI_DELAY);
133446492Swpaul	}
133546492Swpaul
133675373Salfred	if (i == WI_TIMEOUT) {
133775373Salfred		device_printf(sc->dev, "time out allocating memory on card\n");
133846492Swpaul		return(ETIMEDOUT);
133975373Salfred	}
134046492Swpaul
134146492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
134246492Swpaul	*id = CSR_READ_2(sc, WI_ALLOC_FID);
134346492Swpaul
134475373Salfred	if (wi_seek(sc, *id, 0, WI_BAP0)) {
134575373Salfred		device_printf(sc->dev, "seek failed while allocating memory on card\n");
134647789Swpaul		return(EIO);
134775373Salfred	}
134846492Swpaul
134946492Swpaul	for (i = 0; i < len / 2; i++)
135046492Swpaul		CSR_WRITE_2(sc, WI_DATA0, 0);
135146492Swpaul
135246492Swpaul	return(0);
135346492Swpaul}
135446492Swpaul
135588546Salfredstatic void
135688546Salfredwi_setmulti(sc)
135746492Swpaul	struct wi_softc		*sc;
135846492Swpaul{
135946492Swpaul	struct ifnet		*ifp;
136046492Swpaul	int			i = 0;
136146492Swpaul	struct ifmultiaddr	*ifma;
136246492Swpaul	struct wi_ltv_mcast	mcast;
136346492Swpaul
136446492Swpaul	ifp = &sc->arpcom.ac_if;
136546492Swpaul
136646492Swpaul	bzero((char *)&mcast, sizeof(mcast));
136746492Swpaul
136893756Simp	mcast.wi_type = WI_RID_MCAST_LIST;
136946492Swpaul	mcast.wi_len = (3 * 16) + 1;
137046492Swpaul
137146492Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
137246492Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
137346492Swpaul		return;
137446492Swpaul	}
137546492Swpaul
137672084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
137746492Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
137846492Swpaul			continue;
137946492Swpaul		if (i < 16) {
138046492Swpaul			bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
138146492Swpaul			    (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN);
138246492Swpaul			i++;
138346492Swpaul		} else {
138446492Swpaul			bzero((char *)&mcast, sizeof(mcast));
138546492Swpaul			break;
138646492Swpaul		}
138746492Swpaul	}
138846492Swpaul
138946492Swpaul	mcast.wi_len = (i * 3) + 1;
139046492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
139146492Swpaul
139246492Swpaul	return;
139346492Swpaul}
139446492Swpaul
139588546Salfredstatic void
139688546Salfredwi_setdef(sc, wreq)
139746492Swpaul	struct wi_softc		*sc;
139846492Swpaul	struct wi_req		*wreq;
139946492Swpaul{
140046492Swpaul	struct sockaddr_dl	*sdl;
140146492Swpaul	struct ifaddr		*ifa;
140246492Swpaul	struct ifnet		*ifp;
140346492Swpaul
140446492Swpaul	ifp = &sc->arpcom.ac_if;
140546492Swpaul
140646492Swpaul	switch(wreq->wi_type) {
140746492Swpaul	case WI_RID_MAC_NODE:
140883130Sjlemon		ifa = ifaddr_byindex(ifp->if_index);
140946492Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
141046492Swpaul		bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr,
141146492Swpaul		   ETHER_ADDR_LEN);
141246492Swpaul		bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN);
141346492Swpaul		break;
141446492Swpaul	case WI_RID_PORTTYPE:
141591695Simp		sc->wi_ptype = le16toh(wreq->wi_val[0]);
141646492Swpaul		break;
141746492Swpaul	case WI_RID_TX_RATE:
141891695Simp		sc->wi_tx_rate = le16toh(wreq->wi_val[0]);
141946492Swpaul		break;
142046492Swpaul	case WI_RID_MAX_DATALEN:
142191695Simp		sc->wi_max_data_len = le16toh(wreq->wi_val[0]);
142246492Swpaul		break;
142346492Swpaul	case WI_RID_RTS_THRESH:
142491695Simp		sc->wi_rts_thresh = le16toh(wreq->wi_val[0]);
142546492Swpaul		break;
142646492Swpaul	case WI_RID_SYSTEM_SCALE:
142791695Simp		sc->wi_ap_density = le16toh(wreq->wi_val[0]);
142846492Swpaul		break;
142946492Swpaul	case WI_RID_CREATE_IBSS:
143091695Simp		sc->wi_create_ibss = le16toh(wreq->wi_val[0]);
143146492Swpaul		break;
143246563Swpaul	case WI_RID_OWN_CHNL:
143391695Simp		sc->wi_channel = le16toh(wreq->wi_val[0]);
143446563Swpaul		break;
143546492Swpaul	case WI_RID_NODENAME:
143646492Swpaul		bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
143746492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30);
143846492Swpaul		break;
143946492Swpaul	case WI_RID_DESIRED_SSID:
144046492Swpaul		bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
144146492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30);
144246492Swpaul		break;
144346492Swpaul	case WI_RID_OWN_SSID:
144446492Swpaul		bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
144546492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30);
144646492Swpaul		break;
144746611Swpaul	case WI_RID_PM_ENABLED:
144891695Simp		sc->wi_pm_enabled = le16toh(wreq->wi_val[0]);
144946611Swpaul		break;
145091695Simp	case WI_RID_MICROWAVE_OVEN:
145191695Simp		sc->wi_mor_enabled = le16toh(wreq->wi_val[0]);
145291695Simp		break;
145346611Swpaul	case WI_RID_MAX_SLEEP:
145491695Simp		sc->wi_max_sleep = le16toh(wreq->wi_val[0]);
145546611Swpaul		break;
145694405Simp	case WI_RID_CNFAUTHMODE:
145791695Simp		sc->wi_authtype = le16toh(wreq->wi_val[0]);
145891695Simp		break;
145991695Simp	case WI_RID_ROAMING_MODE:
146091695Simp		sc->wi_roaming = le16toh(wreq->wi_val[0]);
146191695Simp		break;
146256965Swpaul	case WI_RID_ENCRYPTION:
146391695Simp		sc->wi_use_wep = le16toh(wreq->wi_val[0]);
146456965Swpaul		break;
146556965Swpaul	case WI_RID_TX_CRYPT_KEY:
146691695Simp		sc->wi_tx_key = le16toh(wreq->wi_val[0]);
146756965Swpaul		break;
146856965Swpaul	case WI_RID_DEFLT_CRYPT_KEYS:
146956965Swpaul		bcopy((char *)wreq, (char *)&sc->wi_keys,
147056965Swpaul		    sizeof(struct wi_ltv_keys));
147156965Swpaul		break;
147246492Swpaul	default:
147346492Swpaul		break;
147446492Swpaul	}
147546492Swpaul
147646563Swpaul	/* Reinitialize WaveLAN. */
147746563Swpaul	wi_init(sc);
147846563Swpaul
147946492Swpaul	return;
148046492Swpaul}
148146492Swpaul
148288546Salfredstatic int
148388546Salfredwi_ioctl(ifp, command, data)
148446492Swpaul	struct ifnet		*ifp;
148546492Swpaul	u_long			command;
148646492Swpaul	caddr_t			data;
148746492Swpaul{
148867092Swpaul	int			error = 0;
148977217Sphk	int			len;
149077217Sphk	u_int8_t		tmpkey[14];
149177217Sphk	char			tmpssid[IEEE80211_NWID_LEN];
149246492Swpaul	struct wi_softc		*sc;
149346492Swpaul	struct wi_req		wreq;
149446492Swpaul	struct ifreq		*ifr;
149577217Sphk	struct ieee80211req	*ireq;
149693593Sjhb	struct thread		*td = curthread;
149746492Swpaul
149846492Swpaul	sc = ifp->if_softc;
149967092Swpaul	WI_LOCK(sc);
150046492Swpaul	ifr = (struct ifreq *)data;
150177217Sphk	ireq = (struct ieee80211req *)data;
150246492Swpaul
150361818Sroberto	if (sc->wi_gone) {
150461818Sroberto		error = ENODEV;
150561818Sroberto		goto out;
150661818Sroberto	}
150746492Swpaul
150846492Swpaul	switch(command) {
150946492Swpaul	case SIOCSIFADDR:
151046492Swpaul	case SIOCGIFADDR:
151146492Swpaul	case SIOCSIFMTU:
151246492Swpaul		error = ether_ioctl(ifp, command, data);
151346492Swpaul		break;
151446492Swpaul	case SIOCSIFFLAGS:
151546492Swpaul		if (ifp->if_flags & IFF_UP) {
151646492Swpaul			if (ifp->if_flags & IFF_RUNNING &&
151746492Swpaul			    ifp->if_flags & IFF_PROMISC &&
151846492Swpaul			    !(sc->wi_if_flags & IFF_PROMISC)) {
151946492Swpaul				WI_SETVAL(WI_RID_PROMISC, 1);
152046492Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
152146492Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
152246492Swpaul			    sc->wi_if_flags & IFF_PROMISC) {
152346492Swpaul				WI_SETVAL(WI_RID_PROMISC, 0);
152446492Swpaul			} else
152546492Swpaul				wi_init(sc);
152646492Swpaul		} else {
152746492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
152846492Swpaul				wi_stop(sc);
152946492Swpaul			}
153046492Swpaul		}
153146492Swpaul		sc->wi_if_flags = ifp->if_flags;
153246492Swpaul		error = 0;
153346492Swpaul		break;
153477217Sphk	case SIOCSIFMEDIA:
153577217Sphk	case SIOCGIFMEDIA:
153677217Sphk		error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
153777217Sphk		break;
153846492Swpaul	case SIOCADDMULTI:
153946492Swpaul	case SIOCDELMULTI:
154046492Swpaul		wi_setmulti(sc);
154146492Swpaul		error = 0;
154246492Swpaul		break;
154346492Swpaul	case SIOCGWAVELAN:
154446492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
154546492Swpaul		if (error)
154646492Swpaul			break;
154793833Simp		if (wreq.wi_len > WI_MAX_DATALEN) {
154893833Simp			error = EINVAL;
154993833Simp			break;
155093833Simp		}
155165581Swpaul		/* Don't show WEP keys to non-root users. */
155293593Sjhb		if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(td))
155365581Swpaul			break;
155446492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
155546492Swpaul			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
155646492Swpaul			    sizeof(sc->wi_stats));
155746492Swpaul			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
155856965Swpaul		} else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) {
155956965Swpaul			bcopy((char *)&sc->wi_keys, (char *)&wreq,
156056965Swpaul			    sizeof(struct wi_ltv_keys));
156153702Swpaul		}
156253702Swpaul#ifdef WICACHE
156353702Swpaul		else if (wreq.wi_type == WI_RID_ZERO_CACHE) {
156453702Swpaul			sc->wi_sigitems = sc->wi_nextitem = 0;
156553702Swpaul		} else if (wreq.wi_type == WI_RID_READ_CACHE) {
156653702Swpaul			char *pt = (char *)&wreq.wi_val;
156753702Swpaul			bcopy((char *)&sc->wi_sigitems,
156853702Swpaul			    (char *)pt, sizeof(int));
156953702Swpaul			pt += (sizeof (int));
157053702Swpaul			wreq.wi_len = sizeof(int) / 2;
157153702Swpaul			bcopy((char *)&sc->wi_sigcache, (char *)pt,
157253702Swpaul			    sizeof(struct wi_sigcache) * sc->wi_sigitems);
157353702Swpaul			wreq.wi_len += ((sizeof(struct wi_sigcache) *
157453702Swpaul			    sc->wi_sigitems) / 2) + 1;
157553702Swpaul		}
157653702Swpaul#endif
157793359Simp		else if (wreq.wi_type == WI_RID_PROCFRAME) {
157893359Simp			wreq.wi_len = 2;
157993359Simp			wreq.wi_val[0] = sc->wi_procframe;
158093359Simp		} else if (wreq.wi_type == WI_RID_PRISM2) {
158193359Simp			wreq.wi_len = 2;
158293733Simp			wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT;
158393733Simp		} else if (wreq.wi_type == WI_RID_SCAN_RES &&
158493733Simp		    sc->sc_firmware_type == WI_LUCENT) {
158593359Simp			memcpy((char *)wreq.wi_val, (char *)sc->wi_scanbuf,
158693359Simp			    sc->wi_scanbuf_len * 2);
158793359Simp			wreq.wi_len = sc->wi_scanbuf_len;
158893359Simp		} else {
158946492Swpaul			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
159046492Swpaul				error = EINVAL;
159146492Swpaul				break;
159246492Swpaul			}
159346492Swpaul		}
159446492Swpaul		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
159546492Swpaul		break;
159646492Swpaul	case SIOCSWAVELAN:
159793593Sjhb		if ((error = suser(td)))
159861818Sroberto			goto out;
159946492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
160046492Swpaul		if (error)
160146492Swpaul			break;
160293833Simp		if (wreq.wi_len > WI_MAX_DATALEN) {
160393833Simp			error = EINVAL;
160493833Simp			break;
160593833Simp		}
160646492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
160746492Swpaul			error = EINVAL;
160846492Swpaul			break;
160946492Swpaul		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
161046492Swpaul			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
161146492Swpaul			    wreq.wi_len);
161293359Simp		} else if (wreq.wi_type == WI_RID_PROCFRAME) {
161393359Simp			sc->wi_procframe = wreq.wi_val[0];
161493359Simp		/*
161593359Simp		 * if we're getting a scan request from a wavelan card
161693359Simp		 * (non-prism2), send out a cmd_inquire to the card to scan
161793359Simp		 * results for the scan will be received through the info
161893359Simp		 * interrupt handler. otherwise the scan request can be
161993359Simp		 * directly handled by a prism2 card's rid interface.
162093359Simp		 */
162193733Simp		} else if (wreq.wi_type == WI_RID_SCAN_REQ &&
162293733Simp		    sc->sc_firmware_type == WI_LUCENT) {
162393359Simp			wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
162446492Swpaul		} else {
162546492Swpaul			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
162646492Swpaul			if (!error)
162746492Swpaul				wi_setdef(sc, &wreq);
162846492Swpaul		}
162946492Swpaul		break;
163093359Simp	case SIOCGPRISM2DEBUG:
163193359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
163293359Simp		if (error)
163393359Simp			break;
163493733Simp		if (!(ifp->if_flags & IFF_RUNNING) ||
163593733Simp		    sc->sc_firmware_type == WI_LUCENT) {
163693359Simp			error = EIO;
163793359Simp			break;
163893359Simp		}
163993359Simp		error = wi_get_debug(sc, &wreq);
164093359Simp		if (error == 0)
164193359Simp			error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
164293359Simp		break;
164393359Simp	case SIOCSPRISM2DEBUG:
164493593Sjhb		if ((error = suser(td)))
164593359Simp			goto out;
164693359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
164793359Simp		if (error)
164893359Simp			break;
164993359Simp		error = wi_set_debug(sc, &wreq);
165093359Simp		break;
165177217Sphk	case SIOCG80211:
165277217Sphk		switch(ireq->i_type) {
165377217Sphk		case IEEE80211_IOC_SSID:
165477217Sphk			if(ireq->i_val == -1) {
165577217Sphk				bzero(tmpssid, IEEE80211_NWID_LEN);
165677217Sphk				error = wi_get_cur_ssid(sc, tmpssid, &len);
165777217Sphk				if (error != 0)
165877217Sphk					break;
165977217Sphk				error = copyout(tmpssid, ireq->i_data,
166077217Sphk					IEEE80211_NWID_LEN);
166177217Sphk				ireq->i_len = len;
166277217Sphk			} else if (ireq->i_val == 0) {
166377217Sphk				error = copyout(sc->wi_net_name,
166477217Sphk				    ireq->i_data,
166577217Sphk				    IEEE80211_NWID_LEN);
166677217Sphk				ireq->i_len = IEEE80211_NWID_LEN;
166777217Sphk			} else
166877217Sphk				error = EINVAL;
166977217Sphk			break;
167077217Sphk		case IEEE80211_IOC_NUMSSIDS:
167177217Sphk			ireq->i_val = 1;
167277217Sphk			break;
167377217Sphk		case IEEE80211_IOC_WEP:
167477217Sphk			if(!sc->wi_has_wep) {
167577217Sphk				ireq->i_val = IEEE80211_WEP_NOSUP;
167677217Sphk			} else {
167777217Sphk				if(sc->wi_use_wep) {
167877217Sphk					ireq->i_val =
167977217Sphk					    IEEE80211_WEP_MIXED;
168077217Sphk				} else {
168177217Sphk					ireq->i_val =
168277217Sphk					    IEEE80211_WEP_OFF;
168377217Sphk				}
168477217Sphk			}
168577217Sphk			break;
168677217Sphk		case IEEE80211_IOC_WEPKEY:
168777217Sphk			if(!sc->wi_has_wep ||
168877217Sphk			    ireq->i_val < 0 || ireq->i_val > 3) {
168977217Sphk				error = EINVAL;
169077217Sphk				break;
169177217Sphk			}
169277217Sphk			len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen;
169393593Sjhb			if (suser(td))
169477217Sphk				bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
169577217Sphk				    tmpkey, len);
169677217Sphk			else
169777217Sphk				bzero(tmpkey, len);
169877217Sphk
169977217Sphk			ireq->i_len = len;
170077217Sphk			error = copyout(tmpkey, ireq->i_data, len);
170177217Sphk
170277217Sphk			break;
170377217Sphk		case IEEE80211_IOC_NUMWEPKEYS:
170477217Sphk			if(!sc->wi_has_wep)
170577217Sphk				error = EINVAL;
170677217Sphk			else
170777217Sphk				ireq->i_val = 4;
170877217Sphk			break;
170977217Sphk		case IEEE80211_IOC_WEPTXKEY:
171077217Sphk			if(!sc->wi_has_wep)
171177217Sphk				error = EINVAL;
171277217Sphk			else
171377217Sphk				ireq->i_val = sc->wi_tx_key;
171477217Sphk			break;
171577217Sphk		case IEEE80211_IOC_AUTHMODE:
171694405Simp			ireq->i_val = sc->wi_authmode;
171777217Sphk			break;
171877217Sphk		case IEEE80211_IOC_STATIONNAME:
171977217Sphk			error = copyout(sc->wi_node_name,
172077217Sphk			    ireq->i_data, IEEE80211_NWID_LEN);
172177217Sphk			ireq->i_len = IEEE80211_NWID_LEN;
172277217Sphk			break;
172377217Sphk		case IEEE80211_IOC_CHANNEL:
172477217Sphk			wreq.wi_type = WI_RID_CURRENT_CHAN;
172577217Sphk			wreq.wi_len = WI_MAX_DATALEN;
172677217Sphk			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq))
172777217Sphk				error = EINVAL;
172877217Sphk			else {
172977217Sphk				ireq->i_val = wreq.wi_val[0];
173077217Sphk			}
173177217Sphk			break;
173277217Sphk		case IEEE80211_IOC_POWERSAVE:
173377217Sphk			if(sc->wi_pm_enabled)
173477217Sphk				ireq->i_val = IEEE80211_POWERSAVE_ON;
173577217Sphk			else
173677217Sphk				ireq->i_val = IEEE80211_POWERSAVE_OFF;
173777217Sphk			break;
173877217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
173977217Sphk			ireq->i_val = sc->wi_max_sleep;
174077217Sphk			break;
174177217Sphk		default:
174277217Sphk			error = EINVAL;
174377217Sphk		}
174477217Sphk		break;
174577217Sphk	case SIOCS80211:
174693593Sjhb		if ((error = suser(td)))
174777217Sphk			goto out;
174877217Sphk		switch(ireq->i_type) {
174977217Sphk		case IEEE80211_IOC_SSID:
175077217Sphk			if (ireq->i_val != 0 ||
175177217Sphk			    ireq->i_len > IEEE80211_NWID_LEN) {
175277217Sphk				error = EINVAL;
175377217Sphk				break;
175477217Sphk			}
175577217Sphk			/* We set both of them */
175677217Sphk			bzero(sc->wi_net_name, IEEE80211_NWID_LEN);
175777217Sphk			error = copyin(ireq->i_data,
175877217Sphk			    sc->wi_net_name, ireq->i_len);
175977217Sphk			bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN);
176077217Sphk			break;
176177217Sphk		case IEEE80211_IOC_WEP:
176277217Sphk			/*
176377217Sphk			 * These cards only support one mode so
176477217Sphk			 * we just turn wep on what ever is
176577217Sphk			 * passed in if it's not OFF.
176677217Sphk			 */
176777217Sphk			if (ireq->i_val == IEEE80211_WEP_OFF) {
176877217Sphk				sc->wi_use_wep = 0;
176977217Sphk			} else {
177077217Sphk				sc->wi_use_wep = 1;
177177217Sphk			}
177277217Sphk			break;
177377217Sphk		case IEEE80211_IOC_WEPKEY:
177477217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3 ||
177577217Sphk				ireq->i_len > 13) {
177677217Sphk				error = EINVAL;
177777217Sphk				break;
177877217Sphk			}
177977217Sphk			bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13);
178077217Sphk			error = copyin(ireq->i_data,
178177217Sphk			    sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
178277217Sphk			    ireq->i_len);
178377217Sphk			if(error)
178477217Sphk				break;
178577217Sphk			sc->wi_keys.wi_keys[ireq->i_val].wi_keylen =
178677217Sphk				    ireq->i_len;
178777217Sphk			break;
178877217Sphk		case IEEE80211_IOC_WEPTXKEY:
178977217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3) {
179077217Sphk				error = EINVAL;
179177217Sphk				break;
179277217Sphk			}
179377217Sphk			sc->wi_tx_key = ireq->i_val;
179477217Sphk			break;
179577217Sphk		case IEEE80211_IOC_AUTHMODE:
179694405Simp			sc->wi_authmode = ireq->i_val;
179777217Sphk			break;
179877217Sphk		case IEEE80211_IOC_STATIONNAME:
179977217Sphk			if (ireq->i_len > 32) {
180077217Sphk				error = EINVAL;
180177217Sphk				break;
180277217Sphk			}
180377217Sphk			bzero(sc->wi_node_name, 32);
180477217Sphk			error = copyin(ireq->i_data,
180577217Sphk			    sc->wi_node_name, ireq->i_len);
180677217Sphk			break;
180777217Sphk		case IEEE80211_IOC_CHANNEL:
180877217Sphk			/*
180977217Sphk			 * The actual range is 1-14, but if you
181077217Sphk			 * set it to 0 you get the default. So
181177217Sphk			 * we let that work too.
181277217Sphk			 */
181377217Sphk			if (ireq->i_val < 0 || ireq->i_val > 14) {
181477217Sphk				error = EINVAL;
181577217Sphk				break;
181677217Sphk			}
181777217Sphk			sc->wi_channel = ireq->i_val;
181877217Sphk			break;
181977217Sphk		case IEEE80211_IOC_POWERSAVE:
182077217Sphk			switch (ireq->i_val) {
182177217Sphk			case IEEE80211_POWERSAVE_OFF:
182277217Sphk				sc->wi_pm_enabled = 0;
182377217Sphk				break;
182477217Sphk			case IEEE80211_POWERSAVE_ON:
182577217Sphk				sc->wi_pm_enabled = 1;
182677217Sphk				break;
182777217Sphk			default:
182877217Sphk				error = EINVAL;
182977217Sphk				break;
183077217Sphk			}
183177217Sphk			break;
183277217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
183377217Sphk			if (ireq->i_val < 0) {
183477217Sphk				error = EINVAL;
183577217Sphk				break;
183677217Sphk			}
183777217Sphk			sc->wi_max_sleep = ireq->i_val;
183877217Sphk			break;
183977217Sphk		default:
184077217Sphk			error = EINVAL;
184177217Sphk			break;
184277217Sphk		}
184377217Sphk
184477217Sphk		/* Reinitialize WaveLAN. */
184577217Sphk		wi_init(sc);
184677217Sphk
184794405Simp	break;
184894405Simp#ifdef WI_HOSTAP
184994405Simp	case SIOCHOSTAP_ADD:
185094405Simp	case SIOCHOSTAP_DEL:
185194405Simp	case SIOCHOSTAP_GET:
185294405Simp	case SIOCHOSTAP_GETALL:
185394405Simp	case SIOCHOSTAP_GFLAGS:
185494405Simp	case SIOCHOSTAP_SFLAGS:
185594405Simp		/* Send all Host AP specific ioctl's to Host AP code. */
185694405Simp		error = wihap_ioctl(sc, command, data);
185777217Sphk		break;
185894405Simp#endif
185946492Swpaul	default:
186046492Swpaul		error = EINVAL;
186146492Swpaul		break;
186246492Swpaul	}
186361818Srobertoout:
186467092Swpaul	WI_UNLOCK(sc);
186546492Swpaul
186646492Swpaul	return(error);
186746492Swpaul}
186846492Swpaul
186988546Salfredstatic void
187088546Salfredwi_init(xsc)
187146492Swpaul	void			*xsc;
187246492Swpaul{
187346492Swpaul	struct wi_softc		*sc = xsc;
187446492Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
187546492Swpaul	struct wi_ltv_macaddr	mac;
187646492Swpaul	int			id = 0;
187746492Swpaul
187867092Swpaul	WI_LOCK(sc);
187967092Swpaul
188067092Swpaul	if (sc->wi_gone) {
188167092Swpaul		WI_UNLOCK(sc);
188246492Swpaul		return;
188367092Swpaul	}
188446492Swpaul
188546492Swpaul	if (ifp->if_flags & IFF_RUNNING)
188646492Swpaul		wi_stop(sc);
188746492Swpaul
188846492Swpaul	wi_reset(sc);
188946492Swpaul
189046492Swpaul	/* Program max data length. */
189146492Swpaul	WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len);
189246492Swpaul
189347401Swpaul	/* Enable/disable IBSS creation. */
189446492Swpaul	WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss);
189546492Swpaul
189646492Swpaul	/* Set the port type. */
189746492Swpaul	WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype);
189846492Swpaul
189946492Swpaul	/* Program the RTS/CTS threshold. */
190046492Swpaul	WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh);
190146492Swpaul
190246492Swpaul	/* Program the TX rate */
190346492Swpaul	WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate);
190446492Swpaul
190546492Swpaul	/* Access point density */
190646492Swpaul	WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density);
190746492Swpaul
190846611Swpaul	/* Power Management Enabled */
190946611Swpaul	WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled);
191046611Swpaul
191146611Swpaul	/* Power Managment Max Sleep */
191246611Swpaul	WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep);
191346611Swpaul
191491695Simp	/* Roaming type */
191591695Simp	WI_SETVAL(WI_RID_ROAMING_MODE, sc->wi_roaming);
191691695Simp
191746492Swpaul	/* Specify the IBSS name */
191846492Swpaul	WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name);
191946492Swpaul
192046492Swpaul	/* Specify the network name */
192146492Swpaul	WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name);
192246492Swpaul
192346563Swpaul	/* Specify the frequency to use */
192446563Swpaul	WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel);
192546563Swpaul
192646492Swpaul	/* Program the nodename. */
192746492Swpaul	WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name);
192846492Swpaul
192994405Simp	/* Specify the authentication mode. */
193094405Simp	WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authmode);
193194405Simp
193246492Swpaul	/* Set our MAC address. */
193346492Swpaul	mac.wi_len = 4;
193446492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
193546492Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
193646492Swpaul	   (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN);
193746492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mac);
193846492Swpaul
193956965Swpaul	/* Configure WEP. */
194056965Swpaul	if (sc->wi_has_wep) {
194156965Swpaul		WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep);
194256965Swpaul		WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key);
194356965Swpaul		sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
194456965Swpaul		sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
194556965Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys);
194693733Simp		if (sc->sc_firmware_type != WI_LUCENT && sc->wi_use_wep) {
194791695Simp			/*
194891695Simp			 * ONLY HWB3163 EVAL-CARD Firmware version
194993733Simp			 * less than 0.8 variant2
195091695Simp			 *
195191695Simp			 *   If promiscuous mode disable, Prism2 chip
195291695Simp			 *  does not work with WEP .
195391695Simp			 * It is under investigation for details.
195491695Simp			 * (ichiro@netbsd.org)
195591695Simp			 */
195693733Simp			if (sc->sc_firmware_type == WI_INTERSIL &&
195793733Simp			    sc->sc_sta_firmware_ver < 802 ) {
195893733Simp				/* firm ver < 0.8 variant 2 */
195991695Simp				WI_SETVAL(WI_RID_PROMISC, 1);
196091695Simp			}
196194405Simp			WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authtype);
196291695Simp		}
196356965Swpaul	}
196456965Swpaul
196546492Swpaul	/* Initialize promisc mode. */
196646492Swpaul	if (ifp->if_flags & IFF_PROMISC) {
196746492Swpaul		WI_SETVAL(WI_RID_PROMISC, 1);
196846492Swpaul	} else {
196946492Swpaul		WI_SETVAL(WI_RID_PROMISC, 0);
197046492Swpaul	}
197146492Swpaul
197246492Swpaul	/* Set multicast filter. */
197346492Swpaul	wi_setmulti(sc);
197446492Swpaul
197546492Swpaul	/* Enable desired port */
197692457Simp	wi_cmd(sc, WI_CMD_ENABLE | sc->wi_portnum, 0, 0, 0);
197746492Swpaul
197875373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
197953702Swpaul		device_printf(sc->dev, "tx buffer allocation failed\n");
198046492Swpaul	sc->wi_tx_data_id = id;
198146492Swpaul
198275373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
198353702Swpaul		device_printf(sc->dev, "mgmt. buffer allocation failed\n");
198446492Swpaul	sc->wi_tx_mgmt_id = id;
198546492Swpaul
198646492Swpaul	/* enable interrupts */
198746492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
198846492Swpaul
198994405Simp#ifdef WI_HOSTAP
199094405Simp	wihap_init(sc);
199194405Simp
199294405Simp	/*
199394405Simp	 * Initialize ICV to something random.  XXX: this doesn't work
199494405Simp	 * if init happens in early boot-up.  Fix later.
199594405Simp	 */
199694405Simp	read_random(&sc->wi_icv, sizeof(sc->wi_icv));
199794405Simp#endif
199846492Swpaul	ifp->if_flags |= IFF_RUNNING;
199946492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
200046492Swpaul
200146492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
200267092Swpaul	WI_UNLOCK(sc);
200346492Swpaul
200446492Swpaul	return;
200546492Swpaul}
200646492Swpaul
200794472Simpstatic u_int32_t crc32_tab[] = {
200894472Simp	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
200994472Simp	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
201094472Simp	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
201194472Simp	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
201294472Simp	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
201394472Simp	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
201494472Simp	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
201594472Simp	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
201694472Simp	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
201794472Simp	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
201894472Simp	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
201994472Simp	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
202094472Simp	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
202194472Simp	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
202294472Simp	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
202394472Simp	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
202494472Simp	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
202594472Simp	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
202694472Simp	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
202794472Simp	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
202894472Simp	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
202994472Simp	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
203094472Simp	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
203194472Simp	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
203294472Simp	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
203394472Simp	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
203494472Simp	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
203594472Simp	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
203694472Simp	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
203794472Simp	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
203894472Simp	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
203994472Simp	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
204094472Simp	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
204194472Simp	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
204294472Simp	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
204394472Simp	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
204494472Simp	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
204594472Simp	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
204694472Simp	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
204794472Simp	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
204894472Simp	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
204994472Simp	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
205094472Simp	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
205194472Simp	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
205294472Simp	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
205394472Simp	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
205494472Simp	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
205594472Simp	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
205694472Simp	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
205794472Simp	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
205894472Simp	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
205994472Simp	0x2d02ef8dL
206094472Simp};
206194405Simp
206294472Simp#define RC4STATE 256
206394472Simp#define RC4KEYLEN 16
206494472Simp#define RC4SWAP(x,y) \
206594472Simp    do { u_int8_t t = state[x]; state[x] = state[y]; state[y] = t; } while(0)
206694405Simp
206788546Salfredstatic void
206894405Simpwi_do_hostencrypt(struct wi_softc *sc, caddr_t buf, int len)
206994405Simp{
207094472Simp	u_int32_t i, crc, klen;
207194472Simp	u_int8_t state[RC4STATE], key[RC4KEYLEN];
207294472Simp	u_int8_t x, y, *dat;
207394405Simp
207494472Simp	if (!sc->wi_icv_flag) {
207594472Simp		sc->wi_icv = arc4random();
207694472Simp		sc->wi_icv_flag++;
207794472Simp        } else
207894472Simp		sc->wi_icv++;
207994472Simp	/*
208094472Simp	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
208194472Simp	 * (B, 255, N) with 3 <= B < 8
208294472Simp	 */
208394472Simp	if (sc->wi_icv >= 0x03ff00 &&
208494472Simp            (sc->wi_icv & 0xf8ff00) == 0x00ff00)
208594472Simp                sc->wi_icv += 0x000100;
208694405Simp
208794472Simp	/* prepend 24bit IV to tx key, byte order does not matter */
208894472Simp	key[0] = sc->wi_icv >> 16;
208994472Simp	key[1] = sc->wi_icv >> 8;
209094472Simp	key[2] = sc->wi_icv;
209194405Simp
209294472Simp	klen = sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keylen +
209394472Simp	    IEEE80211_WEP_IVLEN;
209494472Simp	klen = (klen >= RC4KEYLEN) ? RC4KEYLEN : RC4KEYLEN/2;
209594472Simp	bcopy((char *)&sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keydat,
209694472Simp	    (char *)key + IEEE80211_WEP_IVLEN, klen - IEEE80211_WEP_IVLEN);
209794405Simp
209894472Simp	/* rc4 keysetup */
209994472Simp	x = y = 0;
210094472Simp	for (i = 0; i < RC4STATE; i++)
210194472Simp		state[i] = i;
210294472Simp	for (i = 0; i < RC4STATE; i++) {
210394472Simp		y = (key[x] + state[i] + y) % RC4STATE;
210494472Simp		RC4SWAP(i, y);
210594472Simp		x = (x + 1) % klen;
210694405Simp	}
210794405Simp
210894472Simp	/* output: IV, tx keyid, rc4(data), rc4(crc32(data)) */
210994472Simp	dat = buf;
211094472Simp	dat[0] = key[0];
211194472Simp	dat[1] = key[1];
211294472Simp	dat[2] = key[2];
211394472Simp	dat[3] = sc->wi_tx_key << 6;		/* pad and keyid */
211494472Simp	dat += 4;
211594472Simp
211694472Simp	/* compute rc4 over data, crc32 over data */
211794472Simp	crc = ~0;
211894472Simp	x = y = 0;
211994472Simp	for (i = 0; i < len; i++) {
212094472Simp		x = (x + 1) % RC4STATE;
212194472Simp		y = (state[x] + y) % RC4STATE;
212294472Simp		RC4SWAP(x, y);
212394472Simp		crc = crc32_tab[(crc ^ dat[i]) & 0xff] ^ (crc >> 8);
212494472Simp		dat[i] ^= state[(state[x] + state[y]) % RC4STATE];
212594405Simp	}
212694472Simp	crc = ~crc;
212794472Simp	dat += len;
212894405Simp
212994472Simp	/* append little-endian crc32 and encrypt */
213094472Simp	dat[0] = crc;
213194472Simp	dat[1] = crc >> 8;
213294472Simp	dat[2] = crc >> 16;
213394472Simp	dat[3] = crc >> 24;
213494472Simp	for (i = 0; i < IEEE80211_WEP_CRCLEN; i++) {
213594472Simp		x = (x + 1) % RC4STATE;
213694472Simp		y = (state[x] + y) % RC4STATE;
213794472Simp		RC4SWAP(x, y);
213894472Simp		dat[i] ^= state[(state[x] + state[y]) % RC4STATE];
213994405Simp	}
214094405Simp}
214194405Simp
214294405Simpstatic void
214388546Salfredwi_start(ifp)
214446492Swpaul	struct ifnet		*ifp;
214546492Swpaul{
214646492Swpaul	struct wi_softc		*sc;
214746492Swpaul	struct mbuf		*m0;
214846492Swpaul	struct wi_frame		tx_frame;
214946492Swpaul	struct ether_header	*eh;
215046492Swpaul	int			id;
215146492Swpaul
215246492Swpaul	sc = ifp->if_softc;
215367092Swpaul	WI_LOCK(sc);
215446492Swpaul
215567092Swpaul	if (sc->wi_gone) {
215667092Swpaul		WI_UNLOCK(sc);
215746492Swpaul		return;
215867092Swpaul	}
215946492Swpaul
216067092Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
216167092Swpaul		WI_UNLOCK(sc);
216246492Swpaul		return;
216367092Swpaul	}
216446492Swpaul
216594405Simp#ifdef WI_HOSTAP
216694405Simpnextpkt:
216794405Simp#endif
216846492Swpaul	IF_DEQUEUE(&ifp->if_snd, m0);
216967092Swpaul	if (m0 == NULL) {
217067092Swpaul		WI_UNLOCK(sc);
217146492Swpaul		return;
217267092Swpaul	}
217346492Swpaul
217446492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
217594405Simp	tx_frame.wi_frame_ctl = htole16(WI_FTYPE_DATA);
217646492Swpaul	id = sc->wi_tx_data_id;
217746492Swpaul	eh = mtod(m0, struct ether_header *);
217846492Swpaul
217994405Simp#ifdef WI_HOSTAP
218094405Simp	if (sc->wi_ptype == WI_PORTTYPE_AP) {
218194405Simp		if (!wihap_check_tx(&sc->wi_hostap_info,
218294405Simp		    eh->ether_dhost, &tx_frame.wi_tx_rate)) {
218394405Simp			if (ifp->if_flags & IFF_DEBUG)
218494405Simp				printf("wi_start: dropping unassoc "
218594405Simp				       "dst %6D\n", eh->ether_dhost, ":");
218694405Simp			m_freem(m0);
218794405Simp			goto nextpkt;
218894405Simp		}
218994405Simp	}
219094405Simp#endif
219146492Swpaul	/*
219247401Swpaul	 * Use RFC1042 encoding for IP and ARP datagrams,
219346492Swpaul	 * 802.3 for anything else.
219446492Swpaul	 */
219575275Salfred	if (ntohs(eh->ether_type) > ETHER_MAX_LEN) {
219646492Swpaul		bcopy((char *)&eh->ether_dhost,
219746492Swpaul		    (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN);
219894405Simp#ifdef WI_HOSTAP
219994405Simp		if (sc->wi_ptype == WI_PORTTYPE_AP) {
220094405Simp			tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; /* XXX */
220194405Simp			tx_frame.wi_frame_ctl |= WI_FCTL_FROMDS;
220294405Simp			if (sc->wi_use_wep)
220394405Simp				tx_frame.wi_frame_ctl |= WI_FCTL_WEP;
220494405Simp			bcopy((char *)&sc->arpcom.ac_enaddr,
220594405Simp			      (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
220694405Simp			bcopy((char *)&eh->ether_shost,
220794405Simp			      (char *)&tx_frame.wi_addr3, ETHER_ADDR_LEN);
220894405Simp		}
220994405Simp		else
221094405Simp#endif
221194405Simp			bcopy((char *)&eh->ether_shost,
221294405Simp			    (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
221346492Swpaul		bcopy((char *)&eh->ether_dhost,
221446492Swpaul		    (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN);
221546492Swpaul		bcopy((char *)&eh->ether_shost,
221646492Swpaul		    (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN);
221746492Swpaul
221846492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN;
221946492Swpaul		tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0);
222046492Swpaul		tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1);
222146492Swpaul		tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
222246492Swpaul		tx_frame.wi_type = eh->ether_type;
222346492Swpaul
222494405Simp#ifdef WI_HOSTAP
222594405Simp		if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) {
222694405Simp			/* Do host encryption. */
222794405Simp			bcopy(&tx_frame.wi_dat[0], &sc->wi_txbuf[4], 8);
222894405Simp			m_copydata(m0, sizeof(struct ether_header),
222994405Simp			    m0->m_pkthdr.len - sizeof(struct ether_header),
223094405Simp			    (caddr_t)&sc->wi_txbuf[12]);
223194405Simp			wi_do_hostencrypt(sc, &sc->wi_txbuf[0],
223294405Simp			    tx_frame.wi_dat_len);
223394472Simp			tx_frame.wi_dat_len += IEEE80211_WEP_IVLEN +
223494472Simp			    IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
223594405Simp			wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
223694405Simp			    sizeof(struct wi_frame));
223794405Simp			wi_write_data(sc, id, WI_802_11_OFFSET_RAW,
223894405Simp			    (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len -
223994405Simp			    sizeof(struct ether_header)) + 18);
224094405Simp		}
224194405Simp		else
224294405Simp#endif
224394405Simp		{
224494405Simp			m_copydata(m0, sizeof(struct ether_header),
224594405Simp			    m0->m_pkthdr.len - sizeof(struct ether_header),
224694405Simp			    (caddr_t)&sc->wi_txbuf);
224794405Simp			wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
224894405Simp			    sizeof(struct wi_frame));
224994405Simp			wi_write_data(sc, id, WI_802_11_OFFSET,
225094405Simp			    (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len -
225194405Simp			    sizeof(struct ether_header)) + 2);
225294405Simp		}
225346492Swpaul	} else {
225446492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len;
225546492Swpaul
225694405Simp#ifdef WI_HOSTAP
225794405Simp		if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) {
225894405Simp			/* Do host encryption. */
225994405Simp			printf( "XXX: host encrypt not implemented for 802.3\n" );
226094405Simp		}
226194405Simp		else
226294405Simp#endif
226394405Simp		{
226494405Simp			eh->ether_type = htons(m0->m_pkthdr.len -
226594405Simp			    WI_SNAPHDR_LEN);
226694405Simp			m_copydata(m0, 0, m0->m_pkthdr.len,
226794405Simp			    (caddr_t)&sc->wi_txbuf);
226846492Swpaul
226994405Simp			wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
227094405Simp			    sizeof(struct wi_frame));
227194405Simp			wi_write_data(sc, id, WI_802_3_OFFSET,
227294405Simp			    (caddr_t)&sc->wi_txbuf, m0->m_pkthdr.len + 2);
227394405Simp		}
227446492Swpaul	}
227546492Swpaul
227646492Swpaul	/*
227746492Swpaul	 * If there's a BPF listner, bounce a copy of
227893359Simp 	 * this frame to him. Also, don't send this to the bpf sniffer
227993359Simp 	 * if we're in procframe or monitor sniffing mode.
228046492Swpaul	 */
228193359Simp 	if (!(sc->wi_procframe || sc->wi_debug.wi_monitor) && ifp->if_bpf)
228246492Swpaul		bpf_mtap(ifp, m0);
228346492Swpaul
228446492Swpaul	m_freem(m0);
228546492Swpaul
228692457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0))
228753702Swpaul		device_printf(sc->dev, "xmit failed\n");
228846492Swpaul
228946492Swpaul	ifp->if_flags |= IFF_OACTIVE;
229046492Swpaul
229146492Swpaul	/*
229246492Swpaul	 * Set a timeout in case the chip goes out to lunch.
229346492Swpaul	 */
229446492Swpaul	ifp->if_timer = 5;
229546492Swpaul
229667092Swpaul	WI_UNLOCK(sc);
229746492Swpaul	return;
229846492Swpaul}
229946492Swpaul
230094405Simpint
230188546Salfredwi_mgmt_xmit(sc, data, len)
230246492Swpaul	struct wi_softc		*sc;
230346492Swpaul	caddr_t			data;
230446492Swpaul	int			len;
230546492Swpaul{
230646492Swpaul	struct wi_frame		tx_frame;
230746492Swpaul	int			id;
230846492Swpaul	struct wi_80211_hdr	*hdr;
230946492Swpaul	caddr_t			dptr;
231046492Swpaul
231146492Swpaul	if (sc->wi_gone)
231246492Swpaul		return(ENODEV);
231346492Swpaul
231446492Swpaul	hdr = (struct wi_80211_hdr *)data;
231546492Swpaul	dptr = data + sizeof(struct wi_80211_hdr);
231646492Swpaul
231746492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
231846492Swpaul	id = sc->wi_tx_mgmt_id;
231946492Swpaul
232046492Swpaul	bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl,
232146492Swpaul	   sizeof(struct wi_80211_hdr));
232246492Swpaul
232394405Simp	tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT;
232494405Simp	tx_frame.wi_dat_len = len - sizeof(struct wi_80211_hdr);
232594405Simp	tx_frame.wi_len = htons(tx_frame.wi_dat_len);
232646492Swpaul
232746492Swpaul	wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame));
232846492Swpaul	wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr,
232994405Simp	    len - sizeof(struct wi_80211_hdr) + 2);
233046492Swpaul
233192457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) {
233253702Swpaul		device_printf(sc->dev, "xmit failed\n");
233346492Swpaul		return(EIO);
233446492Swpaul	}
233546492Swpaul
233646492Swpaul	return(0);
233746492Swpaul}
233846492Swpaul
233988546Salfredstatic void
234088546Salfredwi_stop(sc)
234146492Swpaul	struct wi_softc		*sc;
234246492Swpaul{
234346492Swpaul	struct ifnet		*ifp;
234446492Swpaul
234567092Swpaul	WI_LOCK(sc);
234667092Swpaul
234767092Swpaul	if (sc->wi_gone) {
234867092Swpaul		WI_UNLOCK(sc);
234946492Swpaul		return;
235067092Swpaul	}
235146492Swpaul
235294405Simp#ifdef WI_HOSTAP
235394405Simp	wihap_shutdown(sc);
235494405Simp#endif
235594405Simp
235646492Swpaul	ifp = &sc->arpcom.ac_if;
235746492Swpaul
235870173Sjhb	/*
235970173Sjhb	 * If the card is gone and the memory port isn't mapped, we will
236070173Sjhb	 * (hopefully) get 0xffff back from the status read, which is not
236170173Sjhb	 * a valid status value.
236270173Sjhb	 */
236370173Sjhb	if (CSR_READ_2(sc, WI_STATUS) != 0xffff) {
236470173Sjhb		CSR_WRITE_2(sc, WI_INT_EN, 0);
236592457Simp		wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0, 0, 0);
236670173Sjhb	}
236746492Swpaul
236846492Swpaul	untimeout(wi_inquire, sc, sc->wi_stat_ch);
236946492Swpaul
237046492Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
237146492Swpaul
237267092Swpaul	WI_UNLOCK(sc);
237346492Swpaul	return;
237446492Swpaul}
237546492Swpaul
237688546Salfredstatic void
237788546Salfredwi_watchdog(ifp)
237846492Swpaul	struct ifnet		*ifp;
237946492Swpaul{
238046492Swpaul	struct wi_softc		*sc;
238146492Swpaul
238246492Swpaul	sc = ifp->if_softc;
238346492Swpaul
238475199Salfred	device_printf(sc->dev, "watchdog timeout\n");
238546492Swpaul
238646492Swpaul	wi_init(sc);
238746492Swpaul
238846492Swpaul	ifp->if_oerrors++;
238946492Swpaul
239046492Swpaul	return;
239146492Swpaul}
239246492Swpaul
239393611Simpint
239490580Sbrookswi_alloc(dev, rid)
239553702Swpaul	device_t		dev;
239690580Sbrooks	int			rid;
239746492Swpaul{
239853702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
239953702Swpaul
240090580Sbrooks	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
240190580Sbrooks		sc->iobase_rid = rid;
240290580Sbrooks		sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
240390580Sbrooks		    &sc->iobase_rid, 0, ~0, (1 << 6),
240490580Sbrooks		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
240590580Sbrooks		if (!sc->iobase) {
240690580Sbrooks			device_printf(dev, "No I/O space?!\n");
240790580Sbrooks			return (ENXIO);
240890580Sbrooks		}
240990580Sbrooks
241090580Sbrooks		sc->wi_io_addr = rman_get_start(sc->iobase);
241190580Sbrooks		sc->wi_btag = rman_get_bustag(sc->iobase);
241290580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
241390580Sbrooks	} else {
241490580Sbrooks		sc->mem_rid = rid;
241590580Sbrooks		sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY,
241690580Sbrooks		    &sc->mem_rid, 0, ~0, 1, RF_ACTIVE);
241790580Sbrooks
241890580Sbrooks		if (!sc->mem) {
241990580Sbrooks			device_printf(dev, "No Mem space on prism2.5?\n");
242090580Sbrooks			return (ENXIO);
242190580Sbrooks		}
242290580Sbrooks
242390580Sbrooks		sc->wi_btag = rman_get_bustag(sc->mem);
242490580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->mem);
242553702Swpaul	}
242653702Swpaul
242790580Sbrooks
242874906Salfred	sc->irq_rid = 0;
242974906Salfred	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
243090580Sbrooks	    0, ~0, 1, RF_ACTIVE |
243190580Sbrooks	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
243290580Sbrooks
243353702Swpaul	if (!sc->irq) {
243475219Salfred		wi_free(dev);
243553702Swpaul		device_printf(dev, "No irq?!\n");
243653702Swpaul		return (ENXIO);
243753702Swpaul	}
243853702Swpaul
243953702Swpaul	sc->dev = dev;
244053702Swpaul	sc->wi_unit = device_get_unit(dev);
244153702Swpaul
244253702Swpaul	return (0);
244353702Swpaul}
244453702Swpaul
244593611Simpvoid
244688546Salfredwi_free(dev)
244753702Swpaul	device_t		dev;
244853702Swpaul{
244953702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
245053702Swpaul
245175219Salfred	if (sc->iobase != NULL) {
245275219Salfred		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
245375219Salfred		sc->iobase = NULL;
245475219Salfred	}
245575219Salfred	if (sc->irq != NULL) {
245675219Salfred		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
245775219Salfred		sc->irq = NULL;
245875219Salfred	}
245975219Salfred	if (sc->mem != NULL) {
246074906Salfred		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
246175219Salfred		sc->mem = NULL;
246275219Salfred	}
246353702Swpaul
246453702Swpaul	return;
246553702Swpaul}
246653702Swpaul
246793611Simpvoid
246888546Salfredwi_shutdown(dev)
246953702Swpaul	device_t		dev;
247053702Swpaul{
247146492Swpaul	struct wi_softc		*sc;
247246492Swpaul
247353702Swpaul	sc = device_get_softc(dev);
247446492Swpaul	wi_stop(sc);
247546492Swpaul
247646492Swpaul	return;
247746492Swpaul}
247853702Swpaul
247953702Swpaul#ifdef WICACHE
248053702Swpaul/* wavelan signal strength cache code.
248153702Swpaul * store signal/noise/quality on per MAC src basis in
248253702Swpaul * a small fixed cache.  The cache wraps if > MAX slots
248353702Swpaul * used.  The cache may be zeroed out to start over.
248453702Swpaul * Two simple filters exist to reduce computation:
248553702Swpaul * 1. ip only (literally 0x800) which may be used
248653702Swpaul * to ignore some packets.  It defaults to ip only.
248753702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons.
248853702Swpaul * 2. multicast/broadcast only.  This may be used to
248953702Swpaul * ignore unicast packets and only cache signal strength
249053702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP
249153702Swpaul * beacons and not unicast traffic.
249253702Swpaul *
249353702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal,
249453702Swpaul *	quality, noise)
249553702Swpaul *
249653702Swpaul * No apologies for storing IP src here.  It's easy and saves much
249753702Swpaul * trouble elsewhere.  The cache is assumed to be INET dependent,
249853702Swpaul * although it need not be.
249953702Swpaul */
250053702Swpaul
250153702Swpaul#ifdef documentation
250253702Swpaul
250353702Swpaulint wi_sigitems;                                /* number of cached entries */
250453702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE];  /*  array of cache entries */
250553702Swpaulint wi_nextitem;                                /*  index/# of entries */
250653702Swpaul
250753702Swpaul
250853702Swpaul#endif
250953702Swpaul
251053702Swpaul/* control variables for cache filtering.  Basic idea is
251153702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons
251253702Swpaul * which are broadcast or multicast).  Still you might
251353702Swpaul * want to measure signal strength with unicast ping packets
251453702Swpaul * on a pt. to pt. ant. setup.
251553702Swpaul */
251653702Swpaul/* set true if you want to limit cache items to broadcast/mcast
251753702Swpaul * only packets (not unicast).  Useful for mobile-ip beacons which
251853702Swpaul * are broadcast/multicast at network layer.  Default is all packets
251953702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup.
252053702Swpaul */
252153702Swpaulstatic int wi_cache_mcastonly = 0;
252253702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW,
252353702Swpaul	&wi_cache_mcastonly, 0, "");
252453702Swpaul
252553702Swpaul/* set true if you want to limit cache items to IP packets only
252653702Swpaul*/
252753702Swpaulstatic int wi_cache_iponly = 1;
252853702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW,
252953702Swpaul	&wi_cache_iponly, 0, "");
253053702Swpaul
253153702Swpaul/*
253253702Swpaul * Original comments:
253353702Swpaul * -----------------
253453702Swpaul * wi_cache_store, per rx packet store signal
253553702Swpaul * strength in MAC (src) indexed cache.
253653702Swpaul *
253753702Swpaul * follows linux driver in how signal strength is computed.
253853702Swpaul * In ad hoc mode, we use the rx_quality field.
253953702Swpaul * signal and noise are trimmed to fit in the range from 47..138.
254053702Swpaul * rx_quality field MSB is signal strength.
254153702Swpaul * rx_quality field LSB is noise.
254253702Swpaul * "quality" is (signal - noise) as is log value.
254353702Swpaul * note: quality CAN be negative.
254453702Swpaul *
254553702Swpaul * In BSS mode, we use the RID for communication quality.
254653702Swpaul * TBD:  BSS mode is currently untested.
254753702Swpaul *
254853702Swpaul * Bill's comments:
254953702Swpaul * ---------------
255053702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc"
255153702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive:
255253702Swpaul * there's a bunch of PIO operations that have to be done to read a record
255353702Swpaul * from the NIC, and reading the comms quality RID each time a packet is
255453702Swpaul * received can really hurt performance. We don't have to do this anyway:
255553702Swpaul * the comms quality field only reflects the values in the rx_quality field
255653702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode,
255753702Swpaul * but the values it contains are updated based on the rx_quality from
255853702Swpaul * frames received from the access point.
255953702Swpaul *
256053702Swpaul * Also, according to Lucent, the signal strength and noise level values
256153702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code
256253702Swpaul * to do that instead of the scaling it did originally.
256353702Swpaul */
256488546Salfredstatic void
256588546Salfredwi_cache_store(struct wi_softc *sc, struct ether_header *eh,
256653702Swpaul                     struct mbuf *m, unsigned short rx_quality)
256753702Swpaul{
256853702Swpaul	struct ip *ip = 0;
256953702Swpaul	int i;
257053702Swpaul	static int cache_slot = 0; 	/* use this cache entry */
257153702Swpaul	static int wrapindex = 0;       /* next "free" cache entry */
257253702Swpaul	int sig, noise;
257353702Swpaul	int sawip=0;
257453702Swpaul
257594405Simp	/*
257694405Simp	 * filters:
257753702Swpaul	 * 1. ip only
257853702Swpaul	 * 2. configurable filter to throw out unicast packets,
257953702Swpaul	 * keep multicast only.
258053702Swpaul	 */
258153702Swpaul
258275276Salfred	if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) {
258353702Swpaul		sawip = 1;
258453702Swpaul	}
258553702Swpaul
258694405Simp	/*
258794405Simp	 * filter for ip packets only
258853702Swpaul	*/
258953702Swpaul	if (wi_cache_iponly && !sawip) {
259053702Swpaul		return;
259153702Swpaul	}
259253702Swpaul
259394405Simp	/*
259494405Simp	 *  filter for broadcast/multicast only
259553702Swpaul	 */
259653702Swpaul	if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
259753702Swpaul		return;
259853702Swpaul	}
259953702Swpaul
260053702Swpaul#ifdef SIGDEBUG
260153702Swpaul	printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit,
260253702Swpaul	    rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff);
260353702Swpaul#endif
260453702Swpaul
260594405Simp	/*
260694405Simp	 *  find the ip header.  we want to store the ip_src
260753702Swpaul	 * address.
260853702Swpaul	 */
260994405Simp	if (sawip)
261053702Swpaul		ip = mtod(m, struct ip *);
261153702Swpaul
261294405Simp	/*
261394405Simp	 * do a linear search for a matching MAC address
261453702Swpaul	 * in the cache table
261553702Swpaul	 * . MAC address is 6 bytes,
261653702Swpaul	 * . var w_nextitem holds total number of entries already cached
261753702Swpaul	 */
261853702Swpaul	for(i = 0; i < sc->wi_nextitem; i++) {
261953702Swpaul		if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc,  6 )) {
262094405Simp			/*
262194405Simp			 * Match!,
262253702Swpaul			 * so we already have this entry,
262353702Swpaul			 * update the data
262453702Swpaul			 */
262553702Swpaul			break;
262653702Swpaul		}
262753702Swpaul	}
262853702Swpaul
262994405Simp	/*
263094405Simp	 *  did we find a matching mac address?
263153702Swpaul	 * if yes, then overwrite a previously existing cache entry
263253702Swpaul	 */
263353702Swpaul	if (i < sc->wi_nextitem )   {
263453702Swpaul		cache_slot = i;
263553702Swpaul	}
263694405Simp	/*
263794405Simp	 * else, have a new address entry,so
263853702Swpaul	 * add this new entry,
263953702Swpaul	 * if table full, then we need to replace LRU entry
264053702Swpaul	 */
264153702Swpaul	else    {
264253702Swpaul
264394405Simp		/*
264494405Simp		 * check for space in cache table
264553702Swpaul		 * note: wi_nextitem also holds number of entries
264653702Swpaul		 * added in the cache table
264753702Swpaul		 */
264853702Swpaul		if ( sc->wi_nextitem < MAXWICACHE ) {
264953702Swpaul			cache_slot = sc->wi_nextitem;
265053702Swpaul			sc->wi_nextitem++;
265153702Swpaul			sc->wi_sigitems = sc->wi_nextitem;
265253702Swpaul		}
265353702Swpaul        	/* no space found, so simply wrap with wrap index
265453702Swpaul		 * and "zap" the next entry
265553702Swpaul		 */
265653702Swpaul		else {
265753702Swpaul			if (wrapindex == MAXWICACHE) {
265853702Swpaul				wrapindex = 0;
265953702Swpaul			}
266053702Swpaul			cache_slot = wrapindex++;
266153702Swpaul		}
266253702Swpaul	}
266353702Swpaul
266494405Simp	/*
266594405Simp	 * invariant: cache_slot now points at some slot
266653702Swpaul	 * in cache.
266753702Swpaul	 */
266853702Swpaul	if (cache_slot < 0 || cache_slot >= MAXWICACHE) {
266953702Swpaul		log(LOG_ERR, "wi_cache_store, bad index: %d of "
267053702Swpaul		    "[0..%d], gross cache error\n",
267153702Swpaul		    cache_slot, MAXWICACHE);
267253702Swpaul		return;
267353702Swpaul	}
267453702Swpaul
267594405Simp	/*
267694405Simp	 *  store items in cache
267753702Swpaul	 *  .ip source address
267853702Swpaul	 *  .mac src
267953702Swpaul	 *  .signal, etc.
268053702Swpaul	 */
268194405Simp	if (sawip)
268253702Swpaul		sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr;
268353702Swpaul	bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc,  6);
268453702Swpaul
268553702Swpaul	sig = (rx_quality >> 8) & 0xFF;
268653702Swpaul	noise = rx_quality & 0xFF;
268753702Swpaul	sc->wi_sigcache[cache_slot].signal = sig - 149;
268853702Swpaul	sc->wi_sigcache[cache_slot].noise = noise - 149;
268953702Swpaul	sc->wi_sigcache[cache_slot].quality = sig - noise;
269053702Swpaul
269153702Swpaul	return;
269253702Swpaul}
269353702Swpaul#endif
269477217Sphk
269588546Salfredstatic int
269688546Salfredwi_get_cur_ssid(sc, ssid, len)
269777217Sphk	struct wi_softc		*sc;
269877217Sphk	char			*ssid;
269977217Sphk	int			*len;
270077217Sphk{
270177217Sphk	int			error = 0;
270277217Sphk	struct wi_req		wreq;
270377217Sphk
270477217Sphk	wreq.wi_len = WI_MAX_DATALEN;
270577217Sphk	switch (sc->wi_ptype) {
270694405Simp#ifdef WI_HOSTAP
270794405Simp	case WI_PORTTYPE_AP:
270894405Simp		*len = IEEE80211_NWID_LEN;
270994405Simp		bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN);
271094405Simp		break;
271194405Simp#endif
271277217Sphk	case WI_PORTTYPE_ADHOC:
271377217Sphk		wreq.wi_type = WI_RID_CURRENT_SSID;
271477217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
271577217Sphk		if (error != 0)
271677217Sphk			break;
271777217Sphk		if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
271877217Sphk			error = EINVAL;
271977217Sphk			break;
272077217Sphk		}
272177217Sphk		*len = wreq.wi_val[0];
272277217Sphk		bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
272377217Sphk		break;
272477217Sphk	case WI_PORTTYPE_BSS:
272577217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
272677217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
272777217Sphk		if (error != 0)
272877217Sphk			break;
272977217Sphk		if (wreq.wi_val[0] != 0) /* associated */ {
273077217Sphk			wreq.wi_type = WI_RID_CURRENT_SSID;
273177217Sphk			wreq.wi_len = WI_MAX_DATALEN;
273277217Sphk			error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
273377217Sphk			if (error != 0)
273477217Sphk				break;
273577217Sphk			if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
273677217Sphk				error = EINVAL;
273777217Sphk				break;
273877217Sphk			}
273977217Sphk			*len = wreq.wi_val[0];
274077217Sphk			bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
274177217Sphk		} else {
274277217Sphk			*len = IEEE80211_NWID_LEN;
274377217Sphk			bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN);
274477217Sphk		}
274577217Sphk		break;
274677217Sphk	default:
274777217Sphk		error = EINVAL;
274877217Sphk		break;
274977217Sphk	}
275077217Sphk
275177217Sphk	return error;
275277217Sphk}
275377217Sphk
275488546Salfredstatic int
275588546Salfredwi_media_change(ifp)
275677217Sphk	struct ifnet		*ifp;
275777217Sphk{
275877217Sphk	struct wi_softc		*sc = ifp->if_softc;
275977217Sphk	int			otype = sc->wi_ptype;
276077217Sphk	int			orate = sc->wi_tx_rate;
276177217Sphk
276277217Sphk	if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0)
276377217Sphk		sc->wi_ptype = WI_PORTTYPE_ADHOC;
276494405Simp#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP)
276594405Simp	else if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_HOSTAP) != 0)
276694405Simp		sc->wi_ptype = WI_PORTTYPE_AP;
276794405Simp#endif
276877217Sphk	else
276977217Sphk		sc->wi_ptype = WI_PORTTYPE_BSS;
277077217Sphk
277177217Sphk	switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) {
277277217Sphk	case IFM_IEEE80211_DS1:
277377217Sphk		sc->wi_tx_rate = 1;
277477217Sphk		break;
277577217Sphk	case IFM_IEEE80211_DS2:
277677217Sphk		sc->wi_tx_rate = 2;
277777217Sphk		break;
277877217Sphk	case IFM_IEEE80211_DS5:
277977217Sphk		sc->wi_tx_rate = 5;
278077217Sphk		break;
278177217Sphk	case IFM_IEEE80211_DS11:
278277217Sphk		sc->wi_tx_rate = 11;
278377217Sphk		break;
278477217Sphk	case IFM_AUTO:
278577217Sphk		sc->wi_tx_rate = 3;
278677217Sphk		break;
278777217Sphk	}
278877217Sphk
278977217Sphk	if (otype != sc->wi_ptype ||
279077217Sphk	    orate != sc->wi_tx_rate)
279177217Sphk		wi_init(sc);
279277217Sphk
279377217Sphk	return(0);
279477217Sphk}
279577217Sphk
279688546Salfredstatic void
279788546Salfredwi_media_status(ifp, imr)
279877217Sphk	struct ifnet		*ifp;
279977217Sphk	struct ifmediareq	*imr;
280077217Sphk{
280177217Sphk	struct wi_req		wreq;
280277217Sphk	struct wi_softc		*sc = ifp->if_softc;
280377217Sphk
280477217Sphk	if (sc->wi_tx_rate == 3) {
280577217Sphk		imr->ifm_active = IFM_IEEE80211|IFM_AUTO;
280677217Sphk		if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
280777217Sphk			imr->ifm_active |= IFM_IEEE80211_ADHOC;
280894405Simp#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP)
280994405Simp		else if (sc->wi_ptype == WI_PORTTYPE_AP)
281094405Simp			imr->ifm_active |= IFM_IEEE80211_HOSTAP;
281194405Simp#endif
281277217Sphk		wreq.wi_type = WI_RID_CUR_TX_RATE;
281377217Sphk		wreq.wi_len = WI_MAX_DATALEN;
281477217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) {
281577217Sphk			switch(wreq.wi_val[0]) {
281677217Sphk			case 1:
281777217Sphk				imr->ifm_active |= IFM_IEEE80211_DS1;
281877217Sphk				break;
281977217Sphk			case 2:
282077217Sphk				imr->ifm_active |= IFM_IEEE80211_DS2;
282177217Sphk				break;
282277217Sphk			case 6:
282377217Sphk				imr->ifm_active |= IFM_IEEE80211_DS5;
282477217Sphk				break;
282577217Sphk			case 11:
282677217Sphk				imr->ifm_active |= IFM_IEEE80211_DS11;
282777217Sphk				break;
282877217Sphk				}
282977217Sphk		}
283077217Sphk	} else {
283177217Sphk		imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media;
283277217Sphk	}
283377217Sphk
283477217Sphk	imr->ifm_status = IFM_AVALID;
283577217Sphk	if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
283677217Sphk		/*
283777217Sphk		 * XXX: It would be nice if we could give some actually
283877217Sphk		 * useful status like whether we joined another IBSS or
283977217Sphk		 * created one ourselves.
284077217Sphk		 */
284177217Sphk		imr->ifm_status |= IFM_ACTIVE;
284294405Simp#ifdef WI_HOSTAP
284394405Simp	else if (sc->wi_ptype == WI_PORTTYPE_AP)
284494405Simp		imr->ifm_status |= IFM_ACTIVE;
284594405Simp#endif
284677217Sphk	else {
284777217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
284877217Sphk		wreq.wi_len = WI_MAX_DATALEN;
284977217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 &&
285077217Sphk		    wreq.wi_val[0] != 0)
285177217Sphk			imr->ifm_status |= IFM_ACTIVE;
285277217Sphk	}
285377217Sphk}
285493359Simp
285593359Simpstatic int
285693359Simpwi_get_debug(sc, wreq)
285793359Simp	struct wi_softc		*sc;
285893359Simp	struct wi_req		*wreq;
285993359Simp{
286093359Simp	int			error = 0;
286193359Simp
286293359Simp	wreq->wi_len = 1;
286393359Simp
286493359Simp	switch (wreq->wi_type) {
286593359Simp	case WI_DEBUG_SLEEP:
286693359Simp		wreq->wi_len++;
286793359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sleep;
286893359Simp		break;
286993359Simp	case WI_DEBUG_DELAYSUPP:
287093359Simp		wreq->wi_len++;
287193359Simp		wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
287293359Simp		break;
287393359Simp	case WI_DEBUG_TXSUPP:
287493359Simp		wreq->wi_len++;
287593359Simp		wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
287693359Simp		break;
287793359Simp	case WI_DEBUG_MONITOR:
287893359Simp		wreq->wi_len++;
287993359Simp		wreq->wi_val[0] = sc->wi_debug.wi_monitor;
288093359Simp		break;
288193359Simp	case WI_DEBUG_LEDTEST:
288293359Simp		wreq->wi_len += 3;
288393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
288493359Simp		wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
288593359Simp		wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
288693359Simp		break;
288793359Simp	case WI_DEBUG_CONTTX:
288893359Simp		wreq->wi_len += 2;
288993359Simp		wreq->wi_val[0] = sc->wi_debug.wi_conttx;
289093359Simp		wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
289193359Simp		break;
289293359Simp	case WI_DEBUG_CONTRX:
289393359Simp		wreq->wi_len++;
289493359Simp		wreq->wi_val[0] = sc->wi_debug.wi_contrx;
289593359Simp		break;
289693359Simp	case WI_DEBUG_SIGSTATE:
289793359Simp		wreq->wi_len += 2;
289893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
289993359Simp		wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
290093359Simp		break;
290193359Simp	case WI_DEBUG_CONFBITS:
290293359Simp		wreq->wi_len += 2;
290393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_confbits;
290493359Simp		wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
290593359Simp		break;
290693359Simp	default:
290793359Simp		error = EIO;
290893359Simp		break;
290993359Simp	}
291093359Simp
291193359Simp	return (error);
291293359Simp}
291393359Simp
291493359Simpstatic int
291593359Simpwi_set_debug(sc, wreq)
291693359Simp	struct wi_softc		*sc;
291793359Simp	struct wi_req		*wreq;
291893359Simp{
291993359Simp	int			error = 0;
292093359Simp	u_int16_t		cmd, param0 = 0, param1 = 0;
292193359Simp
292293359Simp	switch (wreq->wi_type) {
292393359Simp	case WI_DEBUG_RESET:
292493359Simp	case WI_DEBUG_INIT:
292593359Simp	case WI_DEBUG_CALENABLE:
292693359Simp		break;
292793359Simp	case WI_DEBUG_SLEEP:
292893359Simp		sc->wi_debug.wi_sleep = 1;
292993359Simp		break;
293093359Simp	case WI_DEBUG_WAKE:
293193359Simp		sc->wi_debug.wi_sleep = 0;
293293359Simp		break;
293393359Simp	case WI_DEBUG_CHAN:
293493359Simp		param0 = wreq->wi_val[0];
293593359Simp		break;
293693359Simp	case WI_DEBUG_DELAYSUPP:
293793359Simp		sc->wi_debug.wi_delaysupp = 1;
293893359Simp		break;
293993359Simp	case WI_DEBUG_TXSUPP:
294093359Simp		sc->wi_debug.wi_txsupp = 1;
294193359Simp		break;
294293359Simp	case WI_DEBUG_MONITOR:
294393359Simp		sc->wi_debug.wi_monitor = 1;
294493359Simp		break;
294593359Simp	case WI_DEBUG_LEDTEST:
294693359Simp		param0 = wreq->wi_val[0];
294793359Simp		param1 = wreq->wi_val[1];
294893359Simp		sc->wi_debug.wi_ledtest = 1;
294993359Simp		sc->wi_debug.wi_ledtest_param0 = param0;
295093359Simp		sc->wi_debug.wi_ledtest_param1 = param1;
295193359Simp		break;
295293359Simp	case WI_DEBUG_CONTTX:
295393359Simp		param0 = wreq->wi_val[0];
295493359Simp		sc->wi_debug.wi_conttx = 1;
295593359Simp		sc->wi_debug.wi_conttx_param0 = param0;
295693359Simp		break;
295793359Simp	case WI_DEBUG_STOPTEST:
295893359Simp		sc->wi_debug.wi_delaysupp = 0;
295993359Simp		sc->wi_debug.wi_txsupp = 0;
296093359Simp		sc->wi_debug.wi_monitor = 0;
296193359Simp		sc->wi_debug.wi_ledtest = 0;
296293359Simp		sc->wi_debug.wi_ledtest_param0 = 0;
296393359Simp		sc->wi_debug.wi_ledtest_param1 = 0;
296493359Simp		sc->wi_debug.wi_conttx = 0;
296593359Simp		sc->wi_debug.wi_conttx_param0 = 0;
296693359Simp		sc->wi_debug.wi_contrx = 0;
296793359Simp		sc->wi_debug.wi_sigstate = 0;
296893359Simp		sc->wi_debug.wi_sigstate_param0 = 0;
296993359Simp		break;
297093359Simp	case WI_DEBUG_CONTRX:
297193359Simp		sc->wi_debug.wi_contrx = 1;
297293359Simp		break;
297393359Simp	case WI_DEBUG_SIGSTATE:
297493359Simp		param0 = wreq->wi_val[0];
297593359Simp		sc->wi_debug.wi_sigstate = 1;
297693359Simp		sc->wi_debug.wi_sigstate_param0 = param0;
297793359Simp		break;
297893359Simp	case WI_DEBUG_CONFBITS:
297993359Simp		param0 = wreq->wi_val[0];
298093359Simp		param1 = wreq->wi_val[1];
298193359Simp		sc->wi_debug.wi_confbits = param0;
298293359Simp		sc->wi_debug.wi_confbits_param0 = param1;
298393359Simp		break;
298493359Simp	default:
298593359Simp		error = EIO;
298693359Simp		break;
298793359Simp	}
298893359Simp
298993359Simp	if (error)
299093359Simp		return (error);
299193359Simp
299293359Simp	cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
299393359Simp	error = wi_cmd(sc, cmd, param0, param1, 0);
299493359Simp
299593359Simp	return (error);
299693359Simp}
2997