if_wi.c revision 93611
146492Swpaul/*
246492Swpaul * Copyright (c) 1997, 1998, 1999
346492Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
446492Swpaul *
546492Swpaul * Redistribution and use in source and binary forms, with or without
646492Swpaul * modification, are permitted provided that the following conditions
746492Swpaul * are met:
846492Swpaul * 1. Redistributions of source code must retain the above copyright
946492Swpaul *    notice, this list of conditions and the following disclaimer.
1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1146492Swpaul *    notice, this list of conditions and the following disclaimer in the
1246492Swpaul *    documentation and/or other materials provided with the distribution.
1346492Swpaul * 3. All advertising materials mentioning features or use of this software
1446492Swpaul *    must display the following acknowledgement:
1546492Swpaul *	This product includes software developed by Bill Paul.
1646492Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1746492Swpaul *    may be used to endorse or promote products derived from this software
1846492Swpaul *    without specific prior written permission.
1946492Swpaul *
2046492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2146492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2246492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2346492Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2446492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2546492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2646492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2746492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2846492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2946492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3046492Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3146492Swpaul */
3246492Swpaul
3346492Swpaul/*
3446492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD.
3546492Swpaul *
3646492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
3746492Swpaul * Electrical Engineering Department
3846492Swpaul * Columbia University, New York City
3946492Swpaul */
4046492Swpaul
4146492Swpaul/*
4247401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN
4346492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed
4446492Swpaul * entirely via a firmware-driven controller called the Hermes.
4546492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual
4646492Swpaul * without an NDA (if at all). What they do release is an API library
4746492Swpaul * called the HCF (Hardware Control Functions) which is supposed to
4846492Swpaul * do the device-specific operations of a device driver for you. The
4946492Swpaul * publically available version of the HCF library (the 'HCF Light') is
5047401Swpaul * a) extremely gross, b) lacks certain features, particularly support
5146492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License.
5246492Swpaul *
5346492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it
5446492Swpaul * programs the Hermes controller directly, using information gleaned
5546492Swpaul * from the HCF Light code and corresponding documentation.
5646492Swpaul *
5746492Swpaul * This driver supports both the PCMCIA and ISA versions of the
5846492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really
5946492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter
6046492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is
6146492Swpaul * inserted. Consequently, you need to use the pccard support for
6246492Swpaul * both the ISA and PCMCIA adapters.
6346492Swpaul */
6446492Swpaul
6546492Swpaul#include <sys/param.h>
6646492Swpaul#include <sys/systm.h>
6746492Swpaul#include <sys/sockio.h>
6846492Swpaul#include <sys/mbuf.h>
6983366Sjulian#include <sys/proc.h>
7046492Swpaul#include <sys/kernel.h>
7146492Swpaul#include <sys/socket.h>
7253702Swpaul#include <sys/module.h>
7353702Swpaul#include <sys/bus.h>
7453702Swpaul#include <sys/syslog.h>
7553702Swpaul#include <sys/sysctl.h>
7646492Swpaul
7753702Swpaul#include <machine/bus.h>
7853702Swpaul#include <machine/resource.h>
7953702Swpaul#include <sys/rman.h>
8053702Swpaul
8146492Swpaul#include <net/if.h>
8246492Swpaul#include <net/if_arp.h>
8346492Swpaul#include <net/ethernet.h>
8446492Swpaul#include <net/if_dl.h>
8546492Swpaul#include <net/if_media.h>
8646492Swpaul#include <net/if_types.h>
8777217Sphk#include <net/if_ieee80211.h>
8846492Swpaul
8946492Swpaul#include <netinet/in.h>
9046492Swpaul#include <netinet/in_systm.h>
9146492Swpaul#include <netinet/in_var.h>
9246492Swpaul#include <netinet/ip.h>
9346492Swpaul#include <netinet/if_ether.h>
9446492Swpaul
9546492Swpaul#include <net/bpf.h>
9646492Swpaul
9770808Speter#include <dev/wi/if_wavelan_ieee.h>
9893611Simp#include <dev/wi/if_wivar.h>
9970808Speter#include <dev/wi/if_wireg.h>
10046492Swpaul
10146492Swpaul#if !defined(lint)
10246492Swpaulstatic const char rcsid[] =
10350477Speter  "$FreeBSD: head/sys/dev/wi/if_wi.c 93611 2002-04-02 02:38:35Z imp $";
10446492Swpaul#endif
10546492Swpaul
10691693Simpstatic void wi_intr(void *);
10791693Simpstatic void wi_reset(struct wi_softc *);
10891693Simpstatic int wi_ioctl(struct ifnet *, u_long, caddr_t);
10991693Simpstatic void wi_init(void *);
11091693Simpstatic void wi_start(struct ifnet *);
11191693Simpstatic void wi_stop(struct wi_softc *);
11291693Simpstatic void wi_watchdog(struct ifnet *);
11391693Simpstatic void wi_rxeof(struct wi_softc *);
11491693Simpstatic void wi_txeof(struct wi_softc *, int);
11591693Simpstatic void wi_update_stats(struct wi_softc *);
11691693Simpstatic void wi_setmulti(struct wi_softc *);
11746492Swpaul
11892457Simpstatic int wi_cmd(struct wi_softc *, int, int, int, int);
11991693Simpstatic int wi_read_record(struct wi_softc *, struct wi_ltv_gen *);
12091693Simpstatic int wi_write_record(struct wi_softc *, struct wi_ltv_gen *);
12191693Simpstatic int wi_read_data(struct wi_softc *, int, int, caddr_t, int);
12291693Simpstatic int wi_write_data(struct wi_softc *, int, int, caddr_t, int);
12391693Simpstatic int wi_seek(struct wi_softc *, int, int, int);
12491693Simpstatic int wi_alloc_nicmem(struct wi_softc *, int, int *);
12591693Simpstatic void wi_inquire(void *);
12691693Simpstatic void wi_setdef(struct wi_softc *, struct wi_req *);
12791693Simpstatic int wi_mgmt_xmit(struct wi_softc *, caddr_t, int);
12846492Swpaul
12953702Swpaul#ifdef WICACHE
13053702Swpaulstatic
13191693Simpvoid wi_cache_store(struct wi_softc *, struct ether_header *,
13291693Simp	struct mbuf *, unsigned short);
13353702Swpaul#endif
13453702Swpaul
13591693Simpstatic int wi_get_cur_ssid(struct wi_softc *, char *, int *);
13691693Simpstatic void wi_get_id(struct wi_softc *, device_t);
13791693Simpstatic int wi_media_change(struct ifnet *);
13891693Simpstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
13977217Sphk
14093359Simpstatic int wi_get_debug(struct wi_softc *, struct wi_req *);
14193359Simpstatic int wi_set_debug(struct wi_softc *, struct wi_req *);
14293359Simp
14393611Simpdevclass_t wi_devclass;
14453702Swpaul
14593611Simpint
14693611Simpwi_generic_detach(dev)
14753702Swpaul	device_t		dev;
14846492Swpaul{
14946492Swpaul	struct wi_softc		*sc;
15046492Swpaul	struct ifnet		*ifp;
15146492Swpaul
15253702Swpaul	sc = device_get_softc(dev);
15367092Swpaul	WI_LOCK(sc);
15446492Swpaul	ifp = &sc->arpcom.ac_if;
15546492Swpaul
15646492Swpaul	if (sc->wi_gone) {
15753702Swpaul		device_printf(dev, "already unloaded\n");
15867092Swpaul		WI_UNLOCK(sc);
15953702Swpaul		return(ENODEV);
16046492Swpaul	}
16146492Swpaul
16253702Swpaul	wi_stop(sc);
16358274Srwatson
16477217Sphk	/* Delete all remaining media. */
16577217Sphk	ifmedia_removeall(&sc->ifmedia);
16677217Sphk
16763090Sarchie	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
16854277Swpaul	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
16953702Swpaul	wi_free(dev);
17046492Swpaul	sc->wi_gone = 1;
17146492Swpaul
17267092Swpaul	WI_UNLOCK(sc);
17367092Swpaul	mtx_destroy(&sc->wi_mtx);
17446492Swpaul
17546492Swpaul	return(0);
17646492Swpaul}
17746492Swpaul
17893611Simpint
17974906Salfredwi_generic_attach(device_t dev)
18074906Salfred{
18174906Salfred	struct wi_softc		*sc;
18274906Salfred	struct wi_ltv_macaddr	mac;
18374906Salfred	struct wi_ltv_gen	gen;
18474906Salfred	struct ifnet		*ifp;
18574906Salfred	int			error;
18674906Salfred
18774906Salfred	sc = device_get_softc(dev);
18874906Salfred	ifp = &sc->arpcom.ac_if;
18974906Salfred
19053702Swpaul	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
19174998Swpaul	    wi_intr, sc, &sc->wi_intrhand);
19253702Swpaul
19353702Swpaul	if (error) {
19453702Swpaul		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
19553702Swpaul		wi_free(dev);
19653702Swpaul		return (error);
19753702Swpaul	}
19853702Swpaul
19971228Sbmilekic	mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
20067092Swpaul	WI_LOCK(sc);
20167092Swpaul
20246492Swpaul	/* Reset the NIC. */
20346492Swpaul	wi_reset(sc);
20446492Swpaul
20576438Swpaul	/*
20676438Swpaul	 * Read the station address.
20776438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
20876438Swpaul	 * an error when trying to read it the first time, which causes
20976438Swpaul	 * the probe to fail.
21076438Swpaul	 */
21146492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
21246492Swpaul	mac.wi_len = 4;
21376457Sgrog	wi_read_record(sc, (struct wi_ltv_gen *)&mac);
21475150Simp	if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) {
21575149Simp		device_printf(dev, "mac read failed %d\n", error);
21675149Simp		wi_free(dev);
21775149Simp		return (error);
21875149Simp	}
21946492Swpaul	bcopy((char *)&mac.wi_mac_addr,
22046492Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
22146492Swpaul
22287383Simp	device_printf(dev, "802.11 address: %6D\n", sc->arpcom.ac_enaddr, ":");
22346492Swpaul
22487383Simp	wi_get_id(sc, dev);
22587383Simp
22646492Swpaul	ifp->if_softc = sc;
22746492Swpaul	ifp->if_unit = sc->wi_unit;
22846492Swpaul	ifp->if_name = "wi";
22946492Swpaul	ifp->if_mtu = ETHERMTU;
23046492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
23146492Swpaul	ifp->if_ioctl = wi_ioctl;
23246492Swpaul	ifp->if_output = ether_output;
23346492Swpaul	ifp->if_start = wi_start;
23446492Swpaul	ifp->if_watchdog = wi_watchdog;
23546492Swpaul	ifp->if_init = wi_init;
23646492Swpaul	ifp->if_baudrate = 10000000;
23746492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
23846492Swpaul
23946492Swpaul	bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
24046492Swpaul	bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name,
24146492Swpaul	    sizeof(WI_DEFAULT_NODENAME) - 1);
24246492Swpaul
24346492Swpaul	bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
24446492Swpaul	bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name,
24546492Swpaul	    sizeof(WI_DEFAULT_NETNAME) - 1);
24646492Swpaul
24746492Swpaul	bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
24846492Swpaul	bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name,
24946492Swpaul	    sizeof(WI_DEFAULT_IBSS) - 1);
25046492Swpaul
25146492Swpaul	sc->wi_portnum = WI_DEFAULT_PORT;
25274139Sassar	sc->wi_ptype = WI_PORTTYPE_BSS;
25346492Swpaul	sc->wi_ap_density = WI_DEFAULT_AP_DENSITY;
25446492Swpaul	sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH;
25546492Swpaul	sc->wi_tx_rate = WI_DEFAULT_TX_RATE;
25646492Swpaul	sc->wi_max_data_len = WI_DEFAULT_DATALEN;
25746492Swpaul	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
25846611Swpaul	sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED;
25946611Swpaul	sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP;
26091695Simp	sc->wi_roaming = WI_DEFAULT_ROAMING;
26191695Simp	sc->wi_authtype = WI_DEFAULT_AUTHTYPE;
26246492Swpaul
26346563Swpaul	/*
26446563Swpaul	 * Read the default channel from the NIC. This may vary
26546563Swpaul	 * depending on the country where the NIC was purchased, so
26646563Swpaul	 * we can't hard-code a default and expect it to work for
26746563Swpaul	 * everyone.
26846563Swpaul	 */
26946563Swpaul	gen.wi_type = WI_RID_OWN_CHNL;
27046563Swpaul	gen.wi_len = 2;
27146563Swpaul	wi_read_record(sc, &gen);
27246563Swpaul	sc->wi_channel = gen.wi_val;
27346563Swpaul
27456965Swpaul	/*
27556965Swpaul	 * Find out if we support WEP on this card.
27656965Swpaul	 */
27756965Swpaul	gen.wi_type = WI_RID_WEP_AVAIL;
27856965Swpaul	gen.wi_len = 2;
27956965Swpaul	wi_read_record(sc, &gen);
28056965Swpaul	sc->wi_has_wep = gen.wi_val;
28156965Swpaul
28270073Swpaul	if (bootverbose) {
28370073Swpaul		device_printf(sc->dev,
28487599Sobrien				"%s:wi_has_wep = %d\n",
28587599Sobrien				__func__, sc->wi_has_wep);
28670073Swpaul	}
28770073Swpaul
28846492Swpaul	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
28946492Swpaul
29046492Swpaul	wi_init(sc);
29146492Swpaul	wi_stop(sc);
29246492Swpaul
29377217Sphk	ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status);
29477217Sphk	/* XXX: Should read from card capabilities */
29577217Sphk#define ADD(m, c)       ifmedia_add(&sc->ifmedia, (m), (c), NULL)
29677217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1,
29777217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
29877217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0);
29977217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2,
30077217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
30177217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0);
30277217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5,
30377217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
30477217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0);
30577217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11,
30677217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
30777217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0);
30877217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
30977217Sphk		IFM_IEEE80211_ADHOC, 0), 0);
31077217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0);
31177217Sphk#undef	ADD
31277217Sphk	ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
31377217Sphk	    0, 0));
31477217Sphk
31577217Sphk
31646492Swpaul	/*
31763090Sarchie	 * Call MI attach routine.
31846492Swpaul	 */
31963090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
32053702Swpaul	callout_handle_init(&sc->wi_stat_ch);
32167092Swpaul	WI_UNLOCK(sc);
32246492Swpaul
32346492Swpaul	return(0);
32446492Swpaul}
32546492Swpaul
32687383Simpstatic void
32787383Simpwi_get_id(sc, dev)
32887383Simp	struct wi_softc *sc;
32987383Simp	device_t dev;
33087383Simp{
33187383Simp	struct wi_ltv_ver       ver;
33287383Simp
33387383Simp	/* getting chip identity */
33487383Simp	memset(&ver, 0, sizeof(ver));
33593567Simp	ver.wi_type = WI_RID_CARD_ID;
33687383Simp	ver.wi_len = 5;
33787383Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
33887383Simp	device_printf(dev, "using ");
33992457Simp	sc->wi_prism2 = 1;
34092457Simp	sc->wi_nic_type = le16toh(ver.wi_ver[0]);
34192457Simp	switch (sc->wi_nic_type) {
34287383Simp	case WI_NIC_EVB2:
34393360Simp		printf("RF:PRISM I MAC:HFA3841");
34487383Simp		break;
34587383Simp	case WI_NIC_HWB3763:
34693360Simp		printf("RF:PRISM II MAC:HFA3841 CARD:HWB3763 rev.B");
34787383Simp		break;
34887383Simp	case WI_NIC_HWB3163:
34993360Simp		printf("RF:PRISM II MAC:HFA3841 CARD:HWB3163 rev.A");
35087383Simp		break;
35187383Simp	case WI_NIC_HWB3163B:
35293360Simp		printf("RF:PRISM II MAC:HFA3841 CARD:HWB3163 rev.B");
35387383Simp		break;
35487383Simp	case WI_NIC_EVB3:
35593560Simp	case WI_NIC_3842:
35693360Simp		printf("RF:PRISM II MAC:HFA3842");
35787383Simp		break;
35887383Simp	case WI_NIC_HWB1153:
35993360Simp		printf("RF:PRISM I MAC:HFA3841 CARD:HWB1153");
36087383Simp		break;
36187383Simp	case WI_NIC_P2_SST:
36293560Simp	case WI_NIC_EVB2_SST:
36393360Simp		printf("RF:PRISM II MAC:HFA3841 CARD:HWB3163-SST-flash");
36487383Simp		break;
36587383Simp	case WI_NIC_PRISM2_5:
36687383Simp		printf("RF:PRISM2.5 MAC:ISL3873");
36787383Simp		break;
36887383Simp	case WI_NIC_3874A:
36987383Simp		printf("RF:PRISM2.5 MAC:ISL3874A(PCI)");
37087383Simp		break;
37193360Simp	case WI_NIC_37300P:
37293360Simp		printf("RF:PRISM2.5 MAC:ISL37300P");
37393360Simp		break;
37493563Simp	case WI_NIC_P3_SST:
37593560Simp		printf("RF:PRISM3");
37693560Simp		break;
37793563Simp	case WI_NIC_P3_PCI:
37893560Simp		printf("RF:PRISM3");
37993560Simp		break;
38092457Simp	case WI_NIC_LUCENT:
38192457Simp	case WI_NIC_LUCENT_ALT:
38292457Simp		printf("WaveLan/Lucent/Orinoco chip");
38392457Simp		sc->wi_prism2 = 0;
38492457Simp		break;
38587383Simp	default:
38692457Simp		printf("Lucent chip or unknown chip %04x", ver.wi_ver[0]);
38787383Simp		sc->wi_prism2 = 0;
38887383Simp		break;
38987383Simp	}
39087383Simp
39192457Simp	/* get firmware version */
39292457Simp	memset(&ver, 0, sizeof(ver));
39392457Simp	ver.wi_type = WI_RID_STA_IDENTITY;
39492457Simp	ver.wi_len = 5;
39592457Simp	wi_read_record(sc, (struct wi_ltv_gen *)&ver);
39692457Simp	ver.wi_ver[1] = le16toh(ver.wi_ver[1]);
39792457Simp	ver.wi_ver[2] = le16toh(ver.wi_ver[2]);
39892457Simp	ver.wi_ver[3] = le16toh(ver.wi_ver[3]);
39992457Simp	sc->wi_firmware_ver = ver.wi_ver[2] * 100 + ver.wi_ver[3] *  10 +
40092457Simp	    ver.wi_ver[1];
40192457Simp	printf(", Firmware: %d.%02d variant %d\n", ver.wi_ver[2],
40292457Simp	    ver.wi_ver[3], ver.wi_ver[1]);
40387383Simp
40487383Simp	return;
40587383Simp}
40687383Simp
40788546Salfredstatic void
40888546Salfredwi_rxeof(sc)
40946492Swpaul	struct wi_softc		*sc;
41046492Swpaul{
41146492Swpaul	struct ifnet		*ifp;
41246492Swpaul	struct ether_header	*eh;
41346492Swpaul	struct mbuf		*m;
41446492Swpaul	int			id;
41546492Swpaul
41646492Swpaul	ifp = &sc->arpcom.ac_if;
41746492Swpaul
41846492Swpaul	id = CSR_READ_2(sc, WI_RX_FID);
41946492Swpaul
42093359Simp	/*
42193359Simp	 * if we have the procframe flag set, disregard all this and just
42293359Simp	 * read the data from the device.
42393359Simp	 */
42493359Simp	if (sc->wi_procframe || sc->wi_debug.wi_monitor) {
42593359Simp		struct wi_frame		*rx_frame;
42693359Simp		int			datlen, hdrlen;
42746492Swpaul
42893359Simp		/* first allocate mbuf for packet storage */
42993359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
43093359Simp		if (m == NULL) {
43193359Simp			ifp->if_ierrors++;
43293359Simp			return;
43393359Simp		}
43493359Simp		MCLGET(m, M_DONTWAIT);
43593359Simp		if (!(m->m_flags & M_EXT)) {
43693359Simp			m_freem(m);
43793359Simp			ifp->if_ierrors++;
43893359Simp			return;
43993359Simp		}
44046492Swpaul
44193359Simp		m->m_pkthdr.rcvif = ifp;
44246492Swpaul
44393359Simp		/* now read wi_frame first so we know how much data to read */
44493359Simp		if (wi_read_data(sc, id, 0, mtod(m, caddr_t),
44593359Simp		    sizeof(struct wi_frame))) {
44693359Simp			m_freem(m);
44793359Simp			ifp->if_ierrors++;
44893359Simp			return;
44993359Simp		}
45046492Swpaul
45193359Simp		rx_frame = mtod(m, struct wi_frame *);
45293359Simp
45393359Simp		switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) {
45493359Simp		case 7:
45593359Simp			switch (rx_frame->wi_frame_ctl & WI_FCTL_FTYPE) {
45693359Simp			case WI_FTYPE_DATA:
45793359Simp				hdrlen = WI_DATA_HDRLEN;
45893359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
45993359Simp				break;
46093359Simp			case WI_FTYPE_MGMT:
46193359Simp				hdrlen = WI_MGMT_HDRLEN;
46293359Simp				datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
46393359Simp				break;
46493359Simp			case WI_FTYPE_CTL:
46593359Simp				/*
46693359Simp				 * prism2 cards don't pass control packets
46793359Simp				 * down properly or consistently, so we'll only
46893359Simp				 * pass down the header.
46993359Simp				 */
47093359Simp				hdrlen = WI_CTL_HDRLEN;
47193359Simp				datlen = 0;
47293359Simp				break;
47393359Simp			default:
47493359Simp				device_printf(sc->dev, "received packet of "
47593359Simp				    "unknown type on port 7\n");
47693359Simp				m_freem(m);
47793359Simp				ifp->if_ierrors++;
47893359Simp				return;
47993359Simp			}
48093359Simp			break;
48193359Simp		case 0:
48293359Simp			hdrlen = WI_DATA_HDRLEN;
48393359Simp			datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
48493359Simp			break;
48593359Simp		default:
48693359Simp			device_printf(sc->dev, "received packet on invalid "
48793359Simp			    "port (wi_status=0x%x)\n", rx_frame->wi_status);
48893359Simp			m_freem(m);
48993359Simp			ifp->if_ierrors++;
49093359Simp			return;
49193359Simp		}
49293359Simp
49393359Simp		if ((hdrlen + datlen + 2) > MCLBYTES) {
49453702Swpaul			device_printf(sc->dev, "oversized packet received "
49553702Swpaul			    "(wi_dat_len=%d, wi_status=0x%x)\n",
49693359Simp			    datlen, rx_frame->wi_status);
49748553Swpaul			m_freem(m);
49848553Swpaul			ifp->if_ierrors++;
49948553Swpaul			return;
50048553Swpaul		}
50146492Swpaul
50293359Simp		if (wi_read_data(sc, id, hdrlen, mtod(m, caddr_t) + hdrlen,
50393359Simp		    datlen + 2)) {
50493359Simp			m_freem(m);
50593359Simp			ifp->if_ierrors++;
50693359Simp			return;
50759328Swpaul		}
50875373Salfred
50993359Simp		m->m_pkthdr.len = m->m_len = hdrlen + datlen;
51046492Swpaul
51193359Simp		ifp->if_ipackets++;
51293359Simp
51393359Simp		/* Handle BPF listeners. */
51493359Simp		if (ifp->if_bpf)
51593359Simp			bpf_mtap(ifp, m);
51693359Simp
51793359Simp		m_freem(m);
51893359Simp	} else {
51993359Simp		struct wi_frame		rx_frame;
52093359Simp
52193359Simp		/* First read in the frame header */
52293359Simp		if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame,
52393359Simp		    sizeof(rx_frame))) {
52446492Swpaul			ifp->if_ierrors++;
52546492Swpaul			return;
52646492Swpaul		}
52793359Simp
52893359Simp		if (rx_frame.wi_status & WI_STAT_ERRSTAT) {
52948553Swpaul			ifp->if_ierrors++;
53048553Swpaul			return;
53148553Swpaul		}
53246492Swpaul
53393359Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
53493359Simp		if (m == NULL) {
53593359Simp			ifp->if_ierrors++;
53693359Simp			return;
53793359Simp		}
53893359Simp		MCLGET(m, M_DONTWAIT);
53993359Simp		if (!(m->m_flags & M_EXT)) {
54046492Swpaul			m_freem(m);
54146492Swpaul			ifp->if_ierrors++;
54246492Swpaul			return;
54346492Swpaul		}
54446492Swpaul
54593359Simp		eh = mtod(m, struct ether_header *);
54693359Simp		m->m_pkthdr.rcvif = ifp;
54746492Swpaul
54893359Simp		if (rx_frame.wi_status == WI_STAT_1042 ||
54993359Simp		    rx_frame.wi_status == WI_STAT_TUNNEL ||
55093359Simp		    rx_frame.wi_status == WI_STAT_WMP_MSG) {
55193359Simp			if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) {
55293359Simp				device_printf(sc->dev,
55393359Simp				    "oversized packet received "
55493359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
55593359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
55693359Simp				m_freem(m);
55793359Simp				ifp->if_ierrors++;
55893359Simp				return;
55993359Simp			}
56093359Simp			m->m_pkthdr.len = m->m_len =
56193359Simp			    rx_frame.wi_dat_len + WI_SNAPHDR_LEN;
56293359Simp
56393359Simp#if 0
56493359Simp			bcopy((char *)&rx_frame.wi_addr1,
56593359Simp			    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
56693359Simp			if (sc->wi_ptype == WI_PORTTYPE_ADHOC) {
56793359Simp				bcopy((char *)&rx_frame.wi_addr2,
56893359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
56993359Simp			} else {
57093359Simp				bcopy((char *)&rx_frame.wi_addr3,
57193359Simp				    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
57293359Simp			}
57393359Simp#else
57493359Simp			bcopy((char *)&rx_frame.wi_dst_addr,
57593359Simp				(char *)&eh->ether_dhost, ETHER_ADDR_LEN);
57693359Simp			bcopy((char *)&rx_frame.wi_src_addr,
57793359Simp				(char *)&eh->ether_shost, ETHER_ADDR_LEN);
57893359Simp#endif
57993359Simp
58093359Simp			bcopy((char *)&rx_frame.wi_type,
58193359Simp			    (char *)&eh->ether_type, ETHER_TYPE_LEN);
58293359Simp
58393359Simp			if (wi_read_data(sc, id, WI_802_11_OFFSET,
58493359Simp			    mtod(m, caddr_t) + sizeof(struct ether_header),
58593359Simp			    m->m_len + 2)) {
58693359Simp				m_freem(m);
58793359Simp				ifp->if_ierrors++;
58893359Simp				return;
58993359Simp			}
59093359Simp		} else {
59193359Simp			if((rx_frame.wi_dat_len +
59293359Simp			    sizeof(struct ether_header)) > MCLBYTES) {
59393359Simp				device_printf(sc->dev,
59493359Simp				    "oversized packet received "
59593359Simp				    "(wi_dat_len=%d, wi_status=0x%x)\n",
59693359Simp				    rx_frame.wi_dat_len, rx_frame.wi_status);
59793359Simp				m_freem(m);
59893359Simp				ifp->if_ierrors++;
59993359Simp				return;
60093359Simp			}
60193359Simp			m->m_pkthdr.len = m->m_len =
60293359Simp			    rx_frame.wi_dat_len + sizeof(struct ether_header);
60393359Simp
60493359Simp			if (wi_read_data(sc, id, WI_802_3_OFFSET,
60593359Simp			    mtod(m, caddr_t), m->m_len + 2)) {
60693359Simp				m_freem(m);
60793359Simp				ifp->if_ierrors++;
60893359Simp				return;
60993359Simp			}
61093359Simp		}
61193359Simp
61293359Simp		ifp->if_ipackets++;
61393359Simp
61493359Simp		/* Receive packet. */
61593359Simp		m_adj(m, sizeof(struct ether_header));
61653702Swpaul#ifdef WICACHE
61793359Simp		wi_cache_store(sc, eh, m, rx_frame.wi_q_info);
61853702Swpaul#endif
61993359Simp		ether_input(ifp, eh, m);
62093359Simp	}
62146492Swpaul}
62246492Swpaul
62388546Salfredstatic void
62488546Salfredwi_txeof(sc, status)
62546492Swpaul	struct wi_softc		*sc;
62646492Swpaul	int			status;
62746492Swpaul{
62846492Swpaul	struct ifnet		*ifp;
62946492Swpaul
63046492Swpaul	ifp = &sc->arpcom.ac_if;
63146492Swpaul
63246492Swpaul	ifp->if_timer = 0;
63346492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
63446492Swpaul
63546492Swpaul	if (status & WI_EV_TX_EXC)
63646492Swpaul		ifp->if_oerrors++;
63746492Swpaul	else
63846492Swpaul		ifp->if_opackets++;
63946492Swpaul
64046492Swpaul	return;
64146492Swpaul}
64246492Swpaul
64388546Salfredvoid
64488546Salfredwi_inquire(xsc)
64546492Swpaul	void			*xsc;
64646492Swpaul{
64746492Swpaul	struct wi_softc		*sc;
64846492Swpaul	struct ifnet		*ifp;
64946492Swpaul
65046492Swpaul	sc = xsc;
65146492Swpaul	ifp = &sc->arpcom.ac_if;
65246492Swpaul
65346492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
65446492Swpaul
65546492Swpaul	/* Don't do this while we're transmitting */
65646492Swpaul	if (ifp->if_flags & IFF_OACTIVE)
65746492Swpaul		return;
65846492Swpaul
65992457Simp	wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0);
66046492Swpaul
66146492Swpaul	return;
66246492Swpaul}
66346492Swpaul
66488546Salfredvoid
66588546Salfredwi_update_stats(sc)
66646492Swpaul	struct wi_softc		*sc;
66746492Swpaul{
66846492Swpaul	struct wi_ltv_gen	gen;
66946492Swpaul	u_int16_t		id;
67046492Swpaul	struct ifnet		*ifp;
67146492Swpaul	u_int32_t		*ptr;
67275373Salfred	int			len, i;
67346492Swpaul	u_int16_t		t;
67446492Swpaul
67546492Swpaul	ifp = &sc->arpcom.ac_if;
67646492Swpaul
67746492Swpaul	id = CSR_READ_2(sc, WI_INFO_FID);
67846492Swpaul
67946492Swpaul	wi_read_data(sc, id, 0, (char *)&gen, 4);
68046492Swpaul
68193359Simp	/*
68293359Simp	 * if we just got our scan results, copy it over into the scan buffer
68393359Simp	 * so we can return it to anyone that asks for it. (add a little
68493359Simp	 * compatibility with the prism2 scanning mechanism)
68593359Simp	 */
68693359Simp	if (gen.wi_type == WI_INFO_SCAN_RESULTS)
68793359Simp	{
68893359Simp		sc->wi_scanbuf_len = gen.wi_len;
68993359Simp		wi_read_data(sc, id, 4, (char *)sc->wi_scanbuf,
69093359Simp		    sc->wi_scanbuf_len * 2);
69193359Simp
69246492Swpaul		return;
69393359Simp	}
69493359Simp	else if (gen.wi_type != WI_INFO_COUNTERS)
69593359Simp		return;
69646492Swpaul
69775373Salfred	len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ?
69875373Salfred		gen.wi_len - 1 : sizeof(sc->wi_stats) / 4;
69946492Swpaul	ptr = (u_int32_t *)&sc->wi_stats;
70046492Swpaul
70175373Salfred	for (i = 0; i < len - 1; i++) {
70246492Swpaul		t = CSR_READ_2(sc, WI_DATA1);
70346492Swpaul#ifdef WI_HERMES_STATS_WAR
70446492Swpaul		if (t > 0xF000)
70546492Swpaul			t = ~t & 0xFFFF;
70646492Swpaul#endif
70746492Swpaul		ptr[i] += t;
70846492Swpaul	}
70946492Swpaul
71046492Swpaul	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
71146492Swpaul	    sc->wi_stats.wi_tx_multi_retries +
71246492Swpaul	    sc->wi_stats.wi_tx_retry_limit;
71346492Swpaul
71446492Swpaul	return;
71546492Swpaul}
71646492Swpaul
71788546Salfredstatic void
71888546Salfredwi_intr(xsc)
71953702Swpaul	void		*xsc;
72046492Swpaul{
72153702Swpaul	struct wi_softc		*sc = xsc;
72246492Swpaul	struct ifnet		*ifp;
72346492Swpaul	u_int16_t		status;
72446492Swpaul
72567092Swpaul	WI_LOCK(sc);
72667092Swpaul
72746492Swpaul	ifp = &sc->arpcom.ac_if;
72846492Swpaul
72975373Salfred	if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) {
73046492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
73146492Swpaul		CSR_WRITE_2(sc, WI_INT_EN, 0);
73267092Swpaul		WI_UNLOCK(sc);
73346492Swpaul		return;
73446492Swpaul	}
73546492Swpaul
73646492Swpaul	/* Disable interrupts. */
73746492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
73846492Swpaul
73946492Swpaul	status = CSR_READ_2(sc, WI_EVENT_STAT);
74046492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS);
74146492Swpaul
74246492Swpaul	if (status & WI_EV_RX) {
74346492Swpaul		wi_rxeof(sc);
74446492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
74546492Swpaul	}
74646492Swpaul
74746492Swpaul	if (status & WI_EV_TX) {
74846492Swpaul		wi_txeof(sc, status);
74946492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX);
75046492Swpaul	}
75146492Swpaul
75246492Swpaul	if (status & WI_EV_ALLOC) {
75346492Swpaul		int			id;
75475373Salfred
75546492Swpaul		id = CSR_READ_2(sc, WI_ALLOC_FID);
75646492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
75746492Swpaul		if (id == sc->wi_tx_data_id)
75846492Swpaul			wi_txeof(sc, status);
75946492Swpaul	}
76046492Swpaul
76146492Swpaul	if (status & WI_EV_INFO) {
76246492Swpaul		wi_update_stats(sc);
76346492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
76446492Swpaul	}
76546492Swpaul
76646492Swpaul	if (status & WI_EV_TX_EXC) {
76746492Swpaul		wi_txeof(sc, status);
76846492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
76946492Swpaul	}
77046492Swpaul
77146492Swpaul	if (status & WI_EV_INFO_DROP) {
77246492Swpaul		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP);
77346492Swpaul	}
77446492Swpaul
77546492Swpaul	/* Re-enable interrupts. */
77646492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
77746492Swpaul
77875373Salfred	if (ifp->if_snd.ifq_head != NULL) {
77946492Swpaul		wi_start(ifp);
78075373Salfred	}
78146492Swpaul
78267092Swpaul	WI_UNLOCK(sc);
78367092Swpaul
78446492Swpaul	return;
78546492Swpaul}
78646492Swpaul
78788546Salfredstatic int
78892457Simpwi_cmd(sc, cmd, val0, val1, val2)
78946492Swpaul	struct wi_softc		*sc;
79046492Swpaul	int			cmd;
79192457Simp	int			val0;
79292457Simp	int			val1;
79392457Simp	int			val2;
79446492Swpaul{
79546492Swpaul	int			i, s = 0;
79646492Swpaul
79770073Swpaul	/* wait for the busy bit to clear */
79875331Simp	for (i = 500; i > 0; i--) {	/* 5s */
79970073Swpaul		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) {
80070073Swpaul			break;
80170073Swpaul		}
80270073Swpaul		DELAY(10*1000);	/* 10 m sec */
80370073Swpaul	}
80475229Salfred	if (i == 0) {
80590580Sbrooks		device_printf(sc->dev, "wi_cmd: busy bit won't clear.\n" );
80670073Swpaul		return(ETIMEDOUT);
80770073Swpaul	}
80870073Swpaul
80992457Simp	CSR_WRITE_2(sc, WI_PARAM0, val0);
81092457Simp	CSR_WRITE_2(sc, WI_PARAM1, val1);
81192457Simp	CSR_WRITE_2(sc, WI_PARAM2, val2);
81246492Swpaul	CSR_WRITE_2(sc, WI_COMMAND, cmd);
81346492Swpaul
81446492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
81546492Swpaul		/*
81646492Swpaul		 * Wait for 'command complete' bit to be
81746492Swpaul		 * set in the event status register.
81846492Swpaul		 */
81990580Sbrooks		s = CSR_READ_2(sc, WI_EVENT_STAT);
82090580Sbrooks		if (s & WI_EV_CMD) {
82146492Swpaul			/* Ack the event and read result code. */
82246492Swpaul			s = CSR_READ_2(sc, WI_STATUS);
82346492Swpaul			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
82446492Swpaul#ifdef foo
82546492Swpaul			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
82646492Swpaul				return(EIO);
82746492Swpaul#endif
82846492Swpaul			if (s & WI_STAT_CMD_RESULT)
82946492Swpaul				return(EIO);
83046492Swpaul			break;
83146492Swpaul		}
83290580Sbrooks		DELAY(WI_DELAY);
83346492Swpaul	}
83446492Swpaul
83590580Sbrooks	if (i == WI_TIMEOUT) {
83690580Sbrooks		device_printf(sc->dev,
83792457Simp		    "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
83846492Swpaul		return(ETIMEDOUT);
83990580Sbrooks	}
84046492Swpaul
84146492Swpaul	return(0);
84246492Swpaul}
84346492Swpaul
84488546Salfredstatic void
84588546Salfredwi_reset(sc)
84646492Swpaul	struct wi_softc		*sc;
84746492Swpaul{
84875331Simp#define WI_INIT_TRIES 5
84975150Simp	int i;
85075149Simp
85175331Simp	for (i = 0; i < WI_INIT_TRIES; i++) {
85292457Simp		if (wi_cmd(sc, WI_CMD_INI, 0, 0, 0) == 0)
85375149Simp			break;
85490580Sbrooks		DELAY(WI_DELAY * 1000);
85575149Simp	}
85675331Simp	if (i == WI_INIT_TRIES)
85753702Swpaul		device_printf(sc->dev, "init failed\n");
85875373Salfred
85946492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, 0);
86046492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
86146492Swpaul
86246492Swpaul	/* Calibrate timer. */
86346492Swpaul	WI_SETVAL(WI_RID_TICK_TIME, 8);
86470073Swpaul
86546492Swpaul	return;
86646492Swpaul}
86746492Swpaul
86846492Swpaul/*
86946492Swpaul * Read an LTV record from the NIC.
87046492Swpaul */
87188546Salfredstatic int
87288546Salfredwi_read_record(sc, ltv)
87346492Swpaul	struct wi_softc		*sc;
87446492Swpaul	struct wi_ltv_gen	*ltv;
87546492Swpaul{
87646492Swpaul	u_int16_t		*ptr;
87746492Swpaul	int			i, len, code;
87870073Swpaul	struct wi_ltv_gen	*oltv, p2ltv;
87946492Swpaul
88070073Swpaul	oltv = ltv;
88170073Swpaul	if (sc->wi_prism2) {
88270073Swpaul		switch (ltv->wi_type) {
88370073Swpaul		case WI_RID_ENCRYPTION:
88470073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
88570073Swpaul			p2ltv.wi_len = 2;
88670073Swpaul			ltv = &p2ltv;
88770073Swpaul			break;
88870073Swpaul		case WI_RID_TX_CRYPT_KEY:
88970073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
89070073Swpaul			p2ltv.wi_len = 2;
89170073Swpaul			ltv = &p2ltv;
89270073Swpaul			break;
89370073Swpaul		}
89470073Swpaul	}
89570073Swpaul
89646492Swpaul	/* Tell the NIC to enter record read mode. */
89792457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type, 0, 0))
89846492Swpaul		return(EIO);
89946492Swpaul
90047789Swpaul	/* Seek to the record. */
90147789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
90247789Swpaul		return(EIO);
90346492Swpaul
90446492Swpaul	/*
90546492Swpaul	 * Read the length and record type and make sure they
90646492Swpaul	 * match what we expect (this verifies that we have enough
90747401Swpaul	 * room to hold all of the returned data).
90846492Swpaul	 */
90946492Swpaul	len = CSR_READ_2(sc, WI_DATA1);
91046492Swpaul	if (len > ltv->wi_len)
91146492Swpaul		return(ENOSPC);
91246492Swpaul	code = CSR_READ_2(sc, WI_DATA1);
91346492Swpaul	if (code != ltv->wi_type)
91446492Swpaul		return(EIO);
91546492Swpaul
91646492Swpaul	ltv->wi_len = len;
91746492Swpaul	ltv->wi_type = code;
91846492Swpaul
91946492Swpaul	/* Now read the data. */
92046492Swpaul	ptr = &ltv->wi_val;
92146492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
92246492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
92346492Swpaul
92470073Swpaul	if (sc->wi_prism2) {
92570073Swpaul		switch (oltv->wi_type) {
92670073Swpaul		case WI_RID_TX_RATE:
92770073Swpaul		case WI_RID_CUR_TX_RATE:
92870073Swpaul			switch (ltv->wi_val) {
92970073Swpaul			case 1: oltv->wi_val = 1; break;
93070073Swpaul			case 2: oltv->wi_val = 2; break;
93170073Swpaul			case 3:	oltv->wi_val = 6; break;
93270073Swpaul			case 4: oltv->wi_val = 5; break;
93370073Swpaul			case 7: oltv->wi_val = 7; break;
93470073Swpaul			case 8: oltv->wi_val = 11; break;
93570073Swpaul			case 15: oltv->wi_val = 3; break;
93670073Swpaul			default: oltv->wi_val = 0x100 + ltv->wi_val; break;
93770073Swpaul			}
93870073Swpaul			break;
93970073Swpaul		case WI_RID_ENCRYPTION:
94070073Swpaul			oltv->wi_len = 2;
94170073Swpaul			if (ltv->wi_val & 0x01)
94270073Swpaul				oltv->wi_val = 1;
94370073Swpaul			else
94470073Swpaul				oltv->wi_val = 0;
94570073Swpaul			break;
94670073Swpaul		case WI_RID_TX_CRYPT_KEY:
94770073Swpaul			oltv->wi_len = 2;
94870073Swpaul			oltv->wi_val = ltv->wi_val;
94970073Swpaul			break;
95091695Simp		case WI_RID_AUTH_CNTL:
95191695Simp                        oltv->wi_len = 2;
95291695Simp			if (le16toh(ltv->wi_val) & 0x01)
95391695Simp				oltv->wi_val = htole16(1);
95491695Simp			else if (le16toh(ltv->wi_val) & 0x02)
95591695Simp				oltv->wi_val = htole16(2);
95691695Simp			break;
95770073Swpaul		}
95870073Swpaul	}
95970073Swpaul
96046492Swpaul	return(0);
96146492Swpaul}
96246492Swpaul
96346492Swpaul/*
96446492Swpaul * Same as read, except we inject data instead of reading it.
96546492Swpaul */
96688546Salfredstatic int
96788546Salfredwi_write_record(sc, ltv)
96846492Swpaul	struct wi_softc		*sc;
96946492Swpaul	struct wi_ltv_gen	*ltv;
97046492Swpaul{
97146492Swpaul	u_int16_t		*ptr;
97246492Swpaul	int			i;
97370073Swpaul	struct wi_ltv_gen	p2ltv;
97446492Swpaul
97570073Swpaul	if (sc->wi_prism2) {
97670073Swpaul		switch (ltv->wi_type) {
97770073Swpaul		case WI_RID_TX_RATE:
97870073Swpaul			p2ltv.wi_type = WI_RID_TX_RATE;
97970073Swpaul			p2ltv.wi_len = 2;
98070073Swpaul			switch (ltv->wi_val) {
98170073Swpaul			case 1: p2ltv.wi_val = 1; break;
98270073Swpaul			case 2: p2ltv.wi_val = 2; break;
98370073Swpaul			case 3:	p2ltv.wi_val = 15; break;
98470073Swpaul			case 5: p2ltv.wi_val = 4; break;
98570073Swpaul			case 6: p2ltv.wi_val = 3; break;
98670073Swpaul			case 7: p2ltv.wi_val = 7; break;
98770073Swpaul			case 11: p2ltv.wi_val = 8; break;
98870073Swpaul			default: return EINVAL;
98970073Swpaul			}
99070073Swpaul			ltv = &p2ltv;
99170073Swpaul			break;
99270073Swpaul		case WI_RID_ENCRYPTION:
99370073Swpaul			p2ltv.wi_type = WI_RID_P2_ENCRYPTION;
99470073Swpaul			p2ltv.wi_len = 2;
99570073Swpaul			if (ltv->wi_val)
99670073Swpaul				p2ltv.wi_val = 0x03;
99770073Swpaul			else
99870073Swpaul				p2ltv.wi_val = 0x90;
99970073Swpaul			ltv = &p2ltv;
100070073Swpaul			break;
100170073Swpaul		case WI_RID_TX_CRYPT_KEY:
100270073Swpaul			p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY;
100370073Swpaul			p2ltv.wi_len = 2;
100470073Swpaul			p2ltv.wi_val = ltv->wi_val;
100570073Swpaul			ltv = &p2ltv;
100670073Swpaul			break;
100770073Swpaul		case WI_RID_DEFLT_CRYPT_KEYS:
100870073Swpaul		    {
100970073Swpaul			int error;
101091548Sbrooks			int keylen;
101170073Swpaul			struct wi_ltv_str	ws;
101274998Swpaul			struct wi_ltv_keys	*wk =
101374998Swpaul			    (struct wi_ltv_keys *)ltv;
101474998Swpaul
101591548Sbrooks			keylen = wk->wi_keys[sc->wi_tx_key].wi_keylen;
101691548Sbrooks
101770073Swpaul			for (i = 0; i < 4; i++) {
101891548Sbrooks				bzero(&ws, sizeof(ws));
101991548Sbrooks				ws.wi_len = (keylen > 5) ? 8 : 4;
102070073Swpaul				ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i;
102174998Swpaul				memcpy(ws.wi_str,
102291548Sbrooks				    &wk->wi_keys[i].wi_keydat, keylen);
102370073Swpaul				error = wi_write_record(sc,
102470073Swpaul				    (struct wi_ltv_gen *)&ws);
102570073Swpaul				if (error)
102670073Swpaul					return error;
102770073Swpaul			}
102870073Swpaul			return 0;
102970073Swpaul		    }
103091695Simp		case WI_RID_AUTH_CNTL:
103191695Simp			p2ltv.wi_type = WI_RID_AUTH_CNTL;
103291695Simp			p2ltv.wi_len = 2;
103391695Simp			if (le16toh(ltv->wi_val) == 1)
103491695Simp				p2ltv.wi_val = htole16(0x01);
103591695Simp			else if (le16toh(ltv->wi_val) == 2)
103691695Simp				p2ltv.wi_val = htole16(0x02);
103791695Simp			ltv = &p2ltv;
103891695Simp			break;
103970073Swpaul		}
104070073Swpaul	}
104170073Swpaul
104247789Swpaul	if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1))
104347789Swpaul		return(EIO);
104446492Swpaul
104546492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len);
104646492Swpaul	CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type);
104746492Swpaul
104846492Swpaul	ptr = &ltv->wi_val;
104946492Swpaul	for (i = 0; i < ltv->wi_len - 1; i++)
105046492Swpaul		CSR_WRITE_2(sc, WI_DATA1, ptr[i]);
105146492Swpaul
105292457Simp	if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type, 0, 0))
105346492Swpaul		return(EIO);
105446492Swpaul
105546492Swpaul	return(0);
105646492Swpaul}
105746492Swpaul
105888546Salfredstatic int
105988546Salfredwi_seek(sc, id, off, chan)
106046492Swpaul	struct wi_softc		*sc;
106146492Swpaul	int			id, off, chan;
106246492Swpaul{
106346492Swpaul	int			i;
106446492Swpaul	int			selreg, offreg;
106575373Salfred	int			status;
106646492Swpaul
106746492Swpaul	switch (chan) {
106846492Swpaul	case WI_BAP0:
106946492Swpaul		selreg = WI_SEL0;
107046492Swpaul		offreg = WI_OFF0;
107146492Swpaul		break;
107246492Swpaul	case WI_BAP1:
107346492Swpaul		selreg = WI_SEL1;
107446492Swpaul		offreg = WI_OFF1;
107546492Swpaul		break;
107646492Swpaul	default:
107753702Swpaul		device_printf(sc->dev, "invalid data path: %x\n", chan);
107846492Swpaul		return(EIO);
107946492Swpaul	}
108046492Swpaul
108146492Swpaul	CSR_WRITE_2(sc, selreg, id);
108246492Swpaul	CSR_WRITE_2(sc, offreg, off);
108346492Swpaul
108446492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
108575373Salfred		status = CSR_READ_2(sc, offreg);
108675373Salfred		if (!(status & (WI_OFF_BUSY|WI_OFF_ERR)))
108746492Swpaul			break;
108890580Sbrooks		DELAY(WI_DELAY);
108946492Swpaul	}
109046492Swpaul
109175373Salfred	if (i == WI_TIMEOUT) {
109275373Salfred		device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n",
109375373Salfred			id, off, status);
109446492Swpaul		return(ETIMEDOUT);
109575373Salfred	}
109646492Swpaul
109746492Swpaul	return(0);
109846492Swpaul}
109946492Swpaul
110088546Salfredstatic int
110188546Salfredwi_read_data(sc, id, off, buf, len)
110246492Swpaul	struct wi_softc		*sc;
110346492Swpaul	int			id, off;
110446492Swpaul	caddr_t			buf;
110546492Swpaul	int			len;
110646492Swpaul{
110746492Swpaul	int			i;
110846492Swpaul	u_int16_t		*ptr;
110946492Swpaul
111046492Swpaul	if (wi_seek(sc, id, off, WI_BAP1))
111146492Swpaul		return(EIO);
111246492Swpaul
111346492Swpaul	ptr = (u_int16_t *)buf;
111446492Swpaul	for (i = 0; i < len / 2; i++)
111546492Swpaul		ptr[i] = CSR_READ_2(sc, WI_DATA1);
111646492Swpaul
111746492Swpaul	return(0);
111846492Swpaul}
111946492Swpaul
112046492Swpaul/*
112146492Swpaul * According to the comments in the HCF Light code, there is a bug in
112246492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where
112346492Swpaul * the chip's internal autoincrement counter gets thrown off during
112446492Swpaul * data writes: the autoincrement is missed, causing one data word to
112546492Swpaul * be overwritten and subsequent words to be written to the wrong memory
112646492Swpaul * locations. The end result is that we could end up transmitting bogus
112746492Swpaul * frames without realizing it. The workaround for this is to write a
112846492Swpaul * couple of extra guard words after the end of the transfer, then
112946492Swpaul * attempt to read then back. If we fail to locate the guard words where
113046492Swpaul * we expect them, we preform the transfer over again.
113146492Swpaul */
113288546Salfredstatic int
113388546Salfredwi_write_data(sc, id, off, buf, len)
113446492Swpaul	struct wi_softc		*sc;
113546492Swpaul	int			id, off;
113646492Swpaul	caddr_t			buf;
113746492Swpaul	int			len;
113846492Swpaul{
113946492Swpaul	int			i;
114046492Swpaul	u_int16_t		*ptr;
114174838Salfred#ifdef WI_HERMES_AUTOINC_WAR
114274838Salfred	int			retries;
114346492Swpaul
114475373Salfred	retries = 512;
114546492Swpaulagain:
114646492Swpaul#endif
114746492Swpaul
114846492Swpaul	if (wi_seek(sc, id, off, WI_BAP0))
114946492Swpaul		return(EIO);
115046492Swpaul
115146492Swpaul	ptr = (u_int16_t *)buf;
115246492Swpaul	for (i = 0; i < (len / 2); i++)
115346492Swpaul		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
115446492Swpaul
115546492Swpaul#ifdef WI_HERMES_AUTOINC_WAR
115646492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x1234);
115746492Swpaul	CSR_WRITE_2(sc, WI_DATA0, 0x5678);
115846492Swpaul
115946492Swpaul	if (wi_seek(sc, id, off + len, WI_BAP0))
116046492Swpaul		return(EIO);
116146492Swpaul
116246492Swpaul	if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
116374998Swpaul	    CSR_READ_2(sc, WI_DATA0) != 0x5678) {
116474838Salfred		if (--retries >= 0)
116574838Salfred			goto again;
116674838Salfred		device_printf(sc->dev, "wi_write_data device timeout\n");
116774838Salfred		return (EIO);
116874838Salfred	}
116946492Swpaul#endif
117046492Swpaul
117146492Swpaul	return(0);
117246492Swpaul}
117346492Swpaul
117446492Swpaul/*
117546492Swpaul * Allocate a region of memory inside the NIC and zero
117646492Swpaul * it out.
117746492Swpaul */
117888546Salfredstatic int
117988546Salfredwi_alloc_nicmem(sc, len, id)
118046492Swpaul	struct wi_softc		*sc;
118146492Swpaul	int			len;
118246492Swpaul	int			*id;
118346492Swpaul{
118446492Swpaul	int			i;
118546492Swpaul
118692457Simp	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
118774998Swpaul		device_printf(sc->dev,
118874998Swpaul		    "failed to allocate %d bytes on NIC\n", len);
118946492Swpaul		return(ENOMEM);
119046492Swpaul	}
119146492Swpaul
119246492Swpaul	for (i = 0; i < WI_TIMEOUT; i++) {
119346492Swpaul		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
119446492Swpaul			break;
119590580Sbrooks		DELAY(WI_DELAY);
119646492Swpaul	}
119746492Swpaul
119875373Salfred	if (i == WI_TIMEOUT) {
119975373Salfred		device_printf(sc->dev, "time out allocating memory on card\n");
120046492Swpaul		return(ETIMEDOUT);
120175373Salfred	}
120246492Swpaul
120346492Swpaul	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
120446492Swpaul	*id = CSR_READ_2(sc, WI_ALLOC_FID);
120546492Swpaul
120675373Salfred	if (wi_seek(sc, *id, 0, WI_BAP0)) {
120775373Salfred		device_printf(sc->dev, "seek failed while allocating memory on card\n");
120847789Swpaul		return(EIO);
120975373Salfred	}
121046492Swpaul
121146492Swpaul	for (i = 0; i < len / 2; i++)
121246492Swpaul		CSR_WRITE_2(sc, WI_DATA0, 0);
121346492Swpaul
121446492Swpaul	return(0);
121546492Swpaul}
121646492Swpaul
121788546Salfredstatic void
121888546Salfredwi_setmulti(sc)
121946492Swpaul	struct wi_softc		*sc;
122046492Swpaul{
122146492Swpaul	struct ifnet		*ifp;
122246492Swpaul	int			i = 0;
122346492Swpaul	struct ifmultiaddr	*ifma;
122446492Swpaul	struct wi_ltv_mcast	mcast;
122546492Swpaul
122646492Swpaul	ifp = &sc->arpcom.ac_if;
122746492Swpaul
122846492Swpaul	bzero((char *)&mcast, sizeof(mcast));
122946492Swpaul
123046492Swpaul	mcast.wi_type = WI_RID_MCAST;
123146492Swpaul	mcast.wi_len = (3 * 16) + 1;
123246492Swpaul
123346492Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
123446492Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
123546492Swpaul		return;
123646492Swpaul	}
123746492Swpaul
123872084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
123946492Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
124046492Swpaul			continue;
124146492Swpaul		if (i < 16) {
124246492Swpaul			bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
124346492Swpaul			    (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN);
124446492Swpaul			i++;
124546492Swpaul		} else {
124646492Swpaul			bzero((char *)&mcast, sizeof(mcast));
124746492Swpaul			break;
124846492Swpaul		}
124946492Swpaul	}
125046492Swpaul
125146492Swpaul	mcast.wi_len = (i * 3) + 1;
125246492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mcast);
125346492Swpaul
125446492Swpaul	return;
125546492Swpaul}
125646492Swpaul
125788546Salfredstatic void
125888546Salfredwi_setdef(sc, wreq)
125946492Swpaul	struct wi_softc		*sc;
126046492Swpaul	struct wi_req		*wreq;
126146492Swpaul{
126246492Swpaul	struct sockaddr_dl	*sdl;
126346492Swpaul	struct ifaddr		*ifa;
126446492Swpaul	struct ifnet		*ifp;
126546492Swpaul
126646492Swpaul	ifp = &sc->arpcom.ac_if;
126746492Swpaul
126846492Swpaul	switch(wreq->wi_type) {
126946492Swpaul	case WI_RID_MAC_NODE:
127083130Sjlemon		ifa = ifaddr_byindex(ifp->if_index);
127146492Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
127246492Swpaul		bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr,
127346492Swpaul		   ETHER_ADDR_LEN);
127446492Swpaul		bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN);
127546492Swpaul		break;
127646492Swpaul	case WI_RID_PORTTYPE:
127791695Simp		sc->wi_ptype = le16toh(wreq->wi_val[0]);
127846492Swpaul		break;
127946492Swpaul	case WI_RID_TX_RATE:
128091695Simp		sc->wi_tx_rate = le16toh(wreq->wi_val[0]);
128146492Swpaul		break;
128246492Swpaul	case WI_RID_MAX_DATALEN:
128391695Simp		sc->wi_max_data_len = le16toh(wreq->wi_val[0]);
128446492Swpaul		break;
128546492Swpaul	case WI_RID_RTS_THRESH:
128691695Simp		sc->wi_rts_thresh = le16toh(wreq->wi_val[0]);
128746492Swpaul		break;
128846492Swpaul	case WI_RID_SYSTEM_SCALE:
128991695Simp		sc->wi_ap_density = le16toh(wreq->wi_val[0]);
129046492Swpaul		break;
129146492Swpaul	case WI_RID_CREATE_IBSS:
129291695Simp		sc->wi_create_ibss = le16toh(wreq->wi_val[0]);
129346492Swpaul		break;
129446563Swpaul	case WI_RID_OWN_CHNL:
129591695Simp		sc->wi_channel = le16toh(wreq->wi_val[0]);
129646563Swpaul		break;
129746492Swpaul	case WI_RID_NODENAME:
129846492Swpaul		bzero(sc->wi_node_name, sizeof(sc->wi_node_name));
129946492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30);
130046492Swpaul		break;
130146492Swpaul	case WI_RID_DESIRED_SSID:
130246492Swpaul		bzero(sc->wi_net_name, sizeof(sc->wi_net_name));
130346492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30);
130446492Swpaul		break;
130546492Swpaul	case WI_RID_OWN_SSID:
130646492Swpaul		bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name));
130746492Swpaul		bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30);
130846492Swpaul		break;
130946611Swpaul	case WI_RID_PM_ENABLED:
131091695Simp		sc->wi_pm_enabled = le16toh(wreq->wi_val[0]);
131146611Swpaul		break;
131291695Simp	case WI_RID_MICROWAVE_OVEN:
131391695Simp		sc->wi_mor_enabled = le16toh(wreq->wi_val[0]);
131491695Simp		break;
131546611Swpaul	case WI_RID_MAX_SLEEP:
131691695Simp		sc->wi_max_sleep = le16toh(wreq->wi_val[0]);
131746611Swpaul		break;
131891695Simp	case WI_RID_AUTH_CNTL:
131991695Simp		sc->wi_authtype = le16toh(wreq->wi_val[0]);
132091695Simp		break;
132191695Simp	case WI_RID_ROAMING_MODE:
132291695Simp		sc->wi_roaming = le16toh(wreq->wi_val[0]);
132391695Simp		break;
132456965Swpaul	case WI_RID_ENCRYPTION:
132591695Simp		sc->wi_use_wep = le16toh(wreq->wi_val[0]);
132656965Swpaul		break;
132756965Swpaul	case WI_RID_TX_CRYPT_KEY:
132891695Simp		sc->wi_tx_key = le16toh(wreq->wi_val[0]);
132956965Swpaul		break;
133056965Swpaul	case WI_RID_DEFLT_CRYPT_KEYS:
133156965Swpaul		bcopy((char *)wreq, (char *)&sc->wi_keys,
133256965Swpaul		    sizeof(struct wi_ltv_keys));
133356965Swpaul		break;
133446492Swpaul	default:
133546492Swpaul		break;
133646492Swpaul	}
133746492Swpaul
133846563Swpaul	/* Reinitialize WaveLAN. */
133946563Swpaul	wi_init(sc);
134046563Swpaul
134146492Swpaul	return;
134246492Swpaul}
134346492Swpaul
134488546Salfredstatic int
134588546Salfredwi_ioctl(ifp, command, data)
134646492Swpaul	struct ifnet		*ifp;
134746492Swpaul	u_long			command;
134846492Swpaul	caddr_t			data;
134946492Swpaul{
135067092Swpaul	int			error = 0;
135177217Sphk	int			len;
135277217Sphk	u_int8_t		tmpkey[14];
135377217Sphk	char			tmpssid[IEEE80211_NWID_LEN];
135446492Swpaul	struct wi_softc		*sc;
135546492Swpaul	struct wi_req		wreq;
135646492Swpaul	struct ifreq		*ifr;
135777217Sphk	struct ieee80211req	*ireq;
135893593Sjhb	struct thread		*td = curthread;
135946492Swpaul
136046492Swpaul	sc = ifp->if_softc;
136167092Swpaul	WI_LOCK(sc);
136246492Swpaul	ifr = (struct ifreq *)data;
136377217Sphk	ireq = (struct ieee80211req *)data;
136446492Swpaul
136561818Sroberto	if (sc->wi_gone) {
136661818Sroberto		error = ENODEV;
136761818Sroberto		goto out;
136861818Sroberto	}
136946492Swpaul
137046492Swpaul	switch(command) {
137146492Swpaul	case SIOCSIFADDR:
137246492Swpaul	case SIOCGIFADDR:
137346492Swpaul	case SIOCSIFMTU:
137446492Swpaul		error = ether_ioctl(ifp, command, data);
137546492Swpaul		break;
137646492Swpaul	case SIOCSIFFLAGS:
137746492Swpaul		if (ifp->if_flags & IFF_UP) {
137846492Swpaul			if (ifp->if_flags & IFF_RUNNING &&
137946492Swpaul			    ifp->if_flags & IFF_PROMISC &&
138046492Swpaul			    !(sc->wi_if_flags & IFF_PROMISC)) {
138146492Swpaul				WI_SETVAL(WI_RID_PROMISC, 1);
138246492Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
138346492Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
138446492Swpaul			    sc->wi_if_flags & IFF_PROMISC) {
138546492Swpaul				WI_SETVAL(WI_RID_PROMISC, 0);
138646492Swpaul			} else
138746492Swpaul				wi_init(sc);
138846492Swpaul		} else {
138946492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
139046492Swpaul				wi_stop(sc);
139146492Swpaul			}
139246492Swpaul		}
139346492Swpaul		sc->wi_if_flags = ifp->if_flags;
139446492Swpaul		error = 0;
139546492Swpaul		break;
139677217Sphk	case SIOCSIFMEDIA:
139777217Sphk	case SIOCGIFMEDIA:
139877217Sphk		error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
139977217Sphk		break;
140046492Swpaul	case SIOCADDMULTI:
140146492Swpaul	case SIOCDELMULTI:
140246492Swpaul		wi_setmulti(sc);
140346492Swpaul		error = 0;
140446492Swpaul		break;
140546492Swpaul	case SIOCGWAVELAN:
140646492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
140746492Swpaul		if (error)
140846492Swpaul			break;
140965581Swpaul		/* Don't show WEP keys to non-root users. */
141093593Sjhb		if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(td))
141165581Swpaul			break;
141246492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
141346492Swpaul			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
141446492Swpaul			    sizeof(sc->wi_stats));
141546492Swpaul			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
141656965Swpaul		} else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) {
141756965Swpaul			bcopy((char *)&sc->wi_keys, (char *)&wreq,
141856965Swpaul			    sizeof(struct wi_ltv_keys));
141953702Swpaul		}
142053702Swpaul#ifdef WICACHE
142153702Swpaul		else if (wreq.wi_type == WI_RID_ZERO_CACHE) {
142253702Swpaul			sc->wi_sigitems = sc->wi_nextitem = 0;
142353702Swpaul		} else if (wreq.wi_type == WI_RID_READ_CACHE) {
142453702Swpaul			char *pt = (char *)&wreq.wi_val;
142553702Swpaul			bcopy((char *)&sc->wi_sigitems,
142653702Swpaul			    (char *)pt, sizeof(int));
142753702Swpaul			pt += (sizeof (int));
142853702Swpaul			wreq.wi_len = sizeof(int) / 2;
142953702Swpaul			bcopy((char *)&sc->wi_sigcache, (char *)pt,
143053702Swpaul			    sizeof(struct wi_sigcache) * sc->wi_sigitems);
143153702Swpaul			wreq.wi_len += ((sizeof(struct wi_sigcache) *
143253702Swpaul			    sc->wi_sigitems) / 2) + 1;
143353702Swpaul		}
143453702Swpaul#endif
143593359Simp		else if (wreq.wi_type == WI_RID_PROCFRAME) {
143693359Simp			wreq.wi_len = 2;
143793359Simp			wreq.wi_val[0] = sc->wi_procframe;
143893359Simp		} else if (wreq.wi_type == WI_RID_PRISM2) {
143993359Simp			wreq.wi_len = 2;
144093359Simp			wreq.wi_val[0] = sc->wi_prism2;
144193359Simp		} else if (wreq.wi_type == WI_RID_SCAN_RES && !sc->wi_prism2) {
144293359Simp			memcpy((char *)wreq.wi_val, (char *)sc->wi_scanbuf,
144393359Simp			    sc->wi_scanbuf_len * 2);
144493359Simp			wreq.wi_len = sc->wi_scanbuf_len;
144593359Simp		} else {
144646492Swpaul			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
144746492Swpaul				error = EINVAL;
144846492Swpaul				break;
144946492Swpaul			}
145046492Swpaul		}
145146492Swpaul		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
145246492Swpaul		break;
145346492Swpaul	case SIOCSWAVELAN:
145493593Sjhb		if ((error = suser(td)))
145561818Sroberto			goto out;
145646492Swpaul		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
145746492Swpaul		if (error)
145846492Swpaul			break;
145946492Swpaul		if (wreq.wi_type == WI_RID_IFACE_STATS) {
146046492Swpaul			error = EINVAL;
146146492Swpaul			break;
146246492Swpaul		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
146346492Swpaul			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
146446492Swpaul			    wreq.wi_len);
146593359Simp		} else if (wreq.wi_type == WI_RID_PROCFRAME) {
146693359Simp			sc->wi_procframe = wreq.wi_val[0];
146793359Simp		/*
146893359Simp		 * if we're getting a scan request from a wavelan card
146993359Simp		 * (non-prism2), send out a cmd_inquire to the card to scan
147093359Simp		 * results for the scan will be received through the info
147193359Simp		 * interrupt handler. otherwise the scan request can be
147293359Simp		 * directly handled by a prism2 card's rid interface.
147393359Simp		 */
147493359Simp		} else if (wreq.wi_type == WI_RID_SCAN_REQ && !sc->wi_prism2) {
147593359Simp			wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
147646492Swpaul		} else {
147746492Swpaul			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
147846492Swpaul			if (!error)
147946492Swpaul				wi_setdef(sc, &wreq);
148046492Swpaul		}
148146492Swpaul		break;
148293359Simp	case SIOCGPRISM2DEBUG:
148393359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
148493359Simp		if (error)
148593359Simp			break;
148693359Simp		if (!(ifp->if_flags & IFF_RUNNING) || !sc->wi_prism2) {
148793359Simp			error = EIO;
148893359Simp			break;
148993359Simp		}
149093359Simp		error = wi_get_debug(sc, &wreq);
149193359Simp		if (error == 0)
149293359Simp			error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
149393359Simp		break;
149493359Simp	case SIOCSPRISM2DEBUG:
149593593Sjhb		if ((error = suser(td)))
149693359Simp			goto out;
149793359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
149893359Simp		if (error)
149993359Simp			break;
150093359Simp		error = wi_set_debug(sc, &wreq);
150193359Simp		break;
150277217Sphk	case SIOCG80211:
150377217Sphk		switch(ireq->i_type) {
150477217Sphk		case IEEE80211_IOC_SSID:
150577217Sphk			if(ireq->i_val == -1) {
150677217Sphk				bzero(tmpssid, IEEE80211_NWID_LEN);
150777217Sphk				error = wi_get_cur_ssid(sc, tmpssid, &len);
150877217Sphk				if (error != 0)
150977217Sphk					break;
151077217Sphk				error = copyout(tmpssid, ireq->i_data,
151177217Sphk					IEEE80211_NWID_LEN);
151277217Sphk				ireq->i_len = len;
151377217Sphk			} else if (ireq->i_val == 0) {
151477217Sphk				error = copyout(sc->wi_net_name,
151577217Sphk				    ireq->i_data,
151677217Sphk				    IEEE80211_NWID_LEN);
151777217Sphk				ireq->i_len = IEEE80211_NWID_LEN;
151877217Sphk			} else
151977217Sphk				error = EINVAL;
152077217Sphk			break;
152177217Sphk		case IEEE80211_IOC_NUMSSIDS:
152277217Sphk			ireq->i_val = 1;
152377217Sphk			break;
152477217Sphk		case IEEE80211_IOC_WEP:
152577217Sphk			if(!sc->wi_has_wep) {
152677217Sphk				ireq->i_val = IEEE80211_WEP_NOSUP;
152777217Sphk			} else {
152877217Sphk				if(sc->wi_use_wep) {
152977217Sphk					ireq->i_val =
153077217Sphk					    IEEE80211_WEP_MIXED;
153177217Sphk				} else {
153277217Sphk					ireq->i_val =
153377217Sphk					    IEEE80211_WEP_OFF;
153477217Sphk				}
153577217Sphk			}
153677217Sphk			break;
153777217Sphk		case IEEE80211_IOC_WEPKEY:
153877217Sphk			if(!sc->wi_has_wep ||
153977217Sphk			    ireq->i_val < 0 || ireq->i_val > 3) {
154077217Sphk				error = EINVAL;
154177217Sphk				break;
154277217Sphk			}
154377217Sphk			len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen;
154493593Sjhb			if (suser(td))
154577217Sphk				bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
154677217Sphk				    tmpkey, len);
154777217Sphk			else
154877217Sphk				bzero(tmpkey, len);
154977217Sphk
155077217Sphk			ireq->i_len = len;
155177217Sphk			error = copyout(tmpkey, ireq->i_data, len);
155277217Sphk
155377217Sphk			break;
155477217Sphk		case IEEE80211_IOC_NUMWEPKEYS:
155577217Sphk			if(!sc->wi_has_wep)
155677217Sphk				error = EINVAL;
155777217Sphk			else
155877217Sphk				ireq->i_val = 4;
155977217Sphk			break;
156077217Sphk		case IEEE80211_IOC_WEPTXKEY:
156177217Sphk			if(!sc->wi_has_wep)
156277217Sphk				error = EINVAL;
156377217Sphk			else
156477217Sphk				ireq->i_val = sc->wi_tx_key;
156577217Sphk			break;
156677217Sphk		case IEEE80211_IOC_AUTHMODE:
156777217Sphk			ireq->i_val = IEEE80211_AUTH_NONE;
156877217Sphk			break;
156977217Sphk		case IEEE80211_IOC_STATIONNAME:
157077217Sphk			error = copyout(sc->wi_node_name,
157177217Sphk			    ireq->i_data, IEEE80211_NWID_LEN);
157277217Sphk			ireq->i_len = IEEE80211_NWID_LEN;
157377217Sphk			break;
157477217Sphk		case IEEE80211_IOC_CHANNEL:
157577217Sphk			wreq.wi_type = WI_RID_CURRENT_CHAN;
157677217Sphk			wreq.wi_len = WI_MAX_DATALEN;
157777217Sphk			if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq))
157877217Sphk				error = EINVAL;
157977217Sphk			else {
158077217Sphk				ireq->i_val = wreq.wi_val[0];
158177217Sphk			}
158277217Sphk			break;
158377217Sphk		case IEEE80211_IOC_POWERSAVE:
158477217Sphk			if(sc->wi_pm_enabled)
158577217Sphk				ireq->i_val = IEEE80211_POWERSAVE_ON;
158677217Sphk			else
158777217Sphk				ireq->i_val = IEEE80211_POWERSAVE_OFF;
158877217Sphk			break;
158977217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
159077217Sphk			ireq->i_val = sc->wi_max_sleep;
159177217Sphk			break;
159277217Sphk		default:
159377217Sphk			error = EINVAL;
159477217Sphk		}
159577217Sphk		break;
159677217Sphk	case SIOCS80211:
159793593Sjhb		if ((error = suser(td)))
159877217Sphk			goto out;
159977217Sphk		switch(ireq->i_type) {
160077217Sphk		case IEEE80211_IOC_SSID:
160177217Sphk			if (ireq->i_val != 0 ||
160277217Sphk			    ireq->i_len > IEEE80211_NWID_LEN) {
160377217Sphk				error = EINVAL;
160477217Sphk				break;
160577217Sphk			}
160677217Sphk			/* We set both of them */
160777217Sphk			bzero(sc->wi_net_name, IEEE80211_NWID_LEN);
160877217Sphk			error = copyin(ireq->i_data,
160977217Sphk			    sc->wi_net_name, ireq->i_len);
161077217Sphk			bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN);
161177217Sphk			break;
161277217Sphk		case IEEE80211_IOC_WEP:
161377217Sphk			/*
161477217Sphk			 * These cards only support one mode so
161577217Sphk			 * we just turn wep on what ever is
161677217Sphk			 * passed in if it's not OFF.
161777217Sphk			 */
161877217Sphk			if (ireq->i_val == IEEE80211_WEP_OFF) {
161977217Sphk				sc->wi_use_wep = 0;
162077217Sphk			} else {
162177217Sphk				sc->wi_use_wep = 1;
162277217Sphk			}
162377217Sphk			break;
162477217Sphk		case IEEE80211_IOC_WEPKEY:
162577217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3 ||
162677217Sphk				ireq->i_len > 13) {
162777217Sphk				error = EINVAL;
162877217Sphk				break;
162977217Sphk			}
163077217Sphk			bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13);
163177217Sphk			error = copyin(ireq->i_data,
163277217Sphk			    sc->wi_keys.wi_keys[ireq->i_val].wi_keydat,
163377217Sphk			    ireq->i_len);
163477217Sphk			if(error)
163577217Sphk				break;
163677217Sphk			sc->wi_keys.wi_keys[ireq->i_val].wi_keylen =
163777217Sphk				    ireq->i_len;
163877217Sphk			break;
163977217Sphk		case IEEE80211_IOC_WEPTXKEY:
164077217Sphk			if (ireq->i_val < 0 || ireq->i_val > 3) {
164177217Sphk				error = EINVAL;
164277217Sphk				break;
164377217Sphk			}
164477217Sphk			sc->wi_tx_key = ireq->i_val;
164577217Sphk			break;
164677217Sphk		case IEEE80211_IOC_AUTHMODE:
164777217Sphk			error = EINVAL;
164877217Sphk			break;
164977217Sphk		case IEEE80211_IOC_STATIONNAME:
165077217Sphk			if (ireq->i_len > 32) {
165177217Sphk				error = EINVAL;
165277217Sphk				break;
165377217Sphk			}
165477217Sphk			bzero(sc->wi_node_name, 32);
165577217Sphk			error = copyin(ireq->i_data,
165677217Sphk			    sc->wi_node_name, ireq->i_len);
165777217Sphk			break;
165877217Sphk		case IEEE80211_IOC_CHANNEL:
165977217Sphk			/*
166077217Sphk			 * The actual range is 1-14, but if you
166177217Sphk			 * set it to 0 you get the default. So
166277217Sphk			 * we let that work too.
166377217Sphk			 */
166477217Sphk			if (ireq->i_val < 0 || ireq->i_val > 14) {
166577217Sphk				error = EINVAL;
166677217Sphk				break;
166777217Sphk			}
166877217Sphk			sc->wi_channel = ireq->i_val;
166977217Sphk			break;
167077217Sphk		case IEEE80211_IOC_POWERSAVE:
167177217Sphk			switch (ireq->i_val) {
167277217Sphk			case IEEE80211_POWERSAVE_OFF:
167377217Sphk				sc->wi_pm_enabled = 0;
167477217Sphk				break;
167577217Sphk			case IEEE80211_POWERSAVE_ON:
167677217Sphk				sc->wi_pm_enabled = 1;
167777217Sphk				break;
167877217Sphk			default:
167977217Sphk				error = EINVAL;
168077217Sphk				break;
168177217Sphk			}
168277217Sphk			break;
168377217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
168477217Sphk			if (ireq->i_val < 0) {
168577217Sphk				error = EINVAL;
168677217Sphk				break;
168777217Sphk			}
168877217Sphk			sc->wi_max_sleep = ireq->i_val;
168977217Sphk			break;
169077217Sphk		default:
169177217Sphk			error = EINVAL;
169277217Sphk			break;
169377217Sphk		}
169477217Sphk
169577217Sphk		/* Reinitialize WaveLAN. */
169677217Sphk		wi_init(sc);
169777217Sphk
169877217Sphk		break;
169946492Swpaul	default:
170046492Swpaul		error = EINVAL;
170146492Swpaul		break;
170246492Swpaul	}
170361818Srobertoout:
170467092Swpaul	WI_UNLOCK(sc);
170546492Swpaul
170646492Swpaul	return(error);
170746492Swpaul}
170846492Swpaul
170988546Salfredstatic void
171088546Salfredwi_init(xsc)
171146492Swpaul	void			*xsc;
171246492Swpaul{
171346492Swpaul	struct wi_softc		*sc = xsc;
171446492Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
171546492Swpaul	struct wi_ltv_macaddr	mac;
171646492Swpaul	int			id = 0;
171746492Swpaul
171867092Swpaul	WI_LOCK(sc);
171967092Swpaul
172067092Swpaul	if (sc->wi_gone) {
172167092Swpaul		WI_UNLOCK(sc);
172246492Swpaul		return;
172367092Swpaul	}
172446492Swpaul
172546492Swpaul	if (ifp->if_flags & IFF_RUNNING)
172646492Swpaul		wi_stop(sc);
172746492Swpaul
172846492Swpaul	wi_reset(sc);
172946492Swpaul
173046492Swpaul	/* Program max data length. */
173146492Swpaul	WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len);
173246492Swpaul
173347401Swpaul	/* Enable/disable IBSS creation. */
173446492Swpaul	WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss);
173546492Swpaul
173646492Swpaul	/* Set the port type. */
173746492Swpaul	WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype);
173846492Swpaul
173946492Swpaul	/* Program the RTS/CTS threshold. */
174046492Swpaul	WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh);
174146492Swpaul
174246492Swpaul	/* Program the TX rate */
174346492Swpaul	WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate);
174446492Swpaul
174546492Swpaul	/* Access point density */
174646492Swpaul	WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density);
174746492Swpaul
174846611Swpaul	/* Power Management Enabled */
174946611Swpaul	WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled);
175046611Swpaul
175146611Swpaul	/* Power Managment Max Sleep */
175246611Swpaul	WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep);
175346611Swpaul
175491695Simp	/* Roaming type */
175591695Simp	WI_SETVAL(WI_RID_ROAMING_MODE, sc->wi_roaming);
175691695Simp
175746492Swpaul	/* Specify the IBSS name */
175846492Swpaul	WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name);
175946492Swpaul
176046492Swpaul	/* Specify the network name */
176146492Swpaul	WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name);
176246492Swpaul
176346563Swpaul	/* Specify the frequency to use */
176446563Swpaul	WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel);
176546563Swpaul
176646492Swpaul	/* Program the nodename. */
176746492Swpaul	WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name);
176846492Swpaul
176946492Swpaul	/* Set our MAC address. */
177046492Swpaul	mac.wi_len = 4;
177146492Swpaul	mac.wi_type = WI_RID_MAC_NODE;
177246492Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
177346492Swpaul	   (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN);
177446492Swpaul	wi_write_record(sc, (struct wi_ltv_gen *)&mac);
177546492Swpaul
177656965Swpaul	/* Configure WEP. */
177756965Swpaul	if (sc->wi_has_wep) {
177856965Swpaul		WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep);
177956965Swpaul		WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key);
178056965Swpaul		sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
178156965Swpaul		sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
178256965Swpaul		wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys);
178391695Simp		if (sc->wi_prism2 && sc->wi_use_wep) {
178491695Simp			/*
178591695Simp			 * ONLY HWB3163 EVAL-CARD Firmware version
178691695Simp			 * less than 0.8 variant3
178791695Simp			 *
178891695Simp			 *   If promiscuous mode disable, Prism2 chip
178991695Simp			 *  does not work with WEP .
179091695Simp			 * It is under investigation for details.
179191695Simp			 * (ichiro@netbsd.org)
179291695Simp			 */
179392457Simp			if (sc->wi_firmware_ver < 83 ) {
179491695Simp				/* firm ver < 0.8 variant 3 */
179591695Simp				WI_SETVAL(WI_RID_PROMISC, 1);
179691695Simp			}
179791695Simp			WI_SETVAL(WI_RID_AUTH_CNTL, sc->wi_authtype);
179891695Simp		}
179956965Swpaul	}
180056965Swpaul
180146492Swpaul	/* Initialize promisc mode. */
180246492Swpaul	if (ifp->if_flags & IFF_PROMISC) {
180346492Swpaul		WI_SETVAL(WI_RID_PROMISC, 1);
180446492Swpaul	} else {
180546492Swpaul		WI_SETVAL(WI_RID_PROMISC, 0);
180646492Swpaul	}
180746492Swpaul
180846492Swpaul	/* Set multicast filter. */
180946492Swpaul	wi_setmulti(sc);
181046492Swpaul
181146492Swpaul	/* Enable desired port */
181292457Simp	wi_cmd(sc, WI_CMD_ENABLE | sc->wi_portnum, 0, 0, 0);
181346492Swpaul
181475373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
181553702Swpaul		device_printf(sc->dev, "tx buffer allocation failed\n");
181646492Swpaul	sc->wi_tx_data_id = id;
181746492Swpaul
181875373Salfred	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
181953702Swpaul		device_printf(sc->dev, "mgmt. buffer allocation failed\n");
182046492Swpaul	sc->wi_tx_mgmt_id = id;
182146492Swpaul
182246492Swpaul	/* enable interrupts */
182346492Swpaul	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
182446492Swpaul
182546492Swpaul	ifp->if_flags |= IFF_RUNNING;
182646492Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
182746492Swpaul
182846492Swpaul	sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60);
182967092Swpaul	WI_UNLOCK(sc);
183046492Swpaul
183146492Swpaul	return;
183246492Swpaul}
183346492Swpaul
183488546Salfredstatic void
183588546Salfredwi_start(ifp)
183646492Swpaul	struct ifnet		*ifp;
183746492Swpaul{
183846492Swpaul	struct wi_softc		*sc;
183946492Swpaul	struct mbuf		*m0;
184046492Swpaul	struct wi_frame		tx_frame;
184146492Swpaul	struct ether_header	*eh;
184246492Swpaul	int			id;
184346492Swpaul
184446492Swpaul	sc = ifp->if_softc;
184567092Swpaul	WI_LOCK(sc);
184646492Swpaul
184767092Swpaul	if (sc->wi_gone) {
184867092Swpaul		WI_UNLOCK(sc);
184946492Swpaul		return;
185067092Swpaul	}
185146492Swpaul
185267092Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
185367092Swpaul		WI_UNLOCK(sc);
185446492Swpaul		return;
185567092Swpaul	}
185646492Swpaul
185746492Swpaul	IF_DEQUEUE(&ifp->if_snd, m0);
185867092Swpaul	if (m0 == NULL) {
185967092Swpaul		WI_UNLOCK(sc);
186046492Swpaul		return;
186167092Swpaul	}
186246492Swpaul
186346492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
186446492Swpaul	id = sc->wi_tx_data_id;
186546492Swpaul	eh = mtod(m0, struct ether_header *);
186646492Swpaul
186746492Swpaul	/*
186847401Swpaul	 * Use RFC1042 encoding for IP and ARP datagrams,
186946492Swpaul	 * 802.3 for anything else.
187046492Swpaul	 */
187175275Salfred	if (ntohs(eh->ether_type) > ETHER_MAX_LEN) {
187246492Swpaul		bcopy((char *)&eh->ether_dhost,
187346492Swpaul		    (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN);
187446492Swpaul		bcopy((char *)&eh->ether_shost,
187546492Swpaul		    (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN);
187646492Swpaul		bcopy((char *)&eh->ether_dhost,
187746492Swpaul		    (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN);
187846492Swpaul		bcopy((char *)&eh->ether_shost,
187946492Swpaul		    (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN);
188046492Swpaul
188146492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN;
188246492Swpaul		tx_frame.wi_frame_ctl = WI_FTYPE_DATA;
188346492Swpaul		tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0);
188446492Swpaul		tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1);
188546492Swpaul		tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
188646492Swpaul		tx_frame.wi_type = eh->ether_type;
188746492Swpaul
188846492Swpaul		m_copydata(m0, sizeof(struct ether_header),
188946492Swpaul		    m0->m_pkthdr.len - sizeof(struct ether_header),
189046492Swpaul		    (caddr_t)&sc->wi_txbuf);
189146492Swpaul
189246492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
189346492Swpaul		    sizeof(struct wi_frame));
189446492Swpaul		wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf,
189546492Swpaul		    (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2);
189646492Swpaul	} else {
189746492Swpaul		tx_frame.wi_dat_len = m0->m_pkthdr.len;
189846492Swpaul
189955831Swpaul		eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN);
190046492Swpaul		m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf);
190146492Swpaul
190246492Swpaul		wi_write_data(sc, id, 0, (caddr_t)&tx_frame,
190346492Swpaul		    sizeof(struct wi_frame));
190446492Swpaul		wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf,
190546492Swpaul		    m0->m_pkthdr.len + 2);
190646492Swpaul	}
190746492Swpaul
190846492Swpaul	/*
190946492Swpaul	 * If there's a BPF listner, bounce a copy of
191093359Simp 	 * this frame to him. Also, don't send this to the bpf sniffer
191193359Simp 	 * if we're in procframe or monitor sniffing mode.
191246492Swpaul	 */
191393359Simp 	if (!(sc->wi_procframe || sc->wi_debug.wi_monitor) && ifp->if_bpf)
191446492Swpaul		bpf_mtap(ifp, m0);
191546492Swpaul
191646492Swpaul	m_freem(m0);
191746492Swpaul
191892457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0))
191953702Swpaul		device_printf(sc->dev, "xmit failed\n");
192046492Swpaul
192146492Swpaul	ifp->if_flags |= IFF_OACTIVE;
192246492Swpaul
192346492Swpaul	/*
192446492Swpaul	 * Set a timeout in case the chip goes out to lunch.
192546492Swpaul	 */
192646492Swpaul	ifp->if_timer = 5;
192746492Swpaul
192867092Swpaul	WI_UNLOCK(sc);
192946492Swpaul	return;
193046492Swpaul}
193146492Swpaul
193288546Salfredstatic int
193388546Salfredwi_mgmt_xmit(sc, data, len)
193446492Swpaul	struct wi_softc		*sc;
193546492Swpaul	caddr_t			data;
193646492Swpaul	int			len;
193746492Swpaul{
193846492Swpaul	struct wi_frame		tx_frame;
193946492Swpaul	int			id;
194046492Swpaul	struct wi_80211_hdr	*hdr;
194146492Swpaul	caddr_t			dptr;
194246492Swpaul
194346492Swpaul	if (sc->wi_gone)
194446492Swpaul		return(ENODEV);
194546492Swpaul
194646492Swpaul	hdr = (struct wi_80211_hdr *)data;
194746492Swpaul	dptr = data + sizeof(struct wi_80211_hdr);
194846492Swpaul
194946492Swpaul	bzero((char *)&tx_frame, sizeof(tx_frame));
195046492Swpaul	id = sc->wi_tx_mgmt_id;
195146492Swpaul
195246492Swpaul	bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl,
195346492Swpaul	   sizeof(struct wi_80211_hdr));
195446492Swpaul
195546492Swpaul	tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN;
195646492Swpaul	tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN);
195746492Swpaul
195846492Swpaul	wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame));
195946492Swpaul	wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr,
196046492Swpaul	    (len - sizeof(struct wi_80211_hdr)) + 2);
196146492Swpaul
196292457Simp	if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) {
196353702Swpaul		device_printf(sc->dev, "xmit failed\n");
196446492Swpaul		return(EIO);
196546492Swpaul	}
196646492Swpaul
196746492Swpaul	return(0);
196846492Swpaul}
196946492Swpaul
197088546Salfredstatic void
197188546Salfredwi_stop(sc)
197246492Swpaul	struct wi_softc		*sc;
197346492Swpaul{
197446492Swpaul	struct ifnet		*ifp;
197546492Swpaul
197667092Swpaul	WI_LOCK(sc);
197767092Swpaul
197867092Swpaul	if (sc->wi_gone) {
197967092Swpaul		WI_UNLOCK(sc);
198046492Swpaul		return;
198167092Swpaul	}
198246492Swpaul
198346492Swpaul	ifp = &sc->arpcom.ac_if;
198446492Swpaul
198570173Sjhb	/*
198670173Sjhb	 * If the card is gone and the memory port isn't mapped, we will
198770173Sjhb	 * (hopefully) get 0xffff back from the status read, which is not
198870173Sjhb	 * a valid status value.
198970173Sjhb	 */
199070173Sjhb	if (CSR_READ_2(sc, WI_STATUS) != 0xffff) {
199170173Sjhb		CSR_WRITE_2(sc, WI_INT_EN, 0);
199292457Simp		wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0, 0, 0);
199370173Sjhb	}
199446492Swpaul
199546492Swpaul	untimeout(wi_inquire, sc, sc->wi_stat_ch);
199646492Swpaul
199746492Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
199846492Swpaul
199967092Swpaul	WI_UNLOCK(sc);
200046492Swpaul	return;
200146492Swpaul}
200246492Swpaul
200388546Salfredstatic void
200488546Salfredwi_watchdog(ifp)
200546492Swpaul	struct ifnet		*ifp;
200646492Swpaul{
200746492Swpaul	struct wi_softc		*sc;
200846492Swpaul
200946492Swpaul	sc = ifp->if_softc;
201046492Swpaul
201175199Salfred	device_printf(sc->dev, "watchdog timeout\n");
201246492Swpaul
201346492Swpaul	wi_init(sc);
201446492Swpaul
201546492Swpaul	ifp->if_oerrors++;
201646492Swpaul
201746492Swpaul	return;
201846492Swpaul}
201946492Swpaul
202093611Simpint
202190580Sbrookswi_alloc(dev, rid)
202253702Swpaul	device_t		dev;
202390580Sbrooks	int			rid;
202446492Swpaul{
202553702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
202653702Swpaul
202790580Sbrooks	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
202890580Sbrooks		sc->iobase_rid = rid;
202990580Sbrooks		sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
203090580Sbrooks		    &sc->iobase_rid, 0, ~0, (1 << 6),
203190580Sbrooks		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
203290580Sbrooks		if (!sc->iobase) {
203390580Sbrooks			device_printf(dev, "No I/O space?!\n");
203490580Sbrooks			return (ENXIO);
203590580Sbrooks		}
203690580Sbrooks
203790580Sbrooks		sc->wi_io_addr = rman_get_start(sc->iobase);
203890580Sbrooks		sc->wi_btag = rman_get_bustag(sc->iobase);
203990580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
204090580Sbrooks	} else {
204190580Sbrooks		sc->mem_rid = rid;
204290580Sbrooks		sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY,
204390580Sbrooks		    &sc->mem_rid, 0, ~0, 1, RF_ACTIVE);
204490580Sbrooks
204590580Sbrooks		if (!sc->mem) {
204690580Sbrooks			device_printf(dev, "No Mem space on prism2.5?\n");
204790580Sbrooks			return (ENXIO);
204890580Sbrooks		}
204990580Sbrooks
205090580Sbrooks		sc->wi_btag = rman_get_bustag(sc->mem);
205190580Sbrooks		sc->wi_bhandle = rman_get_bushandle(sc->mem);
205253702Swpaul	}
205353702Swpaul
205490580Sbrooks
205574906Salfred	sc->irq_rid = 0;
205674906Salfred	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
205790580Sbrooks	    0, ~0, 1, RF_ACTIVE |
205890580Sbrooks	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
205990580Sbrooks
206053702Swpaul	if (!sc->irq) {
206175219Salfred		wi_free(dev);
206253702Swpaul		device_printf(dev, "No irq?!\n");
206353702Swpaul		return (ENXIO);
206453702Swpaul	}
206553702Swpaul
206653702Swpaul	sc->dev = dev;
206753702Swpaul	sc->wi_unit = device_get_unit(dev);
206853702Swpaul
206953702Swpaul	return (0);
207053702Swpaul}
207153702Swpaul
207293611Simpvoid
207388546Salfredwi_free(dev)
207453702Swpaul	device_t		dev;
207553702Swpaul{
207653702Swpaul	struct wi_softc		*sc = device_get_softc(dev);
207753702Swpaul
207875219Salfred	if (sc->iobase != NULL) {
207975219Salfred		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
208075219Salfred		sc->iobase = NULL;
208175219Salfred	}
208275219Salfred	if (sc->irq != NULL) {
208375219Salfred		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
208475219Salfred		sc->irq = NULL;
208575219Salfred	}
208675219Salfred	if (sc->mem != NULL) {
208774906Salfred		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
208875219Salfred		sc->mem = NULL;
208975219Salfred	}
209053702Swpaul
209153702Swpaul	return;
209253702Swpaul}
209353702Swpaul
209493611Simpvoid
209588546Salfredwi_shutdown(dev)
209653702Swpaul	device_t		dev;
209753702Swpaul{
209846492Swpaul	struct wi_softc		*sc;
209946492Swpaul
210053702Swpaul	sc = device_get_softc(dev);
210146492Swpaul	wi_stop(sc);
210246492Swpaul
210346492Swpaul	return;
210446492Swpaul}
210553702Swpaul
210653702Swpaul#ifdef WICACHE
210753702Swpaul/* wavelan signal strength cache code.
210853702Swpaul * store signal/noise/quality on per MAC src basis in
210953702Swpaul * a small fixed cache.  The cache wraps if > MAX slots
211053702Swpaul * used.  The cache may be zeroed out to start over.
211153702Swpaul * Two simple filters exist to reduce computation:
211253702Swpaul * 1. ip only (literally 0x800) which may be used
211353702Swpaul * to ignore some packets.  It defaults to ip only.
211453702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons.
211553702Swpaul * 2. multicast/broadcast only.  This may be used to
211653702Swpaul * ignore unicast packets and only cache signal strength
211753702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP
211853702Swpaul * beacons and not unicast traffic.
211953702Swpaul *
212053702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal,
212153702Swpaul *	quality, noise)
212253702Swpaul *
212353702Swpaul * No apologies for storing IP src here.  It's easy and saves much
212453702Swpaul * trouble elsewhere.  The cache is assumed to be INET dependent,
212553702Swpaul * although it need not be.
212653702Swpaul */
212753702Swpaul
212853702Swpaul#ifdef documentation
212953702Swpaul
213053702Swpaulint wi_sigitems;                                /* number of cached entries */
213153702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE];  /*  array of cache entries */
213253702Swpaulint wi_nextitem;                                /*  index/# of entries */
213353702Swpaul
213453702Swpaul
213553702Swpaul#endif
213653702Swpaul
213753702Swpaul/* control variables for cache filtering.  Basic idea is
213853702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons
213953702Swpaul * which are broadcast or multicast).  Still you might
214053702Swpaul * want to measure signal strength with unicast ping packets
214153702Swpaul * on a pt. to pt. ant. setup.
214253702Swpaul */
214353702Swpaul/* set true if you want to limit cache items to broadcast/mcast
214453702Swpaul * only packets (not unicast).  Useful for mobile-ip beacons which
214553702Swpaul * are broadcast/multicast at network layer.  Default is all packets
214653702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup.
214753702Swpaul */
214853702Swpaulstatic int wi_cache_mcastonly = 0;
214953702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW,
215053702Swpaul	&wi_cache_mcastonly, 0, "");
215153702Swpaul
215253702Swpaul/* set true if you want to limit cache items to IP packets only
215353702Swpaul*/
215453702Swpaulstatic int wi_cache_iponly = 1;
215553702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW,
215653702Swpaul	&wi_cache_iponly, 0, "");
215753702Swpaul
215853702Swpaul/*
215953702Swpaul * Original comments:
216053702Swpaul * -----------------
216153702Swpaul * wi_cache_store, per rx packet store signal
216253702Swpaul * strength in MAC (src) indexed cache.
216353702Swpaul *
216453702Swpaul * follows linux driver in how signal strength is computed.
216553702Swpaul * In ad hoc mode, we use the rx_quality field.
216653702Swpaul * signal and noise are trimmed to fit in the range from 47..138.
216753702Swpaul * rx_quality field MSB is signal strength.
216853702Swpaul * rx_quality field LSB is noise.
216953702Swpaul * "quality" is (signal - noise) as is log value.
217053702Swpaul * note: quality CAN be negative.
217153702Swpaul *
217253702Swpaul * In BSS mode, we use the RID for communication quality.
217353702Swpaul * TBD:  BSS mode is currently untested.
217453702Swpaul *
217553702Swpaul * Bill's comments:
217653702Swpaul * ---------------
217753702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc"
217853702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive:
217953702Swpaul * there's a bunch of PIO operations that have to be done to read a record
218053702Swpaul * from the NIC, and reading the comms quality RID each time a packet is
218153702Swpaul * received can really hurt performance. We don't have to do this anyway:
218253702Swpaul * the comms quality field only reflects the values in the rx_quality field
218353702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode,
218453702Swpaul * but the values it contains are updated based on the rx_quality from
218553702Swpaul * frames received from the access point.
218653702Swpaul *
218753702Swpaul * Also, according to Lucent, the signal strength and noise level values
218853702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code
218953702Swpaul * to do that instead of the scaling it did originally.
219053702Swpaul */
219188546Salfredstatic void
219288546Salfredwi_cache_store(struct wi_softc *sc, struct ether_header *eh,
219353702Swpaul                     struct mbuf *m, unsigned short rx_quality)
219453702Swpaul{
219553702Swpaul	struct ip *ip = 0;
219653702Swpaul	int i;
219753702Swpaul	static int cache_slot = 0; 	/* use this cache entry */
219853702Swpaul	static int wrapindex = 0;       /* next "free" cache entry */
219953702Swpaul	int sig, noise;
220053702Swpaul	int sawip=0;
220153702Swpaul
220253702Swpaul	/* filters:
220353702Swpaul	 * 1. ip only
220453702Swpaul	 * 2. configurable filter to throw out unicast packets,
220553702Swpaul	 * keep multicast only.
220653702Swpaul	 */
220753702Swpaul
220875276Salfred	if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) {
220953702Swpaul		sawip = 1;
221053702Swpaul	}
221153702Swpaul
221253702Swpaul	/* filter for ip packets only
221353702Swpaul	*/
221453702Swpaul	if (wi_cache_iponly && !sawip) {
221553702Swpaul		return;
221653702Swpaul	}
221753702Swpaul
221853702Swpaul	/* filter for broadcast/multicast only
221953702Swpaul	 */
222053702Swpaul	if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
222153702Swpaul		return;
222253702Swpaul	}
222353702Swpaul
222453702Swpaul#ifdef SIGDEBUG
222553702Swpaul	printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit,
222653702Swpaul	    rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff);
222753702Swpaul#endif
222853702Swpaul
222953702Swpaul	/* find the ip header.  we want to store the ip_src
223053702Swpaul	 * address.
223153702Swpaul	 */
223253702Swpaul	if (sawip) {
223353702Swpaul		ip = mtod(m, struct ip *);
223453702Swpaul	}
223553702Swpaul
223653702Swpaul	/* do a linear search for a matching MAC address
223753702Swpaul	 * in the cache table
223853702Swpaul	 * . MAC address is 6 bytes,
223953702Swpaul	 * . var w_nextitem holds total number of entries already cached
224053702Swpaul	 */
224153702Swpaul	for(i = 0; i < sc->wi_nextitem; i++) {
224253702Swpaul		if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc,  6 )) {
224353702Swpaul			/* Match!,
224453702Swpaul			 * so we already have this entry,
224553702Swpaul			 * update the data
224653702Swpaul			 */
224753702Swpaul			break;
224853702Swpaul		}
224953702Swpaul	}
225053702Swpaul
225153702Swpaul	/* did we find a matching mac address?
225253702Swpaul	 * if yes, then overwrite a previously existing cache entry
225353702Swpaul	 */
225453702Swpaul	if (i < sc->wi_nextitem )   {
225553702Swpaul		cache_slot = i;
225653702Swpaul	}
225753702Swpaul	/* else, have a new address entry,so
225853702Swpaul	 * add this new entry,
225953702Swpaul	 * if table full, then we need to replace LRU entry
226053702Swpaul	 */
226153702Swpaul	else    {
226253702Swpaul
226353702Swpaul		/* check for space in cache table
226453702Swpaul		 * note: wi_nextitem also holds number of entries
226553702Swpaul		 * added in the cache table
226653702Swpaul		 */
226753702Swpaul		if ( sc->wi_nextitem < MAXWICACHE ) {
226853702Swpaul			cache_slot = sc->wi_nextitem;
226953702Swpaul			sc->wi_nextitem++;
227053702Swpaul			sc->wi_sigitems = sc->wi_nextitem;
227153702Swpaul		}
227253702Swpaul        	/* no space found, so simply wrap with wrap index
227353702Swpaul		 * and "zap" the next entry
227453702Swpaul		 */
227553702Swpaul		else {
227653702Swpaul			if (wrapindex == MAXWICACHE) {
227753702Swpaul				wrapindex = 0;
227853702Swpaul			}
227953702Swpaul			cache_slot = wrapindex++;
228053702Swpaul		}
228153702Swpaul	}
228253702Swpaul
228353702Swpaul	/* invariant: cache_slot now points at some slot
228453702Swpaul	 * in cache.
228553702Swpaul	 */
228653702Swpaul	if (cache_slot < 0 || cache_slot >= MAXWICACHE) {
228753702Swpaul		log(LOG_ERR, "wi_cache_store, bad index: %d of "
228853702Swpaul		    "[0..%d], gross cache error\n",
228953702Swpaul		    cache_slot, MAXWICACHE);
229053702Swpaul		return;
229153702Swpaul	}
229253702Swpaul
229353702Swpaul	/*  store items in cache
229453702Swpaul	 *  .ip source address
229553702Swpaul	 *  .mac src
229653702Swpaul	 *  .signal, etc.
229753702Swpaul	 */
229853702Swpaul	if (sawip) {
229953702Swpaul		sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr;
230053702Swpaul	}
230153702Swpaul	bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc,  6);
230253702Swpaul
230353702Swpaul	sig = (rx_quality >> 8) & 0xFF;
230453702Swpaul	noise = rx_quality & 0xFF;
230553702Swpaul	sc->wi_sigcache[cache_slot].signal = sig - 149;
230653702Swpaul	sc->wi_sigcache[cache_slot].noise = noise - 149;
230753702Swpaul	sc->wi_sigcache[cache_slot].quality = sig - noise;
230853702Swpaul
230953702Swpaul	return;
231053702Swpaul}
231153702Swpaul#endif
231277217Sphk
231388546Salfredstatic int
231488546Salfredwi_get_cur_ssid(sc, ssid, len)
231577217Sphk	struct wi_softc		*sc;
231677217Sphk	char			*ssid;
231777217Sphk	int			*len;
231877217Sphk{
231977217Sphk	int			error = 0;
232077217Sphk	struct wi_req		wreq;
232177217Sphk
232277217Sphk	wreq.wi_len = WI_MAX_DATALEN;
232377217Sphk	switch (sc->wi_ptype) {
232477217Sphk	case WI_PORTTYPE_ADHOC:
232577217Sphk		wreq.wi_type = WI_RID_CURRENT_SSID;
232677217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
232777217Sphk		if (error != 0)
232877217Sphk			break;
232977217Sphk		if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
233077217Sphk			error = EINVAL;
233177217Sphk			break;
233277217Sphk		}
233377217Sphk		*len = wreq.wi_val[0];
233477217Sphk		bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
233577217Sphk		break;
233677217Sphk	case WI_PORTTYPE_BSS:
233777217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
233877217Sphk		error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
233977217Sphk		if (error != 0)
234077217Sphk			break;
234177217Sphk		if (wreq.wi_val[0] != 0) /* associated */ {
234277217Sphk			wreq.wi_type = WI_RID_CURRENT_SSID;
234377217Sphk			wreq.wi_len = WI_MAX_DATALEN;
234477217Sphk			error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq);
234577217Sphk			if (error != 0)
234677217Sphk				break;
234777217Sphk			if (wreq.wi_val[0] > IEEE80211_NWID_LEN) {
234877217Sphk				error = EINVAL;
234977217Sphk				break;
235077217Sphk			}
235177217Sphk			*len = wreq.wi_val[0];
235277217Sphk			bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN);
235377217Sphk		} else {
235477217Sphk			*len = IEEE80211_NWID_LEN;
235577217Sphk			bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN);
235677217Sphk		}
235777217Sphk		break;
235877217Sphk	default:
235977217Sphk		error = EINVAL;
236077217Sphk		break;
236177217Sphk	}
236277217Sphk
236377217Sphk	return error;
236477217Sphk}
236577217Sphk
236688546Salfredstatic int
236788546Salfredwi_media_change(ifp)
236877217Sphk	struct ifnet		*ifp;
236977217Sphk{
237077217Sphk	struct wi_softc		*sc = ifp->if_softc;
237177217Sphk	int			otype = sc->wi_ptype;
237277217Sphk	int			orate = sc->wi_tx_rate;
237377217Sphk
237477217Sphk	if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0)
237577217Sphk		sc->wi_ptype = WI_PORTTYPE_ADHOC;
237677217Sphk	else
237777217Sphk		sc->wi_ptype = WI_PORTTYPE_BSS;
237877217Sphk
237977217Sphk	switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) {
238077217Sphk	case IFM_IEEE80211_DS1:
238177217Sphk		sc->wi_tx_rate = 1;
238277217Sphk		break;
238377217Sphk	case IFM_IEEE80211_DS2:
238477217Sphk		sc->wi_tx_rate = 2;
238577217Sphk		break;
238677217Sphk	case IFM_IEEE80211_DS5:
238777217Sphk		sc->wi_tx_rate = 5;
238877217Sphk		break;
238977217Sphk	case IFM_IEEE80211_DS11:
239077217Sphk		sc->wi_tx_rate = 11;
239177217Sphk		break;
239277217Sphk	case IFM_AUTO:
239377217Sphk		sc->wi_tx_rate = 3;
239477217Sphk		break;
239577217Sphk	}
239677217Sphk
239777217Sphk	if (otype != sc->wi_ptype ||
239877217Sphk	    orate != sc->wi_tx_rate)
239977217Sphk		wi_init(sc);
240077217Sphk
240177217Sphk	return(0);
240277217Sphk}
240377217Sphk
240488546Salfredstatic void
240588546Salfredwi_media_status(ifp, imr)
240677217Sphk	struct ifnet		*ifp;
240777217Sphk	struct ifmediareq	*imr;
240877217Sphk{
240977217Sphk	struct wi_req		wreq;
241077217Sphk	struct wi_softc		*sc = ifp->if_softc;
241177217Sphk
241277217Sphk	if (sc->wi_tx_rate == 3) {
241377217Sphk		imr->ifm_active = IFM_IEEE80211|IFM_AUTO;
241477217Sphk		if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
241577217Sphk			imr->ifm_active |= IFM_IEEE80211_ADHOC;
241677217Sphk		wreq.wi_type = WI_RID_CUR_TX_RATE;
241777217Sphk		wreq.wi_len = WI_MAX_DATALEN;
241877217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) {
241977217Sphk			switch(wreq.wi_val[0]) {
242077217Sphk			case 1:
242177217Sphk				imr->ifm_active |= IFM_IEEE80211_DS1;
242277217Sphk				break;
242377217Sphk			case 2:
242477217Sphk				imr->ifm_active |= IFM_IEEE80211_DS2;
242577217Sphk				break;
242677217Sphk			case 6:
242777217Sphk				imr->ifm_active |= IFM_IEEE80211_DS5;
242877217Sphk				break;
242977217Sphk			case 11:
243077217Sphk				imr->ifm_active |= IFM_IEEE80211_DS11;
243177217Sphk				break;
243277217Sphk				}
243377217Sphk		}
243477217Sphk	} else {
243577217Sphk		imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media;
243677217Sphk	}
243777217Sphk
243877217Sphk	imr->ifm_status = IFM_AVALID;
243977217Sphk	if (sc->wi_ptype == WI_PORTTYPE_ADHOC)
244077217Sphk		/*
244177217Sphk		 * XXX: It would be nice if we could give some actually
244277217Sphk		 * useful status like whether we joined another IBSS or
244377217Sphk		 * created one ourselves.
244477217Sphk		 */
244577217Sphk		imr->ifm_status |= IFM_ACTIVE;
244677217Sphk	else {
244777217Sphk		wreq.wi_type = WI_RID_COMMQUAL;
244877217Sphk		wreq.wi_len = WI_MAX_DATALEN;
244977217Sphk		if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 &&
245077217Sphk		    wreq.wi_val[0] != 0)
245177217Sphk			imr->ifm_status |= IFM_ACTIVE;
245277217Sphk	}
245377217Sphk}
245493359Simp
245593359Simpstatic int
245693359Simpwi_get_debug(sc, wreq)
245793359Simp	struct wi_softc		*sc;
245893359Simp	struct wi_req		*wreq;
245993359Simp{
246093359Simp	int			error = 0;
246193359Simp
246293359Simp	wreq->wi_len = 1;
246393359Simp
246493359Simp	switch (wreq->wi_type) {
246593359Simp	case WI_DEBUG_SLEEP:
246693359Simp		wreq->wi_len++;
246793359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sleep;
246893359Simp		break;
246993359Simp	case WI_DEBUG_DELAYSUPP:
247093359Simp		wreq->wi_len++;
247193359Simp		wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
247293359Simp		break;
247393359Simp	case WI_DEBUG_TXSUPP:
247493359Simp		wreq->wi_len++;
247593359Simp		wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
247693359Simp		break;
247793359Simp	case WI_DEBUG_MONITOR:
247893359Simp		wreq->wi_len++;
247993359Simp		wreq->wi_val[0] = sc->wi_debug.wi_monitor;
248093359Simp		break;
248193359Simp	case WI_DEBUG_LEDTEST:
248293359Simp		wreq->wi_len += 3;
248393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
248493359Simp		wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
248593359Simp		wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
248693359Simp		break;
248793359Simp	case WI_DEBUG_CONTTX:
248893359Simp		wreq->wi_len += 2;
248993359Simp		wreq->wi_val[0] = sc->wi_debug.wi_conttx;
249093359Simp		wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
249193359Simp		break;
249293359Simp	case WI_DEBUG_CONTRX:
249393359Simp		wreq->wi_len++;
249493359Simp		wreq->wi_val[0] = sc->wi_debug.wi_contrx;
249593359Simp		break;
249693359Simp	case WI_DEBUG_SIGSTATE:
249793359Simp		wreq->wi_len += 2;
249893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
249993359Simp		wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
250093359Simp		break;
250193359Simp	case WI_DEBUG_CONFBITS:
250293359Simp		wreq->wi_len += 2;
250393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_confbits;
250493359Simp		wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
250593359Simp		break;
250693359Simp	default:
250793359Simp		error = EIO;
250893359Simp		break;
250993359Simp	}
251093359Simp
251193359Simp	return (error);
251293359Simp}
251393359Simp
251493359Simpstatic int
251593359Simpwi_set_debug(sc, wreq)
251693359Simp	struct wi_softc		*sc;
251793359Simp	struct wi_req		*wreq;
251893359Simp{
251993359Simp	int			error = 0;
252093359Simp	u_int16_t		cmd, param0 = 0, param1 = 0;
252193359Simp
252293359Simp	switch (wreq->wi_type) {
252393359Simp	case WI_DEBUG_RESET:
252493359Simp	case WI_DEBUG_INIT:
252593359Simp	case WI_DEBUG_CALENABLE:
252693359Simp		break;
252793359Simp	case WI_DEBUG_SLEEP:
252893359Simp		sc->wi_debug.wi_sleep = 1;
252993359Simp		break;
253093359Simp	case WI_DEBUG_WAKE:
253193359Simp		sc->wi_debug.wi_sleep = 0;
253293359Simp		break;
253393359Simp	case WI_DEBUG_CHAN:
253493359Simp		param0 = wreq->wi_val[0];
253593359Simp		break;
253693359Simp	case WI_DEBUG_DELAYSUPP:
253793359Simp		sc->wi_debug.wi_delaysupp = 1;
253893359Simp		break;
253993359Simp	case WI_DEBUG_TXSUPP:
254093359Simp		sc->wi_debug.wi_txsupp = 1;
254193359Simp		break;
254293359Simp	case WI_DEBUG_MONITOR:
254393359Simp		sc->wi_debug.wi_monitor = 1;
254493359Simp		break;
254593359Simp	case WI_DEBUG_LEDTEST:
254693359Simp		param0 = wreq->wi_val[0];
254793359Simp		param1 = wreq->wi_val[1];
254893359Simp		sc->wi_debug.wi_ledtest = 1;
254993359Simp		sc->wi_debug.wi_ledtest_param0 = param0;
255093359Simp		sc->wi_debug.wi_ledtest_param1 = param1;
255193359Simp		break;
255293359Simp	case WI_DEBUG_CONTTX:
255393359Simp		param0 = wreq->wi_val[0];
255493359Simp		sc->wi_debug.wi_conttx = 1;
255593359Simp		sc->wi_debug.wi_conttx_param0 = param0;
255693359Simp		break;
255793359Simp	case WI_DEBUG_STOPTEST:
255893359Simp		sc->wi_debug.wi_delaysupp = 0;
255993359Simp		sc->wi_debug.wi_txsupp = 0;
256093359Simp		sc->wi_debug.wi_monitor = 0;
256193359Simp		sc->wi_debug.wi_ledtest = 0;
256293359Simp		sc->wi_debug.wi_ledtest_param0 = 0;
256393359Simp		sc->wi_debug.wi_ledtest_param1 = 0;
256493359Simp		sc->wi_debug.wi_conttx = 0;
256593359Simp		sc->wi_debug.wi_conttx_param0 = 0;
256693359Simp		sc->wi_debug.wi_contrx = 0;
256793359Simp		sc->wi_debug.wi_sigstate = 0;
256893359Simp		sc->wi_debug.wi_sigstate_param0 = 0;
256993359Simp		break;
257093359Simp	case WI_DEBUG_CONTRX:
257193359Simp		sc->wi_debug.wi_contrx = 1;
257293359Simp		break;
257393359Simp	case WI_DEBUG_SIGSTATE:
257493359Simp		param0 = wreq->wi_val[0];
257593359Simp		sc->wi_debug.wi_sigstate = 1;
257693359Simp		sc->wi_debug.wi_sigstate_param0 = param0;
257793359Simp		break;
257893359Simp	case WI_DEBUG_CONFBITS:
257993359Simp		param0 = wreq->wi_val[0];
258093359Simp		param1 = wreq->wi_val[1];
258193359Simp		sc->wi_debug.wi_confbits = param0;
258293359Simp		sc->wi_debug.wi_confbits_param0 = param1;
258393359Simp		break;
258493359Simp	default:
258593359Simp		error = EIO;
258693359Simp		break;
258793359Simp	}
258893359Simp
258993359Simp	if (error)
259093359Simp		return (error);
259193359Simp
259293359Simp	cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
259393359Simp	error = wi_cmd(sc, cmd, param0, param1, 0);
259493359Simp
259593359Simp	return (error);
259693359Simp}
2597