if_wi.c revision 109592
1109323Ssam/*	$NetBSD: wi.c,v 1.109 2003/01/09 08:52:19 dyoung Exp $	*/
2109323Ssam
346492Swpaul/*
446492Swpaul * Copyright (c) 1997, 1998, 1999
546492Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
646492Swpaul *
746492Swpaul * Redistribution and use in source and binary forms, with or without
846492Swpaul * modification, are permitted provided that the following conditions
946492Swpaul * are met:
1046492Swpaul * 1. Redistributions of source code must retain the above copyright
1146492Swpaul *    notice, this list of conditions and the following disclaimer.
1246492Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1346492Swpaul *    notice, this list of conditions and the following disclaimer in the
1446492Swpaul *    documentation and/or other materials provided with the distribution.
1546492Swpaul * 3. All advertising materials mentioning features or use of this software
1646492Swpaul *    must display the following acknowledgement:
1746492Swpaul *	This product includes software developed by Bill Paul.
1846492Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1946492Swpaul *    may be used to endorse or promote products derived from this software
2046492Swpaul *    without specific prior written permission.
2146492Swpaul *
2246492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2346492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2446492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2546492Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2646492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2746492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2846492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2946492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3046492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3146492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3246492Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3346492Swpaul */
3446492Swpaul
3546492Swpaul/*
36109323Ssam * Lucent WaveLAN/IEEE 802.11 PCMCIA driver.
3746492Swpaul *
38109323Ssam * Original FreeBSD driver written by Bill Paul <wpaul@ctr.columbia.edu>
3946492Swpaul * Electrical Engineering Department
4046492Swpaul * Columbia University, New York City
4146492Swpaul */
4246492Swpaul
4346492Swpaul/*
4447401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN
4546492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed
4646492Swpaul * entirely via a firmware-driven controller called the Hermes.
4746492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual
4846492Swpaul * without an NDA (if at all). What they do release is an API library
4946492Swpaul * called the HCF (Hardware Control Functions) which is supposed to
5046492Swpaul * do the device-specific operations of a device driver for you. The
5146492Swpaul * publically available version of the HCF library (the 'HCF Light') is
5247401Swpaul * a) extremely gross, b) lacks certain features, particularly support
5346492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License.
5446492Swpaul *
5546492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it
5646492Swpaul * programs the Hermes controller directly, using information gleaned
5746492Swpaul * from the HCF Light code and corresponding documentation.
5846492Swpaul *
5995534Simp * This driver supports the ISA, PCMCIA and PCI versions of the Lucent
6095534Simp * WaveLan cards (based on the Hermes chipset), as well as the newer
6195534Simp * Prism 2 chipsets with firmware from Intersil and Symbol.
6246492Swpaul */
6346492Swpaul
64109323Ssam#define WI_HERMES_AUTOINC_WAR	/* Work around data write autoinc bug. */
65109323Ssam#define WI_HERMES_STATS_WAR	/* Work around stats counter bug. */
66109323Ssam
67109323Ssam#define	NBPFILTER	1
68109323Ssam
6946492Swpaul#include <sys/param.h>
7046492Swpaul#include <sys/systm.h>
7195706Simp#if __FreeBSD_version >= 500033
7295533Smike#include <sys/endian.h>
7395706Simp#endif
7446492Swpaul#include <sys/sockio.h>
7546492Swpaul#include <sys/mbuf.h>
7683366Sjulian#include <sys/proc.h>
7746492Swpaul#include <sys/kernel.h>
7846492Swpaul#include <sys/socket.h>
7953702Swpaul#include <sys/module.h>
8053702Swpaul#include <sys/bus.h>
8194486Simp#include <sys/random.h>
8253702Swpaul#include <sys/syslog.h>
8353702Swpaul#include <sys/sysctl.h>
8446492Swpaul
8553702Swpaul#include <machine/bus.h>
8653702Swpaul#include <machine/resource.h>
8795534Simp#include <machine/clock.h>
8853702Swpaul#include <sys/rman.h>
8953702Swpaul
9046492Swpaul#include <net/if.h>
9146492Swpaul#include <net/if_arp.h>
9246492Swpaul#include <net/ethernet.h>
9346492Swpaul#include <net/if_dl.h>
9446492Swpaul#include <net/if_media.h>
9546492Swpaul#include <net/if_types.h>
9677217Sphk#include <net/if_ieee80211.h>
9746492Swpaul
9846492Swpaul#include <netinet/in.h>
9946492Swpaul#include <netinet/in_systm.h>
10046492Swpaul#include <netinet/in_var.h>
10146492Swpaul#include <netinet/ip.h>
10246492Swpaul#include <netinet/if_ether.h>
10346492Swpaul
10446492Swpaul#include <net/bpf.h>
10546492Swpaul
10670808Speter#include <dev/wi/if_wavelan_ieee.h>
10793611Simp#include <dev/wi/if_wivar.h>
10870808Speter#include <dev/wi/if_wireg.h>
10946492Swpaul
110109323Ssam#define IF_POLL(ifq, m)		((m) = (ifq)->ifq_head)
111109323Ssam#define	IFQ_POLL(ifq, m)	IF_POLL((ifq), (m))
112109323Ssam#define IFQ_DEQUEUE(ifq, m)	IF_DEQUEUE((ifq), (m))
113109323Ssam
11446492Swpaul#if !defined(lint)
11546492Swpaulstatic const char rcsid[] =
11650477Speter  "$FreeBSD: head/sys/dev/wi/if_wi.c 109592 2003-01-20 20:55:37Z sam $";
11746492Swpaul#endif
11846492Swpaul
11991693Simpstatic void wi_start(struct ifnet *);
120109323Ssamstatic int  wi_reset(struct wi_softc *);
12191693Simpstatic void wi_watchdog(struct ifnet *);
122109323Ssamstatic int  wi_ioctl(struct ifnet *, u_long, caddr_t);
123109323Ssamstatic int  wi_media_change(struct ifnet *);
124109323Ssamstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
12546492Swpaul
126109323Ssamstatic void wi_rx_intr(struct wi_softc *);
127109323Ssamstatic void wi_tx_intr(struct wi_softc *);
128109323Ssamstatic void wi_tx_ex_intr(struct wi_softc *);
129109323Ssamstatic void wi_info_intr(struct wi_softc *);
13046492Swpaul
131109323Ssamstatic int  wi_get_cfg(struct ifnet *, u_long, caddr_t);
132109323Ssamstatic int  wi_set_cfg(struct ifnet *, u_long, caddr_t);
133109323Ssamstatic int  wi_write_txrate(struct wi_softc *);
134109323Ssamstatic int  wi_write_wep(struct wi_softc *);
135109323Ssamstatic int  wi_write_multi(struct wi_softc *);
136109323Ssamstatic int  wi_alloc_fid(struct wi_softc *, int, int *);
137109323Ssamstatic void wi_read_nicid(struct wi_softc *);
138109323Ssamstatic int  wi_write_ssid(struct wi_softc *, int, u_int8_t *, int);
13953702Swpaul
140109323Ssamstatic int  wi_cmd(struct wi_softc *, int, int, int, int);
141109323Ssamstatic int  wi_seek_bap(struct wi_softc *, int, int);
142109323Ssamstatic int  wi_read_bap(struct wi_softc *, int, int, void *, int);
143109323Ssamstatic int  wi_write_bap(struct wi_softc *, int, int, void *, int);
144109323Ssamstatic int  wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int);
145109323Ssamstatic int  wi_read_rid(struct wi_softc *, int, void *, int *);
146109323Ssamstatic int  wi_write_rid(struct wi_softc *, int, void *, int);
14777217Sphk
148109323Ssamstatic int  wi_newstate(void *, enum ieee80211_state);
149109323Ssam
150109323Ssamstatic int  wi_scan_ap(struct wi_softc *);
151109323Ssamstatic void wi_scan_result(struct wi_softc *, int, int);
152109323Ssam
153109323Ssamstatic void wi_dump_pkt(struct wi_frame *, struct ieee80211_node *, int rssi);
154109323Ssam
15593359Simpstatic int wi_get_debug(struct wi_softc *, struct wi_req *);
15693359Simpstatic int wi_set_debug(struct wi_softc *, struct wi_req *);
15793359Simp
158105076Simp#if __FreeBSD_version >= 500000
159101903Simp/* support to download firmware for symbol CF card */
160101903Simpstatic int wi_symbol_write_firm(struct wi_softc *, const void *, int,
161101903Simp		const void *, int);
162101903Simpstatic int wi_symbol_set_hcr(struct wi_softc *, int);
163105076Simp#endif
164101903Simp
165109323Ssamstatic __inline int
166109323Ssamwi_write_val(struct wi_softc *sc, int rid, u_int16_t val)
167109323Ssam{
16853702Swpaul
169109323Ssam	val = htole16(val);
170109323Ssam	return wi_write_rid(sc, rid, &val, sizeof(val));
171109323Ssam}
172109323Ssam
173109592SsamSYSCTL_NODE(_hw, OID_AUTO, wi, CTLFLAG_RD, 0, "Wireless driver parameters");
174109592Ssam
175109323Ssamstatic	struct timeval lasttxerror;	/* time of last tx error msg */
176109323Ssamstatic	int curtxeps;			/* current tx error msgs/sec */
177109545Ssamstatic	int wi_txerate = 0;		/* tx error rate: max msgs/sec */
178109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate,
179109592Ssam	    0, "max tx error msgs/sec; 0 disables msgs");
180109323Ssam
181109323Ssam#define	WI_DEBUG
182109323Ssam#ifdef WI_DEBUG
183109323Ssamstatic	int wi_debug = 0;
184109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug,
185109592Ssam	    0, "control debugging printfs");
186109323Ssam
187109323Ssam#define	DPRINTF(X)	if (wi_debug) printf X
188109323Ssam#define	DPRINTF2(X)	if (wi_debug > 1) printf X
189109323Ssam#define	IFF_DUMPPKTS(_ifp) \
190109323Ssam	(((_ifp)->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
191109323Ssam#else
192109323Ssam#define	DPRINTF(X)
193109323Ssam#define	DPRINTF2(X)
194109323Ssam#define	IFF_DUMPPKTS(_ifp)	0
195109323Ssam#endif
196109323Ssam
197109323Ssam#define WI_INTRS	(WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO)
198109323Ssam
19993825Simpstruct wi_card_ident wi_card_ident[] = {
20093825Simp	/* CARD_ID			CARD_NAME		FIRM_TYPE */
20193825Simp	{ WI_NIC_LUCENT_ID,		WI_NIC_LUCENT_STR,	WI_LUCENT },
20293825Simp	{ WI_NIC_SONY_ID,		WI_NIC_SONY_STR,	WI_LUCENT },
20393825Simp	{ WI_NIC_LUCENT_EMB_ID,		WI_NIC_LUCENT_EMB_STR,	WI_LUCENT },
20493825Simp	{ WI_NIC_EVB2_ID,		WI_NIC_EVB2_STR,	WI_INTERSIL },
20593825Simp	{ WI_NIC_HWB3763_ID,		WI_NIC_HWB3763_STR,	WI_INTERSIL },
20693825Simp	{ WI_NIC_HWB3163_ID,		WI_NIC_HWB3163_STR,	WI_INTERSIL },
20793825Simp	{ WI_NIC_HWB3163B_ID,		WI_NIC_HWB3163B_STR,	WI_INTERSIL },
20893825Simp	{ WI_NIC_EVB3_ID,		WI_NIC_EVB3_STR,	WI_INTERSIL },
20993825Simp	{ WI_NIC_HWB1153_ID,		WI_NIC_HWB1153_STR,	WI_INTERSIL },
21093825Simp	{ WI_NIC_P2_SST_ID,		WI_NIC_P2_SST_STR,	WI_INTERSIL },
21193825Simp	{ WI_NIC_EVB2_SST_ID,		WI_NIC_EVB2_SST_STR,	WI_INTERSIL },
21293825Simp	{ WI_NIC_3842_EVA_ID,		WI_NIC_3842_EVA_STR,	WI_INTERSIL },
21393825Simp	{ WI_NIC_3842_PCMCIA_AMD_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
21493825Simp	{ WI_NIC_3842_PCMCIA_SST_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
215101355Simp	{ WI_NIC_3842_PCMCIA_ATL_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
216101355Simp	{ WI_NIC_3842_PCMCIA_ATS_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
21793825Simp	{ WI_NIC_3842_MINI_AMD_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
21893825Simp	{ WI_NIC_3842_MINI_SST_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
219101355Simp	{ WI_NIC_3842_MINI_ATL_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
220101355Simp	{ WI_NIC_3842_MINI_ATS_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
22193825Simp	{ WI_NIC_3842_PCI_AMD_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
22293825Simp	{ WI_NIC_3842_PCI_SST_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
223101355Simp	{ WI_NIC_3842_PCI_ATS_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
224101355Simp	{ WI_NIC_3842_PCI_ATL_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
22593825Simp	{ WI_NIC_P3_PCMCIA_AMD_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
22693825Simp	{ WI_NIC_P3_PCMCIA_SST_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
227101355Simp	{ WI_NIC_P3_PCMCIA_ATL_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
228101355Simp	{ WI_NIC_P3_PCMCIA_ATS_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
22993825Simp	{ WI_NIC_P3_MINI_AMD_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
23093825Simp	{ WI_NIC_P3_MINI_SST_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
231101355Simp	{ WI_NIC_P3_MINI_ATL_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
232101355Simp	{ WI_NIC_P3_MINI_ATS_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
23393825Simp	{ 0,	NULL,	0 },
23493825Simp};
23593825Simp
236109323Ssamdevclass_t wi_devclass;
23746492Swpaul
23893611Simpint
239109323Ssamwi_attach(device_t dev)
24074906Salfred{
241109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
242109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
243109323Ssam	struct ifnet *ifp = &ic->ic_if;
244109323Ssam	int i, nrate, mword, buflen;
245109323Ssam	u_int8_t r;
246109323Ssam	u_int16_t val;
247109323Ssam	u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE];
248109323Ssam	static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
249109323Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
250109323Ssam	};
251109323Ssam	int error;
25274906Salfred
253109323Ssam	/*
254109323Ssam	 * NB: no locking is needed here; don't put it here
255109323Ssam	 *     unless you can prove it!
256109323Ssam	 */
25753702Swpaul	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
25874998Swpaul	    wi_intr, sc, &sc->wi_intrhand);
25953702Swpaul
26053702Swpaul	if (error) {
26153702Swpaul		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
26253702Swpaul		wi_free(dev);
26353702Swpaul		return (error);
26453702Swpaul	}
26553702Swpaul
26695534Simp#if __FreeBSD_version >= 500000
267109323Ssam	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
26893818Sjhb	    MTX_DEF | MTX_RECURSE);
26995534Simp#endif
27067092Swpaul
27146492Swpaul	/* Reset the NIC. */
272109323Ssam	if (wi_reset(sc) != 0) {
273109323Ssam		WI_UNLOCK(sc);
274109323Ssam		return ENXIO;		/* XXX */
275109323Ssam	}
27646492Swpaul
27776438Swpaul	/*
27876438Swpaul	 * Read the station address.
27976438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
28076438Swpaul	 * an error when trying to read it the first time, which causes
28176438Swpaul	 * the probe to fail.
28276438Swpaul	 */
283109323Ssam	buflen = IEEE80211_ADDR_LEN;
284109323Ssam	error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen);
285109323Ssam	if (error != 0) {
286109323Ssam		buflen = IEEE80211_ADDR_LEN;
287109323Ssam		error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen);
288109323Ssam	}
289109323Ssam	if (error || IEEE80211_ADDR_EQ(ic->ic_myaddr, empty_macaddr)) {
290109323Ssam		if (error != 0)
291109323Ssam			device_printf(dev, "mac read failed %d\n", error);
292109323Ssam		else
293109323Ssam			device_printf(dev, "mac read failed (all zeros)\n");
29475149Simp		wi_free(dev);
29575149Simp		return (error);
29675149Simp	}
297109323Ssam	device_printf(dev, "802.11 address: %6D\n", ic->ic_myaddr, ":");
29846492Swpaul
299109323Ssam	/* Read NIC identification */
300109323Ssam	wi_read_nicid(sc);
30146492Swpaul
30246492Swpaul	ifp->if_softc = sc;
303109323Ssam	ifp->if_unit = sc->sc_unit;
30446492Swpaul	ifp->if_name = "wi";
30546492Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30646492Swpaul	ifp->if_ioctl = wi_ioctl;
30746492Swpaul	ifp->if_start = wi_start;
30846492Swpaul	ifp->if_watchdog = wi_watchdog;
30946492Swpaul	ifp->if_init = wi_init;
31046492Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
31146492Swpaul
312109323Ssam	ic->ic_phytype = IEEE80211_T_DS;
313109323Ssam	ic->ic_opmode = IEEE80211_M_STA;
314109323Ssam	ic->ic_flags = IEEE80211_F_HASPMGT | IEEE80211_F_HASAHDEMO;
315109323Ssam	ic->ic_state = IEEE80211_S_INIT;
316109323Ssam	ic->ic_newstate = wi_newstate;
31746492Swpaul
318109323Ssam	/* Find available channels */
319109323Ssam	buflen = sizeof(val);
320109323Ssam	if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0)
321109323Ssam		val = htole16(0x1fff);	/* assume 1-11 */
322109323Ssam	for (i = 0; i < 16; i++) {
323109323Ssam		if (isset((u_int8_t*)&val, i))
324109323Ssam			setbit(ic->ic_chan_avail, i + 1);
325109323Ssam	}
326109323Ssam	KASSERT(ic->ic_chan_avail != 0,
327109323Ssam		("wi_attach: no available channels listed!"));
32846492Swpaul
32946563Swpaul	/*
33046563Swpaul	 * Read the default channel from the NIC. This may vary
33146563Swpaul	 * depending on the country where the NIC was purchased, so
33246563Swpaul	 * we can't hard-code a default and expect it to work for
33346563Swpaul	 * everyone.
33446563Swpaul	 */
335109323Ssam	buflen = sizeof(val);
336109323Ssam	if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0)
337109323Ssam		ic->ic_ibss_chan = le16toh(val);
338109323Ssam	else {
339109323Ssam		/* use lowest available channel */
340109323Ssam		for (i = 0; i < 16 && !isset(ic->ic_chan_avail, i); i++)
341109323Ssam			;
342109323Ssam		ic->ic_ibss_chan = i;
343109323Ssam	}
34446563Swpaul
34556965Swpaul	/*
34698440Simp	 * Set flags based on firmware version.
34798440Simp	 */
34898440Simp	switch (sc->sc_firmware_type) {
34998440Simp	case WI_LUCENT:
350109323Ssam		sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
351109323Ssam#ifdef WI_HERMES_AUTOINC_WAR
352109323Ssam		/* XXX: not confirmed, but never seen for recent firmware */
353109323Ssam		if (sc->sc_sta_firmware_ver <  40000) {
354109323Ssam			sc->sc_flags |= WI_FLAGS_BUG_AUTOINC;
355109323Ssam		}
356109323Ssam#endif
35798440Simp		if (sc->sc_sta_firmware_ver >= 60000)
358109323Ssam			sc->sc_flags |= WI_FLAGS_HAS_MOR;
359109323Ssam		if (sc->sc_sta_firmware_ver >= 60006)
360109323Ssam			ic->ic_flags |= IEEE80211_F_HASIBSS;
361109323Ssam		sc->sc_ibss_port = htole16(1);
36298440Simp		break;
363109323Ssam
36498440Simp	case WI_INTERSIL:
365109323Ssam		sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR;
366109323Ssam		sc->sc_flags |= WI_FLAGS_HAS_ROAMING;
367109323Ssam		sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
368109323Ssam		if (sc->sc_sta_firmware_ver > 10101)
369109323Ssam			sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST;
370109323Ssam		if (sc->sc_sta_firmware_ver >= 800)
371109323Ssam			ic->ic_flags |= IEEE80211_F_HASIBSS;
372109396Simp		/*
373109396Simp		 * version 0.8.3 and newer are the only ones that are known
374109396Simp		 * to currently work.  Earlier versions can be made to work,
375109396Simp		 * at least according to the Linux driver.
376109396Simp		 */
377100734Simp		if (sc->sc_sta_firmware_ver >= 803)
378109323Ssam			ic->ic_flags |= IEEE80211_F_HASHOSTAP;
379109323Ssam		sc->sc_ibss_port = htole16(0);
38098440Simp		break;
381109323Ssam
38298440Simp	case WI_SYMBOL:
383109323Ssam		sc->sc_flags |= WI_FLAGS_HAS_DIVERSITY;
38498440Simp		if (sc->sc_sta_firmware_ver >= 25000)
385109323Ssam			ic->ic_flags |= IEEE80211_F_HASIBSS;
386109323Ssam		sc->sc_ibss_port = htole16(4);
38798440Simp		break;
38898440Simp	}
38998440Simp
39098440Simp	/*
39156965Swpaul	 * Find out if we support WEP on this card.
39256965Swpaul	 */
393109323Ssam	buflen = sizeof(val);
394109323Ssam	if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 &&
395109323Ssam	    val != htole16(0))
396109323Ssam		ic->ic_flags |= IEEE80211_F_HASWEP;
39756965Swpaul
398109323Ssam	/* Find supported rates. */
399109323Ssam	buflen = sizeof(ratebuf);
400109323Ssam	if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) {
401109323Ssam		nrate = le16toh(*(u_int16_t *)ratebuf);
402109323Ssam		if (nrate > IEEE80211_RATE_SIZE)
403109323Ssam			nrate = IEEE80211_RATE_SIZE;
404109323Ssam		memcpy(ic->ic_sup_rates, ratebuf + 2, nrate);
405109323Ssam	} else {
406109323Ssam		/* XXX fallback on error? */
407109323Ssam		nrate = 0;
40898440Simp	}
40977217Sphk
410109323Ssam	buflen = sizeof(val);
411109323Ssam	if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) &&
412109323Ssam	    wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) {
413109323Ssam		sc->sc_dbm_adjust = le16toh(val);
414109323Ssam	} else
415109323Ssam		sc->sc_dbm_adjust = 100;	/* default */
41646492Swpaul
417109323Ssam	sc->sc_max_datalen = 2304;
418109323Ssam	sc->sc_rts_thresh = 2347;
419109323Ssam	sc->sc_frag_thresh = 2346;
420109323Ssam	sc->sc_system_scale = 1;
421109323Ssam	sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN;
422109323Ssam	sc->sc_roaming_mode = 1;
42346492Swpaul
424109323Ssam	sc->sc_portnum = WI_DEFAULT_PORT;
425109323Ssam	sc->sc_authtype = WI_DEFAULT_AUTHTYPE;
42687383Simp
427109323Ssam	bzero(sc->sc_nodename, sizeof(sc->sc_nodename));
428109323Ssam	sc->sc_nodelen = sizeof(WI_DEFAULT_NODENAME) - 1;
429109323Ssam	bcopy(WI_DEFAULT_NODENAME, sc->sc_nodename, sc->sc_nodelen);
43087383Simp
431109323Ssam	bzero(sc->sc_net_name, sizeof(sc->sc_net_name));
432109323Ssam	bcopy(WI_DEFAULT_NETNAME, sc->sc_net_name,
433109323Ssam	    sizeof(WI_DEFAULT_NETNAME) - 1);
43493733Simp
435109323Ssam	ifmedia_init(&sc->sc_media, 0, wi_media_change, wi_media_status);
436109323Ssam	if_printf(ifp, "supported rates: ");
437109323Ssam#define	ADD(s, o)	ifmedia_add(&sc->sc_media, \
438109323Ssam	IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL)
439109323Ssam	ADD(IFM_AUTO, 0);
440109323Ssam	if (ic->ic_flags & IEEE80211_F_HASHOSTAP)
441109323Ssam		ADD(IFM_AUTO, IFM_IEEE80211_HOSTAP);
442109323Ssam	if (ic->ic_flags & IEEE80211_F_HASIBSS)
443109323Ssam		ADD(IFM_AUTO, IFM_IEEE80211_ADHOC);
444109323Ssam	ADD(IFM_AUTO, IFM_IEEE80211_ADHOC | IFM_FLAG0);
445109323Ssam	for (i = 0; i < nrate; i++) {
446109323Ssam		r = ic->ic_sup_rates[i];
447109323Ssam		mword = ieee80211_rate2media(r, IEEE80211_T_DS);
448109323Ssam		if (mword == 0)
449109323Ssam			continue;
450109323Ssam		printf("%s%d%sMbps", (i != 0 ? " " : ""),
451109323Ssam		    (r & IEEE80211_RATE_VAL) / 2, ((r & 0x1) != 0 ? ".5" : ""));
452109323Ssam		ADD(mword, 0);
453109323Ssam		if (ic->ic_flags & IEEE80211_F_HASHOSTAP)
454109323Ssam			ADD(mword, IFM_IEEE80211_HOSTAP);
455109323Ssam		if (ic->ic_flags & IEEE80211_F_HASIBSS)
456109323Ssam			ADD(mword, IFM_IEEE80211_ADHOC);
457109323Ssam		ADD(mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
45893733Simp	}
45993733Simp	printf("\n");
460109323Ssam	ifmedia_set(&sc->sc_media, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0));
461109323Ssam#undef ADD
46293733Simp
46393733Simp	/*
464109323Ssam	 * Call MI attach routine.
46593733Simp	 */
466109323Ssam	ieee80211_ifattach(ifp);
467109323Ssam
468109323Ssam	return (0);
46987383Simp}
47087383Simp
471109323Ssamint
472109323Ssamwi_detach(device_t dev)
47346492Swpaul{
474109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
475109323Ssam	struct ifnet *ifp = &sc->sc_ic.ic_if;
476109323Ssam	WI_LOCK_DECL();
47746492Swpaul
478109323Ssam	WI_LOCK(sc);
47946492Swpaul
480109323Ssam	/* check if device was removed */
481109323Ssam	sc->wi_gone = !bus_child_present(dev);
48246492Swpaul
483109323Ssam	wi_stop(ifp, 0);
48446492Swpaul
485109323Ssam	/* Delete all remaining media. */
486109323Ssam	ifmedia_removeall(&sc->sc_media);
48746492Swpaul
488109323Ssam	ieee80211_ifdetach(ifp);
489109323Ssam	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
490109323Ssam	wi_free(dev);
49146492Swpaul
492109323Ssam	WI_UNLOCK(sc);
493109323Ssam#if __FreeBSD_version >= 500000
494109323Ssam	mtx_destroy(&sc->sc_mtx);
49593359Simp#endif
496109323Ssam	return (0);
497109323Ssam}
49893359Simp
499109323Ssam#ifdef __NetBSD__
500109323Ssamint
501109323Ssamwi_activate(struct device *self, enum devact act)
502109323Ssam{
503109323Ssam	struct wi_softc *sc = (struct wi_softc *)self;
504109323Ssam	int rv = 0, s;
50593359Simp
506109323Ssam	s = splnet();
507109323Ssam	switch (act) {
508109323Ssam	case DVACT_ACTIVATE:
509109323Ssam		rv = EOPNOTSUPP;
510109323Ssam		break;
51193359Simp
512109323Ssam	case DVACT_DEACTIVATE:
513109323Ssam		if_deactivate(&sc->sc_ic.ic_if);
514109323Ssam		break;
515109323Ssam	}
516109323Ssam	splx(s);
517109323Ssam	return rv;
518109323Ssam}
51993359Simp
520109323Ssamvoid
521109323Ssamwi_power(struct wi_softc *sc, int why)
522109323Ssam{
523109323Ssam	struct ifnet *ifp = &sc->sc_ic.ic_if;
524109323Ssam	int s;
52593359Simp
526109323Ssam	s = splnet();
527109323Ssam	switch (why) {
528109323Ssam	case PWR_SUSPEND:
529109323Ssam	case PWR_STANDBY:
530109323Ssam		wi_stop(ifp, 1);
531109323Ssam		break;
532109323Ssam	case PWR_RESUME:
533109323Ssam		if (ifp->if_flags & IFF_UP) {
534109323Ssam			wi_init(ifp);
535109323Ssam			(void)wi_intr(sc);
53694405Simp		}
537109323Ssam		break;
538109323Ssam	case PWR_SOFTSUSPEND:
539109323Ssam	case PWR_SOFTSTANDBY:
540109323Ssam	case PWR_SOFTRESUME:
541109323Ssam		break;
54293359Simp	}
543109323Ssam	splx(s);
54446492Swpaul}
545109323Ssam#endif /* __NetBSD__ */
54646492Swpaul
547109323Ssamvoid
548109323Ssamwi_shutdown(device_t dev)
54946492Swpaul{
550109323Ssam	struct wi_softc *sc = device_get_softc(dev);
55146492Swpaul
552109323Ssam	wi_stop(&sc->sc_if, 1);
55346492Swpaul}
55446492Swpaul
555109323Ssamvoid
556109323Ssamwi_intr(void *arg)
55746492Swpaul{
558109323Ssam	int i;
559109323Ssam	struct wi_softc *sc = arg;
560109323Ssam	struct ifnet *ifp = &sc->sc_ic.ic_if;
561109323Ssam	u_int16_t status, raw_status, last_status;
562109323Ssam	WI_LOCK_DECL();
56346492Swpaul
564109323Ssam	WI_LOCK(sc);
56546492Swpaul
566109323Ssam	if (sc->wi_gone || (ifp->if_flags & IFF_UP) == 0) {
567109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, ~0);
568109323Ssam		CSR_WRITE_2(sc, WI_INT_EN, 0);
569109323Ssam		WI_UNLOCK(sc);
57046492Swpaul		return;
571109323Ssam	}
57246492Swpaul
573109323Ssam	/* maximum 10 loops per interrupt */
574109323Ssam	last_status = 0;
575109323Ssam	for (i = 0; i < 10; i++) {
576109323Ssam		/*
577109323Ssam		 * Only believe a status bit when we enter wi_intr, or when
578109323Ssam		 * the bit was "off" the last time through the loop. This is
579109323Ssam		 * my strategy to avoid racing the hardware/firmware if I
580109323Ssam		 * can re-read the event status register more quickly than
581109323Ssam		 * it is updated.
582109323Ssam		 */
583109323Ssam		raw_status = CSR_READ_2(sc, WI_EVENT_STAT);
584109323Ssam		status = raw_status & ~last_status;
585109323Ssam		if ((status & WI_INTRS) == 0)
586109323Ssam			break;
587109323Ssam		last_status = raw_status;
58846492Swpaul
589109323Ssam		if (status & WI_EV_RX)
590109323Ssam			wi_rx_intr(sc);
59146492Swpaul
592109323Ssam		if (status & WI_EV_ALLOC)
593109323Ssam			wi_tx_intr(sc);
59446492Swpaul
595109323Ssam		if (status & WI_EV_TX_EXC)
596109323Ssam			wi_tx_ex_intr(sc);
59746492Swpaul
598109323Ssam		if (status & WI_EV_INFO)
599109323Ssam			wi_info_intr(sc);
60046492Swpaul
601109323Ssam		if ((ifp->if_flags & IFF_OACTIVE) == 0 &&
602109323Ssam		    (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0 &&
603109323Ssam		    _IF_QLEN(&ifp->if_snd) != 0)
604109323Ssam			wi_start(ifp);
60593359Simp	}
60646492Swpaul
607109323Ssam	WI_UNLOCK(sc);
60846492Swpaul
60946492Swpaul	return;
61046492Swpaul}
61146492Swpaul
612109323Ssamvoid
613109323Ssamwi_init(void *arg)
61446492Swpaul{
615109323Ssam	struct wi_softc *sc = arg;
616109323Ssam	struct ifnet *ifp = &sc->sc_if;
617109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
618109323Ssam	struct wi_joinreq join;
619109323Ssam	int i;
620109323Ssam	int error = 0, wasenabled;
621109323Ssam	struct ifaddr *ifa;
622109323Ssam	struct sockaddr_dl *sdl;
623109323Ssam	WI_LOCK_DECL();
62446492Swpaul
625109323Ssam	WI_LOCK(sc);
62667092Swpaul
627109323Ssam	if (sc->wi_gone) {
628109323Ssam		WI_UNLOCK(sc);
62946492Swpaul		return;
63046492Swpaul	}
63146492Swpaul
632109323Ssam	wasenabled = sc->sc_enabled;
633109323Ssam	if (!sc->sc_enabled) {
634109323Ssam		sc->sc_enabled = 1;
635109323Ssam	} else
636109323Ssam		wi_stop(ifp, 0);
63746492Swpaul
638109323Ssam	/* Symbol firmware cannot be initialized more than once */
639109323Ssam	if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled)
640109323Ssam		wi_reset(sc);
64146492Swpaul
642109323Ssam	/* common 802.11 configuration */
643109323Ssam	ic->ic_flags &= ~IEEE80211_F_IBSSON;
644109323Ssam	sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
645109323Ssam	switch (ic->ic_opmode) {
646109323Ssam	case IEEE80211_M_STA:
647109323Ssam		wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_BSS);
648109323Ssam		break;
649109323Ssam	case IEEE80211_M_IBSS:
650109323Ssam		wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_ibss_port);
651109323Ssam		ic->ic_flags |= IEEE80211_F_IBSSON;
652109323Ssam		break;
653109323Ssam	case IEEE80211_M_AHDEMO:
654109323Ssam		wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC);
655109323Ssam		break;
656109323Ssam	case IEEE80211_M_HOSTAP:
657109323Ssam		wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP);
658109323Ssam		break;
65946492Swpaul	}
66046492Swpaul
661109323Ssam	/* Intersil interprets this RID as joining ESS even in IBSS mode */
662109323Ssam	if (sc->sc_firmware_type == WI_LUCENT &&
663109323Ssam	    (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen > 0)
664109323Ssam		wi_write_val(sc, WI_RID_CREATE_IBSS, 1);
665109323Ssam	else
666109323Ssam		wi_write_val(sc, WI_RID_CREATE_IBSS, 0);
667109323Ssam	wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
668109323Ssam	wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_essid,
669109323Ssam	    ic->ic_des_esslen);
670109323Ssam	wi_write_val(sc, WI_RID_OWN_CHNL, ic->ic_ibss_chan);
671109323Ssam	wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_essid, ic->ic_des_esslen);
67246492Swpaul
673109323Ssam	ifa = ifaddr_byindex(ifp->if_index);
674109323Ssam	sdl = (struct sockaddr_dl *) ifa->ifa_addr;
675109323Ssam	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(sdl));
676109323Ssam	wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN);
67775373Salfred
678109323Ssam	wi_write_val(sc, WI_RID_PM_ENABLED,
679109323Ssam	    (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
68046492Swpaul
681109323Ssam	/* not yet common 802.11 configuration */
682109323Ssam	wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen);
683109323Ssam	wi_write_val(sc, WI_RID_RTS_THRESH, sc->sc_rts_thresh);
684109323Ssam	if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
685109323Ssam		wi_write_val(sc, WI_RID_FRAG_THRESH, sc->sc_frag_thresh);
68646492Swpaul
687109323Ssam	/* driver specific 802.11 configuration */
688109323Ssam	if (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)
689109323Ssam		wi_write_val(sc, WI_RID_SYSTEM_SCALE, sc->sc_system_scale);
690109323Ssam	if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
691109323Ssam		wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
692109323Ssam	if (sc->sc_flags & WI_FLAGS_HAS_MOR)
693109323Ssam		wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
694109323Ssam	wi_write_txrate(sc);
695109323Ssam	wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
69646492Swpaul
697109323Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
698109323Ssam	    sc->sc_firmware_type == WI_INTERSIL) {
699109323Ssam		wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_lintval);
700109323Ssam		wi_write_val(sc, WI_RID_BASIC_RATE, 0x03);   /* 1, 2 */
701109323Ssam		wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */
702109323Ssam		wi_write_val(sc, WI_RID_DTIM_PERIOD, 1);
70346492Swpaul	}
70446492Swpaul
705109323Ssam	/*
706109323Ssam	 * Initialize promisc mode.
707109323Ssam	 *	Being in the Host-AP mode causes a great
708109323Ssam	 *	deal of pain if primisc mode is set.
709109323Ssam	 *	Therefore we avoid confusing the firmware
710109323Ssam	 *	and always reset promisc mode in Host-AP
711109323Ssam	 *	mode.  Host-AP sees all the packets anyway.
712109323Ssam	 */
713109323Ssam	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
714109323Ssam	    (ifp->if_flags & IFF_PROMISC) != 0) {
715109323Ssam		wi_write_val(sc, WI_RID_PROMISC, 1);
716109323Ssam	} else {
717109323Ssam		wi_write_val(sc, WI_RID_PROMISC, 0);
71875373Salfred	}
71946492Swpaul
720109323Ssam	/* Configure WEP. */
721109323Ssam	if (ic->ic_flags & IEEE80211_F_HASWEP)
722109323Ssam		wi_write_wep(sc);
72367092Swpaul
724109323Ssam	/* Set multicast filter. */
725109323Ssam	wi_write_multi(sc);
72646492Swpaul
727109323Ssam	if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) {
728109323Ssam		sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
729109323Ssam		if (sc->sc_firmware_type == WI_SYMBOL)
730109323Ssam			sc->sc_buflen = 1585;	/* XXX */
731109323Ssam		for (i = 0; i < WI_NTXBUF; i++) {
732109323Ssam			error = wi_alloc_fid(sc, sc->sc_buflen,
733109323Ssam			    &sc->sc_txd[i].d_fid);
734109323Ssam			if (error) {
735109323Ssam				device_printf(sc->sc_dev,
736109323Ssam				    "tx buffer allocation failed (error %u)\n",
737109323Ssam				    error);
738109323Ssam				goto out;
739109323Ssam			}
740109323Ssam			sc->sc_txd[i].d_len = 0;
74170073Swpaul		}
74270073Swpaul	}
743109323Ssam	sc->sc_txcur = sc->sc_txnext = 0;
74470073Swpaul
745109323Ssam	/* Enable desired port */
746109323Ssam	wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
74746492Swpaul
748109323Ssam	ifp->if_flags |= IFF_RUNNING;
749109323Ssam	ifp->if_flags &= ~IFF_OACTIVE;
750109323Ssam	if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
751109323Ssam	    ic->ic_opmode == IEEE80211_M_HOSTAP)
752109323Ssam		wi_newstate(sc, IEEE80211_S_RUN);
75346492Swpaul
754109323Ssam	/* Enable interrupts */
755109323Ssam	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
75646492Swpaul
757109323Ssam	if (!wasenabled &&
758109323Ssam	    ic->ic_opmode == IEEE80211_M_HOSTAP &&
759109323Ssam	    sc->sc_firmware_type == WI_INTERSIL) {
760109323Ssam		/* XXX: some card need to be re-enabled for hostap */
761109323Ssam		wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
762109323Ssam		wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
76375149Simp	}
76494397Simp
765109323Ssam	if (ic->ic_opmode == IEEE80211_M_STA &&
766109323Ssam	    ((ic->ic_flags & IEEE80211_F_DESBSSID) ||
767109323Ssam	    ic->ic_des_chan != IEEE80211_CHAN_ANY)) {
768109323Ssam		memset(&join, 0, sizeof(join));
769109323Ssam		if (ic->ic_flags & IEEE80211_F_DESBSSID)
770109323Ssam			IEEE80211_ADDR_COPY(&join.wi_bssid, ic->ic_des_bssid);
771109323Ssam		if (ic->ic_des_chan != IEEE80211_CHAN_ANY)
772109323Ssam			join.wi_chan = htole16(ic->ic_des_chan);
773109323Ssam		/* Lucent firmware does not support the JOIN RID. */
774109323Ssam		if (sc->sc_firmware_type != WI_LUCENT)
775109323Ssam			wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
77694397Simp	}
77775373Salfred
778109323Ssam	WI_UNLOCK(sc);
77946492Swpaul	return;
780109323Ssamout:
781109323Ssam	if (error) {
782109323Ssam		if_printf(ifp, "interface not running\n");
783109323Ssam		wi_stop(ifp, 0);
78470073Swpaul	}
785109323Ssam	DPRINTF(("wi_init: return %d\n", error));
786109323Ssam	return;
78746492Swpaul}
78846492Swpaul
789109323Ssamvoid
790109323Ssamwi_stop(struct ifnet *ifp, int disable)
79146492Swpaul{
792109323Ssam	struct wi_softc *sc = ifp->if_softc;
793109323Ssam	WI_LOCK_DECL();
79446492Swpaul
795109323Ssam	WI_LOCK(sc);
79674998Swpaul
797109323Ssam	ieee80211_new_state(ifp, IEEE80211_S_INIT, -1);
798109323Ssam	if (sc->sc_enabled && !sc->wi_gone) {
799109323Ssam		CSR_WRITE_2(sc, WI_INT_EN, 0);
800109323Ssam		wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
801109323Ssam		if (disable) {
802109323Ssam#ifdef __NetBSD__
803109323Ssam			if (sc->sc_disable)
804109323Ssam				(*sc->sc_disable)(sc);
805109323Ssam#endif
806109323Ssam			sc->sc_enabled = 0;
80770073Swpaul		}
80870073Swpaul	}
80970073Swpaul
810109323Ssam	sc->sc_tx_timer = 0;
811109323Ssam	sc->sc_scan_timer = 0;
812109323Ssam	sc->sc_syn_timer = 0;
813109323Ssam	sc->sc_false_syns = 0;
814109323Ssam	sc->sc_naps = 0;
815109323Ssam	ifp->if_flags &= ~(IFF_OACTIVE | IFF_RUNNING);
816109323Ssam	ifp->if_timer = 0;
81746492Swpaul
818109323Ssam	WI_UNLOCK(sc);
81946492Swpaul}
82046492Swpaul
821109323Ssamstatic void
822109323Ssamwi_start(struct ifnet *ifp)
82346492Swpaul{
824109323Ssam	struct wi_softc	*sc = ifp->if_softc;
825109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
826109323Ssam	struct ieee80211_node *ni = NULL;
827109323Ssam	struct ieee80211_frame *wh;
828109323Ssam	struct mbuf *m0;
829109323Ssam	struct wi_frame frmhdr;
830109323Ssam	int cur, fid, off;
831109323Ssam	WI_LOCK_DECL();
83246492Swpaul
833109323Ssam	WI_LOCK(sc);
83446492Swpaul
835109323Ssam	if (sc->wi_gone) {
836109323Ssam		WI_UNLOCK(sc);
837109323Ssam		return;
83846492Swpaul	}
839109323Ssam	if (sc->sc_flags & WI_FLAGS_OUTRANGE) {
840109323Ssam		WI_UNLOCK(sc);
841109323Ssam		return;
84275373Salfred	}
843109323Ssam	KASSERT((ifp->if_flags & IFF_OACTIVE) == 0,
844109323Ssam		("wi_start: if_flags %x\n", ifp->if_flags));
84546492Swpaul
846109323Ssam	memset(&frmhdr, 0, sizeof(frmhdr));
847109323Ssam	cur = sc->sc_txnext;
848109323Ssam	for (;;) {
849109323Ssam		IF_POLL(&ic->ic_mgtq, m0);
850109323Ssam		if (m0 != NULL) {
851109323Ssam			if (sc->sc_txd[cur].d_len != 0) {
852109323Ssam				ifp->if_flags |= IFF_OACTIVE;
853109323Ssam				break;
854109323Ssam			}
855109323Ssam			IF_DEQUEUE(&ic->ic_mgtq, m0);
856109323Ssam			m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
857109323Ssam			    (caddr_t)&frmhdr.wi_ehdr);
858109323Ssam			frmhdr.wi_ehdr.ether_type = 0;
859109323Ssam                        wh = mtod(m0, struct ieee80211_frame *);
860109323Ssam		} else {
861109323Ssam			if (ic->ic_state != IEEE80211_S_RUN)
862109323Ssam				break;
863109323Ssam			IFQ_POLL(&ifp->if_snd, m0);
864109323Ssam			if (m0 == NULL)
865109323Ssam				break;
866109323Ssam			if (sc->sc_txd[cur].d_len != 0) {
867109323Ssam				ifp->if_flags |= IFF_OACTIVE;
868109323Ssam				break;
869109323Ssam			}
870109323Ssam			IFQ_DEQUEUE(&ifp->if_snd, m0);
871109323Ssam			ifp->if_opackets++;
872109323Ssam			m_copydata(m0, 0, ETHER_HDR_LEN,
873109323Ssam			    (caddr_t)&frmhdr.wi_ehdr);
874109323Ssam#if NBPFILTER > 0
875109323Ssam			BPF_MTAP(ifp, m0);
876109323Ssam#endif
87746492Swpaul
878109323Ssam			if ((m0 = ieee80211_encap(ifp, m0)) == NULL) {
879109323Ssam				ifp->if_oerrors++;
880109323Ssam				continue;
881109323Ssam			}
882109323Ssam                        wh = mtod(m0, struct ieee80211_frame *);
883109323Ssam			if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
884109323Ssam			    !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
885109323Ssam			    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
886109323Ssam			    IEEE80211_FC0_TYPE_DATA &&
887109323Ssam			    ((ni = ieee80211_find_node(ic, wh->i_addr1)) ==
888109323Ssam			    NULL || ni->ni_associd == 0)) {
889109323Ssam				m_freem(m0);
890109323Ssam				ifp->if_oerrors++;
891109323Ssam				continue;
892109323Ssam			}
893109323Ssam			if (ic->ic_flags & IEEE80211_F_WEPON)
894109323Ssam				wh->i_fc[1] |= IEEE80211_FC1_WEP;
89546492Swpaul
896109323Ssam		}
897109323Ssam#if NBPFILTER > 0
898109323Ssam		if (ic->ic_rawbpf)
899109323Ssam			bpf_mtap(ic->ic_rawbpf, m0);
900109323Ssam#endif
901109323Ssam		frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
902109323Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
903109323Ssam		    (wh->i_fc[1] & IEEE80211_FC1_WEP)) {
904109323Ssam			if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) {
905109323Ssam				ifp->if_oerrors++;
906109323Ssam				continue;
907109323Ssam			}
908109323Ssam			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
909109323Ssam		}
910109323Ssam		m_copydata(m0, 0, sizeof(struct ieee80211_frame),
911109323Ssam		    (caddr_t)&frmhdr.wi_whdr);
912109323Ssam		m_adj(m0, sizeof(struct ieee80211_frame));
913109323Ssam		frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
914109323Ssam#if NBPFILTER > 0
915109323Ssam		if (sc->sc_drvbpf) {
916109323Ssam			struct mbuf *mb;
91746492Swpaul
918109323Ssam			MGETHDR(mb, M_DONTWAIT, m0->m_type);
919109323Ssam			if (mb != NULL) {
920109323Ssam				(void) m_dup_pkthdr(mb, m0, M_DONTWAIT);
921109323Ssam				mb->m_next = m0;
922109323Ssam				mb->m_data = (caddr_t)&frmhdr;
923109323Ssam				mb->m_len = sizeof(frmhdr);
924109323Ssam				mb->m_pkthdr.len += mb->m_len;
925109323Ssam				bpf_mtap(sc->sc_drvbpf, mb);
926109323Ssam				m_free(mb);
927109323Ssam			}
928109323Ssam		}
92946492Swpaul#endif
930109323Ssam		if (IFF_DUMPPKTS(ifp))
931109323Ssam			wi_dump_pkt(&frmhdr, ni, -1);
932109323Ssam		fid = sc->sc_txd[cur].d_fid;
933109323Ssam		off = sizeof(frmhdr);
934109323Ssam		if (wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0 ||
935109323Ssam		    wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0) {
936109323Ssam			ifp->if_oerrors++;
937109323Ssam			m_freem(m0);
938109323Ssam			continue;
939109323Ssam		}
940109323Ssam		m_freem(m0);
941109323Ssam		sc->sc_txd[cur].d_len = off;
942109323Ssam		if (sc->sc_txcur == cur) {
943109323Ssam			if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) {
944109323Ssam				if_printf(ifp, "xmit failed\n");
945109323Ssam				sc->sc_txd[cur].d_len = 0;
946109323Ssam				continue;
947109323Ssam			}
948109323Ssam			sc->sc_tx_timer = 5;
949109323Ssam			ifp->if_timer = 1;
950109323Ssam		}
951109323Ssam		sc->sc_txnext = cur = (cur + 1) % WI_NTXBUF;
95274838Salfred	}
95346492Swpaul
954109323Ssam	WI_UNLOCK(sc);
95546492Swpaul}
95646492Swpaul
95788546Salfredstatic int
958109323Ssamwi_reset(struct wi_softc *sc)
95946492Swpaul{
960109323Ssam#define WI_INIT_TRIES 5
961109323Ssam	int i, error;
96246492Swpaul
963109323Ssam	for (i = 0; i < WI_INIT_TRIES; i++) {
964109323Ssam		if ((error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0)) == 0)
96546492Swpaul			break;
966109323Ssam		DELAY(WI_DELAY * 1000);
96746492Swpaul	}
96846492Swpaul
969109323Ssam	if (error) {
970109323Ssam		device_printf(sc->sc_dev, "init failed\n");
971109323Ssam		return error;
97275373Salfred	}
97346492Swpaul
974109323Ssam	CSR_WRITE_2(sc, WI_INT_EN, 0);
975109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, ~0);
97646492Swpaul
977109323Ssam	/* Calibrate timer. */
978109323Ssam	wi_write_val(sc, WI_RID_TICK_TIME, 0);
979109323Ssam	return 0;
980109323Ssam#undef WI_INIT_TRIES
98146492Swpaul}
98246492Swpaul
98388546Salfredstatic void
984109323Ssamwi_watchdog(struct ifnet *ifp)
98546492Swpaul{
986109323Ssam	struct wi_softc	*sc = ifp->if_softc;
98746492Swpaul
988109323Ssam	ifp->if_timer = 0;
989109323Ssam	if (!sc->sc_enabled)
990109323Ssam		return;
99146492Swpaul
992109323Ssam	if (sc->sc_tx_timer) {
993109323Ssam		if (--sc->sc_tx_timer == 0) {
994109323Ssam			if_printf(ifp, "device timeout\n");
995109323Ssam			ifp->if_oerrors++;
996109323Ssam			wi_init(ifp->if_softc);
997109323Ssam			return;
998109323Ssam		}
999109323Ssam		ifp->if_timer = 1;
100046492Swpaul	}
100146492Swpaul
1002109323Ssam	if (sc->sc_scan_timer) {
1003109323Ssam		if (--sc->sc_scan_timer <= WI_SCAN_WAIT - WI_SCAN_INQWAIT &&
1004109323Ssam		    sc->sc_firmware_type == WI_INTERSIL) {
1005109323Ssam			DPRINTF(("wi_watchdog: inquire scan\n"));
1006109323Ssam			wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
100746492Swpaul		}
1008109323Ssam		if (sc->sc_scan_timer)
1009109323Ssam			ifp->if_timer = 1;
101046492Swpaul	}
101146492Swpaul
1012109323Ssam	if (sc->sc_syn_timer) {
1013109323Ssam		if (--sc->sc_syn_timer == 0) {
1014109323Ssam			DPRINTF2(("wi_watchdog: %d false syns\n",
1015109323Ssam			    sc->sc_false_syns));
1016109323Ssam			sc->sc_false_syns = 0;
1017109323Ssam			ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
1018109323Ssam			sc->sc_syn_timer = 5;
1019109323Ssam		}
1020109323Ssam		ifp->if_timer = 1;
102146492Swpaul	}
102246492Swpaul
1023109323Ssam	/* TODO: rate control */
1024109323Ssam	ieee80211_watchdog(ifp);
102546492Swpaul}
102646492Swpaul
102788546Salfredstatic int
1028109323Ssamwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
102946492Swpaul{
1030109323Ssam	struct wi_softc *sc = ifp->if_softc;
1031109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1032109323Ssam	struct ifreq *ifr = (struct ifreq *)data;
1033109323Ssam	struct ieee80211req *ireq;
1034109323Ssam	u_int8_t nodename[IEEE80211_NWID_LEN];
1035109323Ssam	int error = 0;
103695534Simp#if __FreeBSD_version >= 500000
1037109323Ssam	struct thread *td = curthread;
103895534Simp#else
1039109323Ssam	struct proc *td = curproc;		/* Little white lie */
104095534Simp#endif
1041109323Ssam	struct wi_req wreq;
1042109323Ssam	WI_LOCK_DECL();
104346492Swpaul
1044109323Ssam	WI_LOCK(sc);
104546492Swpaul
104661818Sroberto	if (sc->wi_gone) {
104761818Sroberto		error = ENODEV;
104861818Sroberto		goto out;
104961818Sroberto	}
105046492Swpaul
1051109323Ssam	switch (cmd) {
105246492Swpaul	case SIOCSIFFLAGS:
1053100876Simp		/*
1054101139Simp		 * Can't do promisc and hostap at the same time.  If all that's
1055101139Simp		 * changing is the promisc flag, try to short-circuit a call to
1056101139Simp		 * wi_init() by just setting PROMISC in the hardware.
1057100876Simp		 */
105846492Swpaul		if (ifp->if_flags & IFF_UP) {
1059109323Ssam			if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
1060101139Simp			    ifp->if_flags & IFF_RUNNING) {
1061101139Simp				if (ifp->if_flags & IFF_PROMISC &&
1062109323Ssam				    !(sc->sc_if_flags & IFF_PROMISC)) {
1063109323Ssam					wi_write_val(sc, WI_RID_PROMISC, 1);
1064101139Simp				} else if (!(ifp->if_flags & IFF_PROMISC) &&
1065109323Ssam				    sc->sc_if_flags & IFF_PROMISC) {
1066109323Ssam					wi_write_val(sc, WI_RID_PROMISC, 0);
1067101139Simp				} else {
1068101139Simp					wi_init(sc);
1069101139Simp				}
1070100876Simp			} else {
107146492Swpaul				wi_init(sc);
1072100876Simp			}
107346492Swpaul		} else {
107446492Swpaul			if (ifp->if_flags & IFF_RUNNING) {
1075109323Ssam				wi_stop(ifp, 0);
107646492Swpaul			}
107746492Swpaul		}
1078109323Ssam		sc->sc_if_flags = ifp->if_flags;
107946492Swpaul		error = 0;
108046492Swpaul		break;
108177217Sphk	case SIOCSIFMEDIA:
108277217Sphk	case SIOCGIFMEDIA:
1083109323Ssam		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
108477217Sphk		break;
108546492Swpaul	case SIOCADDMULTI:
108646492Swpaul	case SIOCDELMULTI:
1087109323Ssam		error = wi_write_multi(sc);
108846492Swpaul		break;
1089109323Ssam	case SIOCGIFGENERIC:
1090109323Ssam		error = wi_get_cfg(ifp, cmd, data);
109146492Swpaul		break;
1092109323Ssam	case SIOCSIFGENERIC:
1093109323Ssam		error = suser(td);
109446492Swpaul		if (error)
109546492Swpaul			break;
1096109323Ssam		error = wi_set_cfg(ifp, cmd, data);
109746492Swpaul		break;
109893359Simp	case SIOCGPRISM2DEBUG:
109993359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
110093359Simp		if (error)
110193359Simp			break;
110293733Simp		if (!(ifp->if_flags & IFF_RUNNING) ||
110393733Simp		    sc->sc_firmware_type == WI_LUCENT) {
110493359Simp			error = EIO;
110593359Simp			break;
110693359Simp		}
110793359Simp		error = wi_get_debug(sc, &wreq);
110893359Simp		if (error == 0)
110993359Simp			error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
111093359Simp		break;
111193359Simp	case SIOCSPRISM2DEBUG:
111293593Sjhb		if ((error = suser(td)))
111393359Simp			goto out;
111493359Simp		error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
111593359Simp		if (error)
111693359Simp			break;
111793359Simp		error = wi_set_debug(sc, &wreq);
111893359Simp		break;
111977217Sphk	case SIOCG80211:
1120109323Ssam		ireq = (struct ieee80211req *) data;
1121109323Ssam		switch (ireq->i_type) {
112277217Sphk		case IEEE80211_IOC_STATIONNAME:
1123109323Ssam			ireq->i_len = sc->sc_nodelen + 1;
1124109323Ssam			error = copyout(sc->sc_nodename, ireq->i_data,
1125109323Ssam					ireq->i_len);
112677217Sphk			break;
1127109323Ssam		default:
1128109323Ssam			error = ieee80211_ioctl(ifp, cmd, data);
112977217Sphk			break;
113077217Sphk		}
113177217Sphk		break;
113277217Sphk	case SIOCS80211:
1133109323Ssam		error = suser(td);
1134109323Ssam		if (error)
1135109323Ssam			break;
1136109323Ssam		ireq = (struct ieee80211req *) data;
1137109323Ssam		switch (ireq->i_type) {
1138109323Ssam		case IEEE80211_IOC_STATIONNAME:
113977217Sphk			if (ireq->i_val != 0 ||
114077217Sphk			    ireq->i_len > IEEE80211_NWID_LEN) {
114177217Sphk				error = EINVAL;
114277217Sphk				break;
114377217Sphk			}
1144109323Ssam			memset(nodename, 0, IEEE80211_NWID_LEN);
1145109323Ssam			error = copyin(ireq->i_data, nodename, ireq->i_len);
1146109323Ssam			if (error)
114777217Sphk				break;
1148109323Ssam			if (sc->sc_enabled) {
1149109323Ssam				error = wi_write_ssid(sc, WI_RID_NODENAME,
1150109323Ssam					nodename, ireq->i_len);
1151109323Ssam				if (error)
1152109323Ssam					break;
115377217Sphk			}
1154109323Ssam			memcpy(sc->sc_nodename, nodename, IEEE80211_NWID_LEN);
1155109323Ssam			sc->sc_nodelen = ireq->i_len;
115677217Sphk			break;
115777217Sphk		default:
1158109323Ssam			error = ieee80211_ioctl(ifp, cmd, data);
115977217Sphk			break;
116077217Sphk		}
116177217Sphk		break;
116246492Swpaul	default:
1163109323Ssam		error = ieee80211_ioctl(ifp, cmd, data);
116446492Swpaul		break;
116546492Swpaul	}
1166109323Ssam	if (error == ENETRESET) {
1167109323Ssam		if (sc->sc_enabled)
1168109323Ssam			wi_init(ifp->if_softc);	/* XXX no error return */
1169109323Ssam		error = 0;
1170109323Ssam	}
117161818Srobertoout:
1172109323Ssam	WI_UNLOCK(sc);
117346492Swpaul
1174109323Ssam	return (error);
117546492Swpaul}
117646492Swpaul
1177109323Ssamstatic int
1178109323Ssamwi_media_change(struct ifnet *ifp)
117946492Swpaul{
1180109323Ssam	struct wi_softc *sc = ifp->if_softc;
1181109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1182109323Ssam	struct ifmedia_entry *ime;
1183109323Ssam	enum ieee80211_opmode newmode;
1184109323Ssam	int i, rate, error = 0;
118546492Swpaul
1186109323Ssam	ime = sc->sc_media.ifm_cur;
1187109323Ssam	if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) {
1188109323Ssam		i = -1;
1189109323Ssam	} else {
1190109323Ssam		rate = ieee80211_media2rate(ime->ifm_media, IEEE80211_T_DS);
1191109323Ssam		if (rate == 0)
1192109323Ssam			return EINVAL;
1193109323Ssam		for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
1194109323Ssam			if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) == rate)
1195109323Ssam				break;
1196109323Ssam		}
1197109323Ssam		if (i == IEEE80211_RATE_SIZE)
1198109323Ssam			return EINVAL;
1199109323Ssam	}
1200109323Ssam	if (ic->ic_fixed_rate != i) {
1201109323Ssam		ic->ic_fixed_rate = i;
1202109323Ssam		error = ENETRESET;
1203109323Ssam	}
120467092Swpaul
1205109323Ssam	if ((ime->ifm_media & IFM_IEEE80211_ADHOC) &&
1206109323Ssam	    (ime->ifm_media & IFM_FLAG0))
1207109323Ssam		newmode = IEEE80211_M_AHDEMO;
1208109323Ssam	else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
1209109323Ssam		newmode = IEEE80211_M_IBSS;
1210109323Ssam	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
1211109323Ssam		newmode = IEEE80211_M_HOSTAP;
1212109323Ssam	else
1213109323Ssam		newmode = IEEE80211_M_STA;
1214109323Ssam	if (ic->ic_opmode != newmode) {
1215109323Ssam		ic->ic_opmode = newmode;
1216109323Ssam		error = ENETRESET;
121767092Swpaul	}
1218109323Ssam	if (error == ENETRESET) {
1219109323Ssam		if (sc->sc_enabled)
1220109323Ssam			wi_init(ifp->if_softc); /* XXX error code lost */
1221109323Ssam		error = 0;
1222109323Ssam	}
1223109323Ssam#if 0
1224109323Ssam	ifp->if_baudrate = ifmedia_baudrate(sc->sc_media.ifm_cur->ifm_media);
1225109323Ssam#endif
1226109323Ssam	return error;
1227109323Ssam}
122846492Swpaul
1229109323Ssamstatic void
1230109323Ssamwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1231109323Ssam{
1232109323Ssam	struct wi_softc *sc = ifp->if_softc;
1233109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1234109323Ssam	u_int16_t val;
1235109323Ssam	int rate, len;
123646492Swpaul
1237109323Ssam	if (sc->wi_gone || !sc->sc_enabled) {
1238109323Ssam		imr->ifm_active = IFM_IEEE80211 | IFM_NONE;
1239109323Ssam		imr->ifm_status = 0;
1240109323Ssam		return;
1241109323Ssam	}
124246492Swpaul
1243109323Ssam	imr->ifm_status = IFM_AVALID;
1244109323Ssam	imr->ifm_active = IFM_IEEE80211;
1245109323Ssam	if (ic->ic_state == IEEE80211_S_RUN &&
1246109323Ssam	    (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0)
1247109323Ssam		imr->ifm_status |= IFM_ACTIVE;
1248109323Ssam	len = sizeof(val);
1249109323Ssam	if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) != 0)
1250109323Ssam		rate = 0;
1251109323Ssam	else {
1252109323Ssam		/* convert to 802.11 rate */
1253109323Ssam		rate = val * 2;
1254109323Ssam		if (sc->sc_firmware_type == WI_LUCENT) {
1255109323Ssam			if (rate == 10)
1256109323Ssam				rate = 11;	/* 5.5Mbps */
1257109323Ssam		} else {
1258109323Ssam			if (rate == 4*2)
1259109323Ssam				rate = 11;	/* 5.5Mbps */
1260109323Ssam			else if (rate == 8*2)
1261109323Ssam				rate = 22;	/* 11Mbps */
1262109323Ssam		}
1263109323Ssam	}
1264109323Ssam	imr->ifm_active |= ieee80211_rate2media(rate, IEEE80211_T_DS);
1265109323Ssam	switch (ic->ic_opmode) {
1266109323Ssam	case IEEE80211_M_STA:
1267109323Ssam		break;
1268109323Ssam	case IEEE80211_M_IBSS:
1269109323Ssam		imr->ifm_active |= IFM_IEEE80211_ADHOC;
1270109323Ssam		break;
1271109323Ssam	case IEEE80211_M_AHDEMO:
1272109323Ssam		imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
1273109323Ssam		break;
1274109323Ssam	case IEEE80211_M_HOSTAP:
1275109323Ssam		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
1276109323Ssam		break;
1277109323Ssam	}
1278109323Ssam}
127946492Swpaul
1280109323Ssamstatic void
1281109323Ssamwi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
1282109323Ssam{
1283109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1284109323Ssam	struct ieee80211_node *ni = &ic->ic_bss;
1285109323Ssam	struct ifnet *ifp = &ic->ic_if;
128698440Simp
1287109323Ssam	if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
1288109323Ssam		return;
128946492Swpaul
1290109323Ssam	DPRINTF(("wi_sync_bssid: bssid %s -> ", ether_sprintf(ni->ni_bssid)));
1291109323Ssam	DPRINTF(("%s ?\n", ether_sprintf(new_bssid)));
129246492Swpaul
1293109323Ssam	/* In promiscuous mode, the BSSID field is not a reliable
1294109323Ssam	 * indicator of the firmware's BSSID. Damp spurious
1295109323Ssam	 * change-of-BSSID indications.
1296109323Ssam	 */
1297109323Ssam	if ((ifp->if_flags & IFF_PROMISC) != 0 &&
1298109323Ssam	    sc->sc_false_syns >= WI_MAX_FALSE_SYNS)
1299109323Ssam		return;
130046492Swpaul
1301109323Ssam	ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
1302109323Ssam}
130346492Swpaul
1304109323Ssamstatic void
1305109323Ssamwi_rx_intr(struct wi_softc *sc)
1306109323Ssam{
1307109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1308109323Ssam	struct ifnet *ifp = &ic->ic_if;
1309109323Ssam	struct wi_frame frmhdr;
1310109323Ssam	struct mbuf *m;
1311109323Ssam	struct ieee80211_frame *wh;
1312109323Ssam	int fid, len, off, rssi;
1313109323Ssam	u_int8_t dir;
1314109323Ssam	u_int16_t status;
1315109323Ssam	u_int32_t rstamp;
131646611Swpaul
1317109323Ssam	fid = CSR_READ_2(sc, WI_RX_FID);
131846611Swpaul
1319109323Ssam	/* First read in the frame header */
1320109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) {
1321109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1322109323Ssam		ifp->if_ierrors++;
1323109323Ssam		DPRINTF(("wi_rx_intr: read fid %x failed\n", fid));
1324109323Ssam		return;
1325109323Ssam	}
132691695Simp
1327109323Ssam	if (IFF_DUMPPKTS(ifp))
1328109323Ssam		wi_dump_pkt(&frmhdr, NULL, frmhdr.wi_rx_signal);
132946492Swpaul
1330109323Ssam	/*
1331109323Ssam	 * Drop undecryptable or packets with receive errors here
1332109323Ssam	 */
1333109323Ssam	status = le16toh(frmhdr.wi_status);
1334109323Ssam	if (status & WI_STAT_ERRSTAT) {
1335109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1336109323Ssam		ifp->if_ierrors++;
1337109323Ssam		DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status));
1338109323Ssam		return;
1339109323Ssam	}
1340109323Ssam	rssi = frmhdr.wi_rx_signal;
1341109323Ssam	rstamp = (le16toh(frmhdr.wi_rx_tstamp0) << 16) |
1342109323Ssam	    le16toh(frmhdr.wi_rx_tstamp1);
134346492Swpaul
1344109323Ssam	len = le16toh(frmhdr.wi_dat_len);
1345109323Ssam	off = ALIGN(sizeof(struct ieee80211_frame));
134646563Swpaul
1347109323Ssam	MGETHDR(m, M_DONTWAIT, MT_DATA);
1348109323Ssam	if (m == NULL) {
1349109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1350109323Ssam		ifp->if_ierrors++;
1351109323Ssam		DPRINTF(("wi_rx_intr: MGET failed\n"));
1352109323Ssam		return;
1353109323Ssam	}
1354109323Ssam	if (off + len > MHLEN) {
1355109323Ssam		MCLGET(m, M_DONTWAIT);
1356109323Ssam		if ((m->m_flags & M_EXT) == 0) {
1357109323Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1358109323Ssam			m_freem(m);
1359109323Ssam			ifp->if_ierrors++;
1360109323Ssam			DPRINTF(("wi_rx_intr: MCLGET failed\n"));
1361109323Ssam			return;
1362109323Ssam		}
1363109323Ssam	}
136446492Swpaul
1365109323Ssam	m->m_data += off - sizeof(struct ieee80211_frame);
1366109323Ssam	memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame));
1367109323Ssam	wi_read_bap(sc, fid, sizeof(frmhdr),
1368109323Ssam	    m->m_data + sizeof(struct ieee80211_frame), len);
1369109323Ssam	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len;
1370109323Ssam	m->m_pkthdr.rcvif = ifp;
137194405Simp
1372109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
137346492Swpaul
1374109323Ssam#if NBPFILTER > 0
1375109323Ssam	if (sc->sc_drvbpf) {
1376109323Ssam		struct mbuf *mb;
1377100876Simp
1378109323Ssam		MGETHDR(mb, M_DONTWAIT, m->m_type);
1379109323Ssam		if (mb != NULL) {
1380109323Ssam			(void) m_dup_pkthdr(mb, m, M_DONTWAIT);
1381109323Ssam			mb->m_next = m;
1382109323Ssam			mb->m_data = (caddr_t)&frmhdr;
1383109323Ssam			mb->m_len = sizeof(frmhdr);
1384109323Ssam			mb->m_pkthdr.len += mb->m_len;
1385109323Ssam			bpf_mtap(sc->sc_drvbpf, mb);
1386109323Ssam			m_free(mb);
138791695Simp		}
138856965Swpaul	}
1389109323Ssam#endif
1390109323Ssam	wh = mtod(m, struct ieee80211_frame *);
1391109323Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
1392109323Ssam		/*
1393109323Ssam		 * WEP is decrypted by hardware. Clear WEP bit
1394109323Ssam		 * header for ieee80211_input().
1395109323Ssam		 */
1396109323Ssam		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
1397109323Ssam	}
139856965Swpaul
1399109323Ssam	/* synchronize driver's BSSID with firmware's BSSID */
1400109323Ssam	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
1401109323Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
1402109323Ssam		wi_sync_bssid(sc, wh->i_addr3);
140346492Swpaul
1404109323Ssam	ieee80211_input(ifp, m, rssi, rstamp);
1405109323Ssam}
140646492Swpaul
1407109323Ssamstatic void
1408109323Ssamwi_tx_ex_intr(struct wi_softc *sc)
1409109323Ssam{
1410109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1411109323Ssam	struct ifnet *ifp = &ic->ic_if;
1412109323Ssam	struct wi_frame frmhdr;
1413109323Ssam	int fid;
141446492Swpaul
1415109323Ssam	fid = CSR_READ_2(sc, WI_TX_CMP_FID);
1416109323Ssam	/* Read in the frame header */
1417109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
1418109323Ssam		u_int16_t status = le16toh(frmhdr.wi_status);
141946492Swpaul
1420109323Ssam		/*
1421109323Ssam		 * Spontaneous station disconnects appear as xmit
1422109323Ssam		 * errors.  Don't announce them and/or count them
1423109323Ssam		 * as an output error.
1424109323Ssam		 */
1425109323Ssam		if ((status & WI_TXSTAT_DISCONNECT) == 0) {
1426109323Ssam			if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
1427109323Ssam				if_printf(ifp, "tx failed");
1428109323Ssam				if (status & WI_TXSTAT_RET_ERR)
1429109323Ssam					printf(", retry limit exceeded");
1430109323Ssam				if (status & WI_TXSTAT_AGED_ERR)
1431109323Ssam					printf(", max transmit lifetime exceeded");
1432109323Ssam				if (status & WI_TXSTAT_DISCONNECT)
1433109323Ssam					printf(", port disconnected");
1434109323Ssam				if (status & WI_TXSTAT_FORM_ERR)
1435109323Ssam					printf(", invalid format (data len %u src %6D)",
1436109323Ssam						le16toh(frmhdr.wi_dat_len),
1437109323Ssam						frmhdr.wi_ehdr.ether_shost, ":");
1438109323Ssam				if (status & ~0xf)
1439109323Ssam					printf(", status=0x%x", status);
1440109323Ssam				printf("\n");
1441109323Ssam			}
1442109323Ssam			ifp->if_oerrors++;
1443109323Ssam		} else {
1444109323Ssam			DPRINTF(("port disconnected\n"));
1445109323Ssam			ifp->if_collisions++;	/* XXX */
1446109323Ssam		}
1447109323Ssam	} else
1448109323Ssam		DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
1449109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
1450109323Ssam}
145146492Swpaul
1452109323Ssamstatic void
1453109323Ssamwi_tx_intr(struct wi_softc *sc)
1454109323Ssam{
1455109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1456109323Ssam	struct ifnet *ifp = &ic->ic_if;
1457109323Ssam	int fid, cur;
145894405Simp
1459109323Ssam	fid = CSR_READ_2(sc, WI_ALLOC_FID);
1460109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
146146492Swpaul
1462109323Ssam	cur = sc->sc_txcur;
1463109323Ssam	if (sc->sc_txd[cur].d_fid != fid) {
1464109323Ssam		if_printf(ifp, "bad alloc %x != %x, cur %d nxt %d\n",
1465109323Ssam		    fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext);
1466109323Ssam		return;
1467109323Ssam	}
1468109323Ssam	sc->sc_tx_timer = 0;
1469109323Ssam	sc->sc_txd[cur].d_len = 0;
1470109323Ssam	sc->sc_txcur = cur = (cur + 1) % WI_NTXBUF;
1471109323Ssam	if (sc->sc_txd[cur].d_len == 0)
1472109323Ssam		ifp->if_flags &= ~IFF_OACTIVE;
1473109323Ssam	else {
1474109323Ssam		if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid,
1475109323Ssam		    0, 0)) {
1476109323Ssam			if_printf(ifp, "xmit failed\n");
1477109323Ssam			sc->sc_txd[cur].d_len = 0;
1478109323Ssam		} else {
1479109323Ssam			sc->sc_tx_timer = 5;
1480109323Ssam			ifp->if_timer = 1;
1481109323Ssam		}
1482109323Ssam	}
148346492Swpaul}
148446492Swpaul
148588546Salfredstatic void
1486109323Ssamwi_info_intr(struct wi_softc *sc)
148794405Simp{
1488109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1489109323Ssam	struct ifnet *ifp = &ic->ic_if;
1490109323Ssam	int i, fid, len, off;
1491109323Ssam	u_int16_t ltbuf[2];
1492109323Ssam	u_int16_t stat;
1493109323Ssam	u_int32_t *ptr;
149494405Simp
1495109323Ssam	fid = CSR_READ_2(sc, WI_INFO_FID);
1496109323Ssam	wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf));
149794405Simp
1498109323Ssam	switch (le16toh(ltbuf[1])) {
149994405Simp
1500109323Ssam	case WI_INFO_LINK_STAT:
1501109323Ssam		wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat));
1502109323Ssam		DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat)));
1503109323Ssam		switch (le16toh(stat)) {
1504109323Ssam		case WI_INFO_LINK_STAT_CONNECTED:
1505109323Ssam			sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
1506109323Ssam			if (ic->ic_state == IEEE80211_S_RUN &&
1507109323Ssam			    ic->ic_opmode != IEEE80211_M_IBSS)
1508109323Ssam				break;
1509109323Ssam			/* FALLTHROUGH */
1510109323Ssam		case WI_INFO_LINK_STAT_AP_CHG:
1511109323Ssam			ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
1512109323Ssam			break;
1513109323Ssam		case WI_INFO_LINK_STAT_AP_INR:
1514109323Ssam			sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
1515109323Ssam			break;
1516109323Ssam		case WI_INFO_LINK_STAT_AP_OOR:
1517109323Ssam			if (sc->sc_firmware_type == WI_SYMBOL &&
1518109323Ssam			    sc->sc_scan_timer > 0) {
1519109323Ssam				if (wi_cmd(sc, WI_CMD_INQUIRE,
1520109323Ssam				    WI_INFO_HOST_SCAN_RESULTS, 0, 0) != 0)
1521109323Ssam					sc->sc_scan_timer = 0;
1522109323Ssam				break;
1523109323Ssam			}
1524109323Ssam			if (ic->ic_opmode == IEEE80211_M_STA)
1525109323Ssam				sc->sc_flags |= WI_FLAGS_OUTRANGE;
1526109323Ssam			break;
1527109323Ssam		case WI_INFO_LINK_STAT_DISCONNECTED:
1528109323Ssam		case WI_INFO_LINK_STAT_ASSOC_FAILED:
1529109323Ssam			if (ic->ic_opmode == IEEE80211_M_STA)
1530109323Ssam				ieee80211_new_state(ifp, IEEE80211_S_INIT, -1);
1531109323Ssam			break;
1532109323Ssam		}
1533109323Ssam		break;
153494405Simp
1535109323Ssam	case WI_INFO_COUNTERS:
1536109323Ssam		/* some card versions have a larger stats structure */
1537109323Ssam		len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4);
1538109323Ssam		ptr = (u_int32_t *)&sc->sc_stats;
1539109323Ssam		off = sizeof(ltbuf);
1540109323Ssam		for (i = 0; i < len; i++, off += 2, ptr++) {
1541109323Ssam			wi_read_bap(sc, fid, off, &stat, sizeof(stat));
1542109323Ssam#ifdef WI_HERMES_STATS_WAR
1543109323Ssam			if (stat & 0xf000)
1544109323Ssam				stat = ~stat;
1545109323Ssam#endif
1546109323Ssam			*ptr += stat;
1547109323Ssam		}
1548109323Ssam		ifp->if_collisions = sc->sc_stats.wi_tx_single_retries +
1549109323Ssam		    sc->sc_stats.wi_tx_multi_retries +
1550109323Ssam		    sc->sc_stats.wi_tx_retry_limit;
1551109323Ssam		break;
1552109323Ssam
1553109323Ssam	case WI_INFO_SCAN_RESULTS:
1554109323Ssam	case WI_INFO_HOST_SCAN_RESULTS:
1555109323Ssam		wi_scan_result(sc, fid, le16toh(ltbuf[0]));
1556109323Ssam		break;
1557109323Ssam
1558109323Ssam	default:
1559109323Ssam		DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid,
1560109323Ssam		    le16toh(ltbuf[1]), le16toh(ltbuf[0])));
1561109323Ssam		break;
156294405Simp	}
1563109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
1564109323Ssam}
156594405Simp
1566109323Ssamstatic int
1567109323Ssamwi_write_multi(struct wi_softc *sc)
1568109323Ssam{
1569109323Ssam	struct ifnet *ifp = &sc->sc_ic.ic_if;
1570109323Ssam	int n;
1571109323Ssam	struct ifmultiaddr *ifma;
1572109323Ssam	struct wi_mcast mlist;
157394472Simp
1574109323Ssam	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
1575109323Ssamallmulti:
1576109323Ssam		memset(&mlist, 0, sizeof(mlist));
1577109323Ssam		return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1578109323Ssam		    sizeof(mlist));
157994405Simp	}
158094405Simp
1581109323Ssam	n = 0;
1582109323Ssam#if __FreeBSD_version < 500000
1583109323Ssam	LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1584109323Ssam#else
1585109323Ssam	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1586109323Ssam#endif
1587109323Ssam		if (ifma->ifma_addr->sa_family != AF_LINK)
1588109323Ssam			continue;
1589109323Ssam		if (n >= 16)
1590109323Ssam			goto allmulti;
1591109323Ssam		IEEE80211_ADDR_COPY(&mlist.wi_mcast[n],
1592109323Ssam		    (LLADDR((struct sockaddr_dl *)ifma->ifma_addr)));
1593109323Ssam		n++;
159494405Simp	}
1595109323Ssam	return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1596109323Ssam	    IEEE80211_ADDR_LEN * n);
159794405Simp}
159894405Simp
159994405Simpstatic void
1600109323Ssamwi_read_nicid(struct wi_softc *sc)
160146492Swpaul{
1602109323Ssam	struct wi_card_ident *id;
1603109323Ssam	char *p;
1604109323Ssam	int len;
1605109323Ssam	u_int16_t ver[4];
160646492Swpaul
1607109323Ssam	/* getting chip identity */
1608109323Ssam	memset(ver, 0, sizeof(ver));
1609109323Ssam	len = sizeof(ver);
1610109323Ssam	wi_read_rid(sc, WI_RID_CARD_ID, ver, &len);
1611109323Ssam	device_printf(sc->sc_dev, "using ");
161246492Swpaul
1613109323Ssam	sc->sc_firmware_type = WI_NOTYPE;
1614109323Ssam	for (id = wi_card_ident; id->card_name != NULL; id++) {
1615109323Ssam		if (le16toh(ver[0]) == id->card_id) {
1616109323Ssam			printf("%s", id->card_name);
1617109323Ssam			sc->sc_firmware_type = id->firm_type;
1618109323Ssam			break;
1619109323Ssam		}
162067092Swpaul	}
1621109323Ssam	if (sc->sc_firmware_type == WI_NOTYPE) {
1622109323Ssam		if (le16toh(ver[0]) & 0x8000) {
1623109323Ssam			printf("Unknown PRISM2 chip");
1624109323Ssam			sc->sc_firmware_type = WI_INTERSIL;
1625109323Ssam		} else {
1626109323Ssam			printf("Unknown Lucent chip");
1627109323Ssam			sc->sc_firmware_type = WI_LUCENT;
1628109323Ssam		}
162967092Swpaul	}
163046492Swpaul
1631109323Ssam	/* get primary firmware version (Only Prism chips) */
1632109323Ssam	if (sc->sc_firmware_type != WI_LUCENT) {
1633109323Ssam		memset(ver, 0, sizeof(ver));
1634109323Ssam		len = sizeof(ver);
1635109323Ssam		wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len);
1636109323Ssam		sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 +
1637109323Ssam		    le16toh(ver[3]) * 100 + le16toh(ver[1]);
163867092Swpaul	}
163946492Swpaul
1640109323Ssam	/* get station firmware version */
1641109323Ssam	memset(ver, 0, sizeof(ver));
1642109323Ssam	len = sizeof(ver);
1643109323Ssam	wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len);
1644109323Ssam	sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 +
1645109323Ssam	    le16toh(ver[3]) * 100 + le16toh(ver[1]);
1646109323Ssam	if (sc->sc_firmware_type == WI_INTERSIL &&
1647109323Ssam	    (sc->sc_sta_firmware_ver == 10102 ||
1648109323Ssam	     sc->sc_sta_firmware_ver == 20102)) {
1649109323Ssam		char ident[12];
1650109323Ssam		memset(ident, 0, sizeof(ident));
1651109323Ssam		len = sizeof(ident);
1652109323Ssam		/* value should be the format like "V2.00-11" */
1653109323Ssam		if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 &&
1654109323Ssam		    *(p = (char *)ident) >= 'A' &&
1655109323Ssam		    p[2] == '.' && p[5] == '-' && p[8] == '\0') {
1656109323Ssam			sc->sc_firmware_type = WI_SYMBOL;
1657109323Ssam			sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 +
1658109323Ssam			    (p[3] - '0') * 1000 + (p[4] - '0') * 100 +
1659109323Ssam			    (p[6] - '0') * 10 + (p[7] - '0');
166094405Simp		}
166194405Simp	}
1662109323Ssam	printf("\n");
1663109323Ssam	device_printf(sc->sc_dev, "%s Firmware: ",
1664109323Ssam	     sc->sc_firmware_type == WI_LUCENT ? "Lucent" :
1665109323Ssam	    (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil"));
1666109323Ssam	if (sc->sc_firmware_type != WI_LUCENT)	/* XXX */
1667109323Ssam		printf("Primary (%u.%u.%u), ",
1668109323Ssam		    sc->sc_pri_firmware_ver / 10000,
1669109323Ssam		    (sc->sc_pri_firmware_ver % 10000) / 100,
1670109323Ssam		    sc->sc_pri_firmware_ver % 100);
1671109323Ssam	printf("Station (%u.%u.%u)\n",
1672109323Ssam	    sc->sc_sta_firmware_ver / 10000,
1673109323Ssam	    (sc->sc_sta_firmware_ver % 10000) / 100,
1674109323Ssam	    sc->sc_sta_firmware_ver % 100);
1675109323Ssam}
167646492Swpaul
1677109323Ssamstatic int
1678109323Ssamwi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen)
1679109323Ssam{
1680109323Ssam	struct wi_ssid ssid;
168146492Swpaul
1682109323Ssam	if (buflen > IEEE80211_NWID_LEN)
1683109323Ssam		return ENOBUFS;
1684109323Ssam	memset(&ssid, 0, sizeof(ssid));
1685109323Ssam	ssid.wi_len = htole16(buflen);
1686109323Ssam	memcpy(ssid.wi_ssid, buf, buflen);
1687109323Ssam	return wi_write_rid(sc, rid, &ssid, sizeof(ssid));
1688109323Ssam}
168946492Swpaul
1690109323Ssamstatic int
1691109323Ssamwi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
1692109323Ssam{
1693109323Ssam	struct wi_softc *sc = ifp->if_softc;
1694109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1695109323Ssam	struct ifreq *ifr = (struct ifreq *)data;
1696109323Ssam	struct wi_req wreq;
1697109323Ssam	int len, n, error, mif, val;
169846492Swpaul
1699109323Ssam	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
1700109323Ssam	if (error)
1701109323Ssam		return error;
1702109323Ssam	len = (wreq.wi_len - 1) * 2;
1703109323Ssam	if (len < sizeof(u_int16_t))
1704109323Ssam		return ENOSPC;
1705109323Ssam	if (len > sizeof(wreq.wi_val))
1706109323Ssam		len = sizeof(wreq.wi_val);
170746492Swpaul
1708109323Ssam	switch (wreq.wi_type) {
170946492Swpaul
1710109323Ssam	case WI_RID_IFACE_STATS:
1711109323Ssam		memcpy(wreq.wi_val, &sc->sc_stats, sizeof(sc->sc_stats));
1712109323Ssam		if (len < sizeof(sc->sc_stats))
1713109323Ssam			error = ENOSPC;
1714109323Ssam		else
1715109323Ssam			len = sizeof(sc->sc_stats);
1716109323Ssam		break;
171746492Swpaul
1718109323Ssam	case WI_RID_ENCRYPTION:
1719109323Ssam	case WI_RID_TX_CRYPT_KEY:
1720109323Ssam	case WI_RID_DEFLT_CRYPT_KEYS:
1721109323Ssam	case WI_RID_TX_RATE:
1722109323Ssam		return ieee80211_cfgget(ifp, cmd, data);
172346492Swpaul
1724109323Ssam	case WI_RID_MICROWAVE_OVEN:
1725109323Ssam		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) {
1726109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1727109323Ssam			    &len);
1728109323Ssam			break;
1729109323Ssam		}
1730109323Ssam		wreq.wi_val[0] = htole16(sc->sc_microwave_oven);
1731109323Ssam		len = sizeof(u_int16_t);
1732109323Ssam		break;
173346492Swpaul
1734109323Ssam	case WI_RID_DBM_ADJUST:
1735109323Ssam		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_DBMADJUST)) {
1736109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1737109323Ssam			    &len);
1738109323Ssam			break;
1739109323Ssam		}
1740109323Ssam		wreq.wi_val[0] = htole16(sc->sc_dbm_adjust);
1741109323Ssam		len = sizeof(u_int16_t);
1742109323Ssam		break;
174346492Swpaul
1744109323Ssam	case WI_RID_ROAMING_MODE:
1745109323Ssam		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_ROAMING)) {
1746109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1747109323Ssam			    &len);
1748109323Ssam			break;
1749109323Ssam		}
1750109323Ssam		wreq.wi_val[0] = htole16(sc->sc_roaming_mode);
1751109323Ssam		len = sizeof(u_int16_t);
1752109323Ssam		break;
175346492Swpaul
1754109323Ssam	case WI_RID_SYSTEM_SCALE:
1755109323Ssam		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)) {
1756109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1757109323Ssam			    &len);
1758109323Ssam			break;
1759109323Ssam		}
1760109323Ssam		wreq.wi_val[0] = htole16(sc->sc_system_scale);
1761109323Ssam		len = sizeof(u_int16_t);
1762109323Ssam		break;
176346492Swpaul
1764109323Ssam	case WI_RID_FRAG_THRESH:
1765109323Ssam		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)) {
1766109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1767109323Ssam			    &len);
1768109323Ssam			break;
1769109323Ssam		}
1770109323Ssam		wreq.wi_val[0] = htole16(sc->sc_frag_thresh);
1771109323Ssam		len = sizeof(u_int16_t);
1772109323Ssam		break;
177346492Swpaul
1774109323Ssam	case WI_RID_READ_APS:
1775109323Ssam	case WI_RID_SCAN_RES:		/* XXX */
1776109323Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
1777109323Ssam			return ieee80211_cfgget(ifp, cmd, data);
1778109323Ssam		if (sc->sc_scan_timer > 0) {
1779109323Ssam			error = EINPROGRESS;
1780109323Ssam			break;
1781109323Ssam		}
1782109323Ssam		n = sc->sc_naps;
1783109323Ssam		if (len < sizeof(n)) {
1784109323Ssam			error = ENOSPC;
1785109323Ssam			break;
1786109323Ssam		}
1787109323Ssam		if (len < sizeof(n) + sizeof(struct wi_apinfo) * n)
1788109323Ssam			n = (len - sizeof(n)) / sizeof(struct wi_apinfo);
1789109323Ssam		len = sizeof(n) + sizeof(struct wi_apinfo) * n;
1790109323Ssam		memcpy(wreq.wi_val, &n, sizeof(n));
1791109323Ssam		memcpy((caddr_t)wreq.wi_val + sizeof(n), sc->sc_aps,
1792109323Ssam		    sizeof(struct wi_apinfo) * n);
1793109323Ssam		break;
179446492Swpaul
1795109323Ssam	case WI_RID_PRISM2:
1796109323Ssam		wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT;
1797109323Ssam		len = sizeof(u_int16_t);
1798109323Ssam		break;
179946492Swpaul
1800109323Ssam	case WI_RID_MIF:
1801109323Ssam		mif = wreq.wi_val[0];
1802109323Ssam		error = wi_cmd(sc, WI_CMD_READMIF, mif, 0, 0);
1803109323Ssam		val = CSR_READ_2(sc, WI_RESP0);
1804109323Ssam		wreq.wi_val[0] = val;
1805109323Ssam		len = sizeof(u_int16_t);
1806109323Ssam		break;
180746492Swpaul
1808109323Ssam	case WI_RID_ZERO_CACHE:
1809109323Ssam	case WI_RID_PROCFRAME:		/* ignore for compatibility */
1810109323Ssam		/* XXX ??? */
1811109323Ssam		break;
181246492Swpaul
1813109323Ssam	case WI_RID_READ_CACHE:
1814109323Ssam		return ieee80211_cfgget(ifp, cmd, data);
181546492Swpaul
1816109323Ssam	default:
1817109323Ssam		if (sc->sc_enabled) {
1818109323Ssam			error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
1819109323Ssam			    &len);
1820109323Ssam			break;
1821109323Ssam		}
1822109323Ssam		switch (wreq.wi_type) {
1823109323Ssam		case WI_RID_MAX_DATALEN:
1824109323Ssam			wreq.wi_val[0] = htole16(sc->sc_max_datalen);
1825109323Ssam			len = sizeof(u_int16_t);
1826109323Ssam			break;
1827109323Ssam		case WI_RID_RTS_THRESH:
1828109323Ssam			wreq.wi_val[0] = htole16(sc->sc_rts_thresh);
1829109323Ssam			len = sizeof(u_int16_t);
1830109323Ssam			break;
1831109323Ssam		case WI_RID_CNFAUTHMODE:
1832109323Ssam			wreq.wi_val[0] = htole16(sc->sc_cnfauthmode);
1833109323Ssam			len = sizeof(u_int16_t);
1834109323Ssam			break;
1835109323Ssam		case WI_RID_NODENAME:
1836109323Ssam			if (len < sc->sc_nodelen + sizeof(u_int16_t)) {
1837109323Ssam				error = ENOSPC;
1838109323Ssam				break;
1839109323Ssam			}
1840109323Ssam			len = sc->sc_nodelen + sizeof(u_int16_t);
1841109323Ssam			wreq.wi_val[0] = htole16((sc->sc_nodelen + 1) / 2);
1842109323Ssam			memcpy(&wreq.wi_val[1], sc->sc_nodename,
1843109323Ssam			    sc->sc_nodelen);
1844109323Ssam			break;
1845109323Ssam		default:
1846109323Ssam			return ieee80211_cfgget(ifp, cmd, data);
1847109323Ssam		}
1848109323Ssam		break;
184946492Swpaul	}
1850109323Ssam	if (error)
1851109323Ssam		return error;
1852109323Ssam	wreq.wi_len = (len + 1) / 2 + 1;
1853109323Ssam	return copyout(&wreq, ifr->ifr_data, (wreq.wi_len + 1) * 2);
185446492Swpaul}
185546492Swpaul
1856109323Ssamstatic int
1857109323Ssamwi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
185846492Swpaul{
1859109323Ssam	struct wi_softc *sc = ifp->if_softc;
1860109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
1861109323Ssam	struct ifreq *ifr = (struct ifreq *)data;
1862109323Ssam	struct wi_req wreq;
1863109323Ssam	struct mbuf *m;
1864109323Ssam	int i, len, error, mif, val;
186546492Swpaul
1866109323Ssam	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
1867109323Ssam	if (error)
1868109323Ssam		return error;
1869109323Ssam	len = (wreq.wi_len - 1) * 2;
1870109323Ssam	switch (wreq.wi_type) {
1871109323Ssam	case WI_RID_DBM_ADJUST:
1872109323Ssam		return ENODEV;
187367092Swpaul
1874109323Ssam	case WI_RID_NODENAME:
1875109323Ssam		if (le16toh(wreq.wi_val[0]) * 2 > len ||
1876109323Ssam		    le16toh(wreq.wi_val[0]) > sizeof(sc->sc_nodename)) {
1877109323Ssam			error = ENOSPC;
1878109323Ssam			break;
1879109323Ssam		}
1880109323Ssam		if (sc->sc_enabled) {
1881109323Ssam			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
1882109323Ssam			    len);
1883109323Ssam			if (error)
1884109323Ssam				break;
1885109323Ssam		}
1886109323Ssam		sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
1887109323Ssam		memcpy(sc->sc_nodename, &wreq.wi_val[1], sc->sc_nodelen);
1888109323Ssam		break;
188946492Swpaul
1890109323Ssam	case WI_RID_MICROWAVE_OVEN:
1891109323Ssam	case WI_RID_ROAMING_MODE:
1892109323Ssam	case WI_RID_SYSTEM_SCALE:
1893109323Ssam	case WI_RID_FRAG_THRESH:
1894109323Ssam		if (wreq.wi_type == WI_RID_MICROWAVE_OVEN &&
1895109323Ssam		    (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0)
1896109323Ssam			break;
1897109323Ssam		if (wreq.wi_type == WI_RID_ROAMING_MODE &&
1898109323Ssam		    (sc->sc_flags & WI_FLAGS_HAS_ROAMING) == 0)
1899109323Ssam			break;
1900109323Ssam		if (wreq.wi_type == WI_RID_SYSTEM_SCALE &&
1901109323Ssam		    (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) == 0)
1902109323Ssam			break;
1903109323Ssam		if (wreq.wi_type == WI_RID_FRAG_THRESH &&
1904109323Ssam		    (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) == 0)
1905109323Ssam			break;
1906109323Ssam		/* FALLTHROUGH */
1907109323Ssam	case WI_RID_RTS_THRESH:
1908109323Ssam	case WI_RID_CNFAUTHMODE:
1909109323Ssam	case WI_RID_MAX_DATALEN:
1910109323Ssam		if (sc->sc_enabled) {
1911109323Ssam			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
1912109323Ssam			    sizeof(u_int16_t));
1913109323Ssam			if (error)
1914109323Ssam				break;
1915109323Ssam		}
1916109323Ssam		switch (wreq.wi_type) {
1917109323Ssam		case WI_RID_FRAG_THRESH:
1918109323Ssam			sc->sc_frag_thresh = le16toh(wreq.wi_val[0]);
1919109323Ssam			break;
1920109323Ssam		case WI_RID_RTS_THRESH:
1921109323Ssam			sc->sc_rts_thresh = le16toh(wreq.wi_val[0]);
1922109323Ssam			break;
1923109323Ssam		case WI_RID_MICROWAVE_OVEN:
1924109323Ssam			sc->sc_microwave_oven = le16toh(wreq.wi_val[0]);
1925109323Ssam			break;
1926109323Ssam		case WI_RID_ROAMING_MODE:
1927109323Ssam			sc->sc_roaming_mode = le16toh(wreq.wi_val[0]);
1928109323Ssam			break;
1929109323Ssam		case WI_RID_SYSTEM_SCALE:
1930109323Ssam			sc->sc_system_scale = le16toh(wreq.wi_val[0]);
1931109323Ssam			break;
1932109323Ssam		case WI_RID_CNFAUTHMODE:
1933109323Ssam			sc->sc_cnfauthmode = le16toh(wreq.wi_val[0]);
1934109323Ssam			break;
1935109323Ssam		case WI_RID_MAX_DATALEN:
1936109323Ssam			sc->sc_max_datalen = le16toh(wreq.wi_val[0]);
1937109323Ssam			break;
1938109323Ssam		}
1939109323Ssam		break;
194094405Simp
1941109323Ssam	case WI_RID_TX_RATE:
1942109323Ssam		switch (le16toh(wreq.wi_val[0])) {
1943109323Ssam		case 3:
1944109323Ssam			ic->ic_fixed_rate = -1;
1945109323Ssam			break;
1946109323Ssam		default:
1947109323Ssam			for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
1948109323Ssam				if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL)
1949109323Ssam				    / 2 == le16toh(wreq.wi_val[0]))
1950109323Ssam					break;
1951109323Ssam			}
1952109323Ssam			if (i == IEEE80211_RATE_SIZE)
1953109323Ssam				return EINVAL;
1954109323Ssam			ic->ic_fixed_rate = i;
1955109323Ssam		}
1956109323Ssam		if (sc->sc_enabled)
1957109323Ssam			error = wi_write_txrate(sc);
1958109323Ssam		break;
195946492Swpaul
1960109323Ssam	case WI_RID_SCAN_APS:
1961109323Ssam		if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
1962109323Ssam			error = wi_scan_ap(sc);
1963109323Ssam		break;
196446492Swpaul
1965109323Ssam	case WI_RID_MGMT_XMIT:
1966109323Ssam		if (!sc->sc_enabled) {
1967109323Ssam			error = ENETDOWN;
1968109323Ssam			break;
1969109323Ssam		}
1970109323Ssam		if (ic->ic_mgtq.ifq_len > 5) {
1971109323Ssam			error = EAGAIN;
1972109323Ssam			break;
1973109323Ssam		}
1974109323Ssam		/* XXX wi_len looks in u_int8_t, not in u_int16_t */
1975109323Ssam		m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0, ifp, NULL);
1976109323Ssam		if (m == NULL) {
1977109323Ssam			error = ENOMEM;
1978109323Ssam			break;
1979109323Ssam		}
1980109323Ssam		IF_ENQUEUE(&ic->ic_mgtq, m);
1981109323Ssam		break;
198246492Swpaul
1983109323Ssam	case WI_RID_MIF:
1984109323Ssam		mif = wreq.wi_val[0];
1985109323Ssam		val = wreq.wi_val[1];
1986109323Ssam		error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0);
1987109323Ssam		break;
198846492Swpaul
1989109323Ssam	case WI_RID_PROCFRAME:		/* ignore for compatibility */
1990109323Ssam		break;
1991109323Ssam
1992109323Ssam	default:
1993109323Ssam		if (sc->sc_enabled) {
1994109323Ssam			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
1995109323Ssam			    len);
1996109323Ssam			if (error)
1997109323Ssam				break;
1998109323Ssam		}
1999109323Ssam		error = ieee80211_cfgset(ifp, cmd, data);
2000109323Ssam		break;
2001109323Ssam	}
2002109323Ssam	return error;
200346492Swpaul}
200446492Swpaul
2005109323Ssamstatic int
2006109323Ssamwi_write_txrate(struct wi_softc *sc)
200746492Swpaul{
2008109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
2009109323Ssam	int i;
2010109323Ssam	u_int16_t rate;
201146492Swpaul
2012109323Ssam	if (ic->ic_fixed_rate < 0)
2013109323Ssam		rate = 0;	/* auto */
2014109323Ssam	else
2015109323Ssam		rate = (ic->ic_sup_rates[ic->ic_fixed_rate] &
2016109323Ssam		    IEEE80211_RATE_VAL) / 2;
201746492Swpaul
2018109323Ssam	/* rate: 0, 1, 2, 5, 11 */
201946492Swpaul
2020109323Ssam	switch (sc->sc_firmware_type) {
2021109323Ssam	case WI_LUCENT:
2022109323Ssam		if (rate == 0)
2023109323Ssam			rate = 3;	/* auto */
2024109323Ssam		break;
2025109323Ssam	default:
2026109323Ssam		/* Choose a bit according to this table.
2027109323Ssam		 *
2028109323Ssam		 * bit | data rate
2029109323Ssam		 * ----+-------------------
2030109323Ssam		 * 0   | 1Mbps
2031109323Ssam		 * 1   | 2Mbps
2032109323Ssam		 * 2   | 5.5Mbps
2033109323Ssam		 * 3   | 11Mbps
2034109323Ssam		 */
2035109323Ssam		for (i = 8; i > 0; i >>= 1) {
2036109323Ssam			if (rate >= i)
2037109323Ssam				break;
2038109323Ssam		}
2039109323Ssam		if (i == 0)
2040109323Ssam			rate = 0xf;	/* auto */
2041109323Ssam		else
2042109323Ssam			rate = i;
2043109323Ssam		break;
2044109323Ssam	}
2045109323Ssam	return wi_write_val(sc, WI_RID_TX_RATE, rate);
2046109323Ssam}
204746492Swpaul
2048109323Ssamstatic int
2049109323Ssamwi_write_wep(struct wi_softc *sc)
2050109323Ssam{
2051109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
2052109323Ssam	int error = 0;
2053109323Ssam	int i, keylen;
2054109323Ssam	u_int16_t val;
2055109323Ssam	struct wi_key wkey[IEEE80211_WEP_NKID];
205646492Swpaul
2057109323Ssam	switch (sc->sc_firmware_type) {
2058109323Ssam	case WI_LUCENT:
2059109323Ssam		val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0;
2060109323Ssam		error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
2061109323Ssam		if (error)
2062109323Ssam			break;
2063109323Ssam		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey);
2064109323Ssam		if (error)
2065109323Ssam			break;
2066109323Ssam		memset(wkey, 0, sizeof(wkey));
2067109323Ssam		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2068109323Ssam			keylen = ic->ic_nw_keys[i].wk_len;
2069109323Ssam			wkey[i].wi_keylen = htole16(keylen);
2070109323Ssam			memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key,
2071109323Ssam			    keylen);
2072109323Ssam		}
2073109323Ssam		error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS,
2074109323Ssam		    wkey, sizeof(wkey));
2075109323Ssam		break;
2076109323Ssam
2077109323Ssam	case WI_INTERSIL:
2078109323Ssam	case WI_SYMBOL:
2079109323Ssam		if (ic->ic_flags & IEEE80211_F_WEPON) {
2080109323Ssam			/*
2081109323Ssam			 * ONLY HWB3163 EVAL-CARD Firmware version
2082109323Ssam			 * less than 0.8 variant2
2083109323Ssam			 *
2084109323Ssam			 *   If promiscuous mode disable, Prism2 chip
2085109323Ssam			 *  does not work with WEP .
2086109323Ssam			 * It is under investigation for details.
2087109323Ssam			 * (ichiro@netbsd.org)
2088109323Ssam			 */
2089109323Ssam			if (sc->sc_firmware_type == WI_INTERSIL &&
2090109323Ssam			    sc->sc_sta_firmware_ver < 802 ) {
2091109323Ssam				/* firm ver < 0.8 variant 2 */
2092109323Ssam				wi_write_val(sc, WI_RID_PROMISC, 1);
2093109323Ssam			}
2094109323Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE,
2095109323Ssam			    sc->sc_cnfauthmode);
2096109323Ssam			val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED;
2097109323Ssam			/*
2098109323Ssam			 * Encryption firmware has a bug for HostAP mode.
2099109323Ssam			 */
2100109323Ssam			if (sc->sc_firmware_type == WI_INTERSIL &&
2101109323Ssam			    ic->ic_opmode == IEEE80211_M_HOSTAP)
2102109323Ssam				val |= HOST_ENCRYPT;
2103109323Ssam		} else {
2104109323Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE,
2105109323Ssam			    IEEE80211_AUTH_OPEN);
2106109323Ssam			val = HOST_ENCRYPT | HOST_DECRYPT;
2107109323Ssam		}
2108109323Ssam		error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
2109109323Ssam		if (error)
2110109323Ssam			break;
2111109323Ssam		error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY,
2112109323Ssam		    ic->ic_wep_txkey);
2113109323Ssam		if (error)
2114109323Ssam			break;
2115109323Ssam		/*
2116109323Ssam		 * It seems that the firmware accept 104bit key only if
2117109323Ssam		 * all the keys have 104bit length.  We get the length of
2118109323Ssam		 * the transmit key and use it for all other keys.
2119109323Ssam		 * Perhaps we should use software WEP for such situation.
2120109323Ssam		 */
2121109323Ssam		keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len;
2122109323Ssam		if (keylen > IEEE80211_WEP_KEYLEN)
2123109323Ssam			keylen = 13;	/* 104bit keys */
2124109323Ssam		else
2125109323Ssam			keylen = IEEE80211_WEP_KEYLEN;
2126109323Ssam		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2127109323Ssam			error = wi_write_rid(sc, WI_RID_P2_CRYPT_KEY0 + i,
2128109323Ssam			    ic->ic_nw_keys[i].wk_key, keylen);
2129109323Ssam			if (error)
2130109323Ssam				break;
2131109323Ssam		}
2132109323Ssam		break;
2133109323Ssam	}
2134109323Ssam	return error;
213546492Swpaul}
213646492Swpaul
2137109323Ssamstatic int
2138109323Ssamwi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
213946492Swpaul{
2140109323Ssam	int			i, s = 0;
2141109323Ssam	static volatile int count  = 0;
2142109323Ssam
2143109323Ssam	if (count > 0)
2144109323Ssam		panic("Hey partner, hold on there!");
2145109323Ssam	count++;
214653702Swpaul
2147109323Ssam	/* wait for the busy bit to clear */
2148109323Ssam	for (i = 500; i > 0; i--) {	/* 5s */
2149109323Ssam		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) {
2150109323Ssam			break;
215190580Sbrooks		}
2152109323Ssam		DELAY(10*1000);	/* 10 m sec */
2153109323Ssam	}
2154109323Ssam	if (i == 0) {
2155109323Ssam		device_printf(sc->sc_dev, "wi_cmd: busy bit won't clear.\n" );
2156109323Ssam		count--;
2157109323Ssam		return(ETIMEDOUT);
2158109323Ssam	}
215990580Sbrooks
2160109323Ssam	CSR_WRITE_2(sc, WI_PARAM0, val0);
2161109323Ssam	CSR_WRITE_2(sc, WI_PARAM1, val1);
2162109323Ssam	CSR_WRITE_2(sc, WI_PARAM2, val2);
2163109323Ssam	CSR_WRITE_2(sc, WI_COMMAND, cmd);
216490580Sbrooks
2165109323Ssam	if (cmd == WI_CMD_INI) {
2166109323Ssam		/* XXX: should sleep here. */
2167109323Ssam		DELAY(100*1000);
2168109323Ssam	}
2169109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
2170109323Ssam		/*
2171109323Ssam		 * Wait for 'command complete' bit to be
2172109323Ssam		 * set in the event status register.
2173109323Ssam		 */
2174109323Ssam		s = CSR_READ_2(sc, WI_EVENT_STAT);
2175109323Ssam		if (s & WI_EV_CMD) {
2176109323Ssam			/* Ack the event and read result code. */
2177109323Ssam			s = CSR_READ_2(sc, WI_STATUS);
2178109323Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
2179109323Ssam#ifdef foo
2180109323Ssam			if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK))
2181109323Ssam				return(EIO);
2182109323Ssam#endif
2183109323Ssam			if (s & WI_STAT_CMD_RESULT) {
2184109323Ssam				count--;
2185109323Ssam				return(EIO);
2186109323Ssam			}
2187109323Ssam			break;
218890580Sbrooks		}
2189109323Ssam		DELAY(WI_DELAY);
2190109323Ssam	}
219190580Sbrooks
2192109323Ssam	count--;
2193109323Ssam	if (i == WI_TIMEOUT) {
2194109323Ssam		device_printf(sc->sc_dev,
2195109323Ssam		    "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
2196109323Ssam		return(ETIMEDOUT);
219753702Swpaul	}
2198109323Ssam	return (0);
2199109323Ssam}
220053702Swpaul
2201109323Ssamstatic int
2202109323Ssamwi_seek_bap(struct wi_softc *sc, int id, int off)
2203109323Ssam{
2204109323Ssam	int i, status;
220590580Sbrooks
2206109323Ssam	CSR_WRITE_2(sc, WI_SEL0, id);
2207109323Ssam	CSR_WRITE_2(sc, WI_OFF0, off);
220890580Sbrooks
2209109323Ssam	for (i = 0; ; i++) {
2210109323Ssam		status = CSR_READ_2(sc, WI_OFF0);
2211109323Ssam		if ((status & WI_OFF_BUSY) == 0)
2212109323Ssam			break;
2213109323Ssam		if (i == WI_TIMEOUT) {
2214109323Ssam			device_printf(sc->sc_dev, "timeout in wi_seek to %x/%x\n",
2215109323Ssam			    id, off);
2216109323Ssam			sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
2217109323Ssam			return ETIMEDOUT;
2218109323Ssam		}
2219109323Ssam		DELAY(1);
222053702Swpaul	}
2221109323Ssam	if (status & WI_OFF_ERR) {
2222109323Ssam		device_printf(sc->sc_dev, "failed in wi_seek to %x/%x\n", id, off);
2223109323Ssam		sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
2224109323Ssam		return EIO;
2225109323Ssam	}
2226109323Ssam	sc->sc_bap_id = id;
2227109323Ssam	sc->sc_bap_off = off;
2228109323Ssam	return 0;
222953702Swpaul}
223053702Swpaul
2231109323Ssamstatic int
2232109323Ssamwi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
223353702Swpaul{
2234109323Ssam	u_int16_t *ptr;
2235109323Ssam	int i, error, cnt;
223653702Swpaul
2237109323Ssam	if (buflen == 0)
2238109323Ssam		return 0;
2239109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
2240109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
2241109323Ssam			return error;
224275219Salfred	}
2243109323Ssam	cnt = (buflen + 1) / 2;
2244109323Ssam	ptr = (u_int16_t *)buf;
2245109323Ssam	for (i = 0; i < cnt; i++)
2246109323Ssam		*ptr++ = CSR_READ_2(sc, WI_DATA0);
2247109323Ssam	sc->sc_bap_off += cnt * 2;
2248109323Ssam	return 0;
224953702Swpaul}
225053702Swpaul
2251109323Ssamstatic int
2252109323Ssamwi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
225353702Swpaul{
2254109323Ssam	u_int16_t *ptr;
2255109323Ssam	int i, error, cnt;
225646492Swpaul
2257109323Ssam	if (buflen == 0)
2258109323Ssam		return 0;
225946492Swpaul
2260109323Ssam#ifdef WI_HERMES_AUTOINC_WAR
2261109323Ssam  again:
2262109323Ssam#endif
2263109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
2264109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
2265109323Ssam			return error;
2266109323Ssam	}
2267109323Ssam	cnt = (buflen + 1) / 2;
2268109323Ssam	ptr = (u_int16_t *)buf;
2269109323Ssam	for (i = 0; i < cnt; i++)
2270109323Ssam		CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
2271109323Ssam	sc->sc_bap_off += cnt * 2;
2272109323Ssam
2273109323Ssam#ifdef WI_HERMES_AUTOINC_WAR
2274109323Ssam	/*
2275109323Ssam	 * According to the comments in the HCF Light code, there is a bug
2276109323Ssam	 * in the Hermes (or possibly in certain Hermes firmware revisions)
2277109323Ssam	 * where the chip's internal autoincrement counter gets thrown off
2278109323Ssam	 * during data writes:  the autoincrement is missed, causing one
2279109323Ssam	 * data word to be overwritten and subsequent words to be written to
2280109323Ssam	 * the wrong memory locations. The end result is that we could end
2281109323Ssam	 * up transmitting bogus frames without realizing it. The workaround
2282109323Ssam	 * for this is to write a couple of extra guard words after the end
2283109323Ssam	 * of the transfer, then attempt to read then back. If we fail to
2284109323Ssam	 * locate the guard words where we expect them, we preform the
2285109323Ssam	 * transfer over again.
2286109323Ssam	 */
2287109323Ssam	if ((sc->sc_flags & WI_FLAGS_BUG_AUTOINC) && (id & 0xf000) == 0) {
2288109323Ssam		CSR_WRITE_2(sc, WI_DATA0, 0x1234);
2289109323Ssam		CSR_WRITE_2(sc, WI_DATA0, 0x5678);
2290109323Ssam		wi_seek_bap(sc, id, sc->sc_bap_off);
2291109323Ssam		sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
2292109323Ssam		if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
2293109323Ssam		    CSR_READ_2(sc, WI_DATA0) != 0x5678) {
2294109323Ssam			device_printf(sc->sc_dev,
2295109323Ssam				"detect auto increment bug, try again\n");
2296109323Ssam			goto again;
2297109323Ssam		}
2298109323Ssam	}
2299109323Ssam#endif
2300109323Ssam	return 0;
230146492Swpaul}
230253702Swpaul
2303109323Ssamstatic int
2304109323Ssamwi_mwrite_bap(struct wi_softc *sc, int id, int off, struct mbuf *m0, int totlen)
2305109323Ssam{
2306109323Ssam	int error, len;
2307109323Ssam	struct mbuf *m;
230853702Swpaul
2309109323Ssam	for (m = m0; m != NULL && totlen > 0; m = m->m_next) {
2310109323Ssam		if (m->m_len == 0)
2311109323Ssam			continue;
231253702Swpaul
2313109323Ssam		len = min(m->m_len, totlen);
231453702Swpaul
2315109323Ssam		if (((u_long)m->m_data) % 2 != 0 || len % 2 != 0) {
2316109323Ssam			m_copydata(m, 0, totlen, (caddr_t)&sc->sc_txbuf);
2317109323Ssam			return wi_write_bap(sc, id, off, (caddr_t)&sc->sc_txbuf,
2318109323Ssam			    totlen);
2319109323Ssam		}
232053702Swpaul
2321109323Ssam		if ((error = wi_write_bap(sc, id, off, m->m_data, len)) != 0)
2322109323Ssam			return error;
232353702Swpaul
2324109323Ssam		off += m->m_len;
2325109323Ssam		totlen -= len;
2326109323Ssam	}
2327109323Ssam	return 0;
2328109323Ssam}
232953702Swpaul
2330109323Ssamstatic int
2331109323Ssamwi_alloc_fid(struct wi_softc *sc, int len, int *idp)
233253702Swpaul{
233353702Swpaul	int i;
233453702Swpaul
2335109323Ssam	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
2336109323Ssam		device_printf(sc->sc_dev, "failed to allocate %d bytes on NIC\n",
2337109323Ssam		    len);
2338109323Ssam		return ENOMEM;
233953702Swpaul	}
234053702Swpaul
2341109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
2342109323Ssam		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
2343109323Ssam			break;
2344109323Ssam		if (i == WI_TIMEOUT) {
2345109323Ssam			device_printf(sc->sc_dev, "timeout in alloc\n");
2346109323Ssam			return ETIMEDOUT;
2347109323Ssam		}
2348109323Ssam		DELAY(1);
234953702Swpaul	}
2350109323Ssam	*idp = CSR_READ_2(sc, WI_ALLOC_FID);
2351109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
2352109323Ssam	return 0;
2353109323Ssam}
235453702Swpaul
2355109323Ssamstatic int
2356109323Ssamwi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp)
2357109323Ssam{
2358109323Ssam	int error, len;
2359109323Ssam	u_int16_t ltbuf[2];
236053702Swpaul
2361109323Ssam	/* Tell the NIC to enter record read mode. */
2362109323Ssam	error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0);
2363109323Ssam	if (error)
2364109323Ssam		return error;
236553702Swpaul
2366109323Ssam	error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
2367109323Ssam	if (error)
2368109323Ssam		return error;
236953702Swpaul
2370109323Ssam	if (le16toh(ltbuf[1]) != rid) {
2371109323Ssam		device_printf(sc->sc_dev, "record read mismatch, rid=%x, got=%x\n",
2372109323Ssam		    rid, le16toh(ltbuf[1]));
2373109323Ssam		return EIO;
237453702Swpaul	}
2375109323Ssam	len = (le16toh(ltbuf[0]) - 1) * 2;	 /* already got rid */
2376109323Ssam	if (*buflenp < len) {
2377109323Ssam		device_printf(sc->sc_dev, "record buffer is too small, "
2378109323Ssam		    "rid=%x, size=%d, len=%d\n",
2379109323Ssam		    rid, *buflenp, len);
2380109323Ssam		return ENOSPC;
238153702Swpaul	}
2382109323Ssam	*buflenp = len;
2383109323Ssam	return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len);
2384109323Ssam}
238553702Swpaul
2386109323Ssamstatic int
2387109323Ssamwi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen)
2388109323Ssam{
2389109323Ssam	int error;
2390109323Ssam	u_int16_t ltbuf[2];
239153702Swpaul
2392109323Ssam	ltbuf[0] = htole16((buflen + 1) / 2 + 1);	 /* includes rid */
2393109323Ssam	ltbuf[1] = htole16(rid);
239453702Swpaul
2395109323Ssam	error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
2396109323Ssam	if (error)
2397109323Ssam		return error;
2398109323Ssam	error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen);
2399109323Ssam	if (error)
2400109323Ssam		return error;
2401102206Simp
2402109323Ssam	return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0);
240353702Swpaul}
240477217Sphk
240588546Salfredstatic int
2406109323Ssamwi_newstate(void *arg, enum ieee80211_state nstate)
240777217Sphk{
2408109323Ssam	struct wi_softc *sc = arg;
2409109323Ssam	struct ieee80211com *ic = &sc->sc_ic;
2410109323Ssam	struct ieee80211_node *ni = &ic->ic_bss;
2411109323Ssam	int i, buflen;
2412109323Ssam	u_int16_t val;
2413109323Ssam	struct wi_ssid ssid;
2414109323Ssam	u_int8_t old_bssid[IEEE80211_ADDR_LEN];
2415109323Ssam	enum ieee80211_state ostate;
2416109323Ssam#ifdef WI_DEBUG
2417109323Ssam	static const char *stname[] =
2418109323Ssam	    { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" };
2419109323Ssam#endif /* WI_DEBUG */
242077217Sphk
2421109323Ssam	ostate = ic->ic_state;
2422109323Ssam	DPRINTF(("wi_newstate: %s -> %s\n", stname[ostate], stname[nstate]));
2423109323Ssam
2424109323Ssam	ic->ic_state = nstate;
2425109323Ssam	switch (nstate) {
2426109323Ssam	case IEEE80211_S_INIT:
2427109323Ssam		ic->ic_flags &= ~IEEE80211_F_SIBSS;
2428109323Ssam		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
2429109323Ssam		return 0;
2430109323Ssam
2431109323Ssam	case IEEE80211_S_RUN:
2432109323Ssam		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
2433109323Ssam		buflen = IEEE80211_ADDR_LEN;
2434109323Ssam		wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen);
2435109323Ssam		IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
2436109323Ssam		buflen = sizeof(val);
2437109323Ssam		wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen);
2438109323Ssam		ni->ni_chan = le16toh(val);
2439109323Ssam
2440109323Ssam		if (IEEE80211_ADDR_EQ(old_bssid, ni->ni_bssid))
2441109323Ssam			sc->sc_false_syns++;
2442109323Ssam		else
2443109323Ssam			sc->sc_false_syns = 0;
2444109323Ssam
2445109323Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
2446109323Ssam			ni->ni_esslen = ic->ic_des_esslen;
2447109323Ssam			memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
2448109323Ssam			ni->ni_nrate = 0;
2449109323Ssam			for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
2450109323Ssam				if (ic->ic_sup_rates[i])
2451109323Ssam					ni->ni_rates[ni->ni_nrate++] =
2452109323Ssam					    ic->ic_sup_rates[i];
245377217Sphk			}
2454109323Ssam			ni->ni_intval = ic->ic_lintval;
2455109323Ssam			ni->ni_capinfo = IEEE80211_CAPINFO_ESS;
2456109323Ssam			if (ic->ic_flags & IEEE80211_F_WEPON)
2457109323Ssam				ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
245877217Sphk		} else {
2459109323Ssam			/* XXX check return value */
2460109323Ssam			buflen = sizeof(ssid);
2461109323Ssam			wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen);
2462109323Ssam			ni->ni_esslen = le16toh(ssid.wi_len);
2463109323Ssam			if (ni->ni_esslen > IEEE80211_NWID_LEN)
2464109323Ssam				ni->ni_esslen = IEEE80211_NWID_LEN;	/*XXX*/
2465109323Ssam			memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
246677217Sphk		}
246777217Sphk		break;
2468109323Ssam
2469109323Ssam	case IEEE80211_S_SCAN:
2470109323Ssam	case IEEE80211_S_AUTH:
2471109323Ssam	case IEEE80211_S_ASSOC:
247277217Sphk		break;
247377217Sphk	}
247477217Sphk
2475109323Ssam	/* skip standard ieee80211 handling */
2476109323Ssam	return EINPROGRESS;
247777217Sphk}
247877217Sphk
247988546Salfredstatic int
2480109323Ssamwi_scan_ap(struct wi_softc *sc)
248177217Sphk{
2482109323Ssam	int error = 0;
2483109323Ssam	u_int16_t val[2];
248477217Sphk
2485109323Ssam	if (!sc->sc_enabled)
2486109323Ssam		return ENXIO;
2487109323Ssam	switch (sc->sc_firmware_type) {
2488109323Ssam	case WI_LUCENT:
2489109323Ssam		(void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
249098440Simp		break;
2491109323Ssam	case WI_INTERSIL:
2492109323Ssam		val[0] = 0x3fff;	/* channel */
2493109323Ssam		val[1] = 0x000f;	/* tx rate */
2494109323Ssam		error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val));
249598440Simp		break;
2496109323Ssam	case WI_SYMBOL:
2497109323Ssam		/*
2498109323Ssam		 * XXX only supported on 3.x ?
2499109323Ssam		 */
2500109323Ssam		val[0] = BSCAN_BCAST | BSCAN_ONETIME;
2501109323Ssam		error = wi_write_rid(sc, WI_RID_BCAST_SCAN_REQ,
2502109323Ssam		    val, sizeof(val[0]));
250398440Simp		break;
250498440Simp	}
2505109323Ssam	if (error == 0) {
2506109323Ssam		sc->sc_scan_timer = WI_SCAN_WAIT;
2507109323Ssam		sc->sc_ic.ic_if.if_timer = 1;
2508109323Ssam		DPRINTF(("wi_scan_ap: start scanning\n"));
2509109323Ssam	}
2510109323Ssam	return error;
2511109323Ssam}
251277217Sphk
2513109323Ssamstatic void
2514109323Ssamwi_scan_result(struct wi_softc *sc, int fid, int cnt)
2515109323Ssam{
2516109323Ssam#define	N(a)	(sizeof (a) / sizeof (a[0]))
2517109323Ssam	int i, naps, off, szbuf;
2518109323Ssam	struct wi_scan_header ws_hdr;	/* Prism2 header */
2519109323Ssam	struct wi_scan_data_p2 ws_dat;	/* Prism2 scantable*/
2520109323Ssam	struct wi_apinfo *ap;
2521109323Ssam
2522109323Ssam	off = sizeof(u_int16_t) * 2;
2523109323Ssam	memset(&ws_hdr, 0, sizeof(ws_hdr));
2524109323Ssam	switch (sc->sc_firmware_type) {
2525109323Ssam	case WI_INTERSIL:
2526109323Ssam		wi_read_bap(sc, fid, off, &ws_hdr, sizeof(ws_hdr));
2527109323Ssam		off += sizeof(ws_hdr);
2528109323Ssam		szbuf = sizeof(struct wi_scan_data_p2);
252977217Sphk		break;
2530109323Ssam	case WI_SYMBOL:
2531109323Ssam		szbuf = sizeof(struct wi_scan_data_p2) + 6;
253277217Sphk		break;
2533109323Ssam	case WI_LUCENT:
2534109323Ssam		szbuf = sizeof(struct wi_scan_data);
253577217Sphk		break;
2536109323Ssam	default:
2537109323Ssam		device_printf(sc->sc_dev,
2538109323Ssam			"wi_scan_result: unknown firmware type %u\n",
2539109323Ssam			sc->sc_firmware_type);
2540109323Ssam		naps = 0;
2541109323Ssam		goto done;
254277217Sphk	}
2543109323Ssam	naps = (cnt * 2 + 2 - off) / szbuf;
2544109323Ssam	if (naps > N(sc->sc_aps))
2545109323Ssam		naps = N(sc->sc_aps);
2546109323Ssam	sc->sc_naps = naps;
2547109323Ssam	/* Read Data */
2548109323Ssam	ap = sc->sc_aps;
2549109323Ssam	memset(&ws_dat, 0, sizeof(ws_dat));
2550109323Ssam	for (i = 0; i < naps; i++, ap++) {
2551109323Ssam		wi_read_bap(sc, fid, off, &ws_dat,
2552109323Ssam		    (sizeof(ws_dat) < szbuf ? sizeof(ws_dat) : szbuf));
2553109323Ssam		DPRINTF2(("wi_scan_result: #%d: off %d bssid %s\n", i, off,
2554109323Ssam		    ether_sprintf(ws_dat.wi_bssid)));
2555109323Ssam		off += szbuf;
2556109323Ssam		ap->scanreason = le16toh(ws_hdr.wi_reason);
2557109323Ssam		memcpy(ap->bssid, ws_dat.wi_bssid, sizeof(ap->bssid));
2558109323Ssam		ap->channel = le16toh(ws_dat.wi_chid);
2559109323Ssam		ap->signal  = le16toh(ws_dat.wi_signal);
2560109323Ssam		ap->noise   = le16toh(ws_dat.wi_noise);
2561109323Ssam		ap->quality = ap->signal - ap->noise;
2562109323Ssam		ap->capinfo = le16toh(ws_dat.wi_capinfo);
2563109323Ssam		ap->interval = le16toh(ws_dat.wi_interval);
2564109323Ssam		ap->rate    = le16toh(ws_dat.wi_rate);
2565109323Ssam		ap->namelen = le16toh(ws_dat.wi_namelen);
2566109323Ssam		if (ap->namelen > sizeof(ap->name))
2567109323Ssam			ap->namelen = sizeof(ap->name);
2568109323Ssam		memcpy(ap->name, ws_dat.wi_name, ap->namelen);
2569109323Ssam	}
2570109323Ssamdone:
2571109323Ssam	/* Done scanning */
2572109323Ssam	sc->sc_scan_timer = 0;
2573109323Ssam	DPRINTF(("wi_scan_result: scan complete: ap %d\n", naps));
2574109323Ssam#undef N
257577217Sphk}
257677217Sphk
257788546Salfredstatic void
2578109323Ssamwi_dump_pkt(struct wi_frame *wh, struct ieee80211_node *ni, int rssi)
257977217Sphk{
2580109323Ssam	ieee80211_dump_pkt((u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr),
2581109323Ssam	    ni ? ni->ni_rates[ni->ni_txrate] & IEEE80211_RATE_VAL : -1, rssi);
2582109323Ssam	printf(" status 0x%x rx_tstamp1 %u rx_tstamp0 0x%u rx_silence %u\n",
2583109323Ssam		le16toh(wh->wi_status), le16toh(wh->wi_rx_tstamp1),
2584109323Ssam		le16toh(wh->wi_rx_tstamp0), wh->wi_rx_silence);
2585109323Ssam	printf(" rx_signal %u rx_rate %u rx_flow %u\n",
2586109323Ssam		wh->wi_rx_signal, wh->wi_rx_rate, wh->wi_rx_flow);
2587109323Ssam	printf(" tx_rtry %u tx_rate %u tx_ctl 0x%x dat_len %u\n",
2588109323Ssam		wh->wi_tx_rtry, wh->wi_tx_rate,
2589109323Ssam		le16toh(wh->wi_tx_ctl), le16toh(wh->wi_dat_len));
2590109323Ssam	printf(" ehdr dst %6D src %6D type 0x%x\n",
2591109323Ssam		wh->wi_ehdr.ether_dhost, ":", wh->wi_ehdr.ether_shost, ":",
2592109323Ssam		wh->wi_ehdr.ether_type);
2593109323Ssam}
259477217Sphk
2595109323Ssamint
2596109323Ssamwi_alloc(device_t dev, int rid)
2597109323Ssam{
2598109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
2599109323Ssam
2600109323Ssam	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
2601109323Ssam		sc->iobase_rid = rid;
2602109323Ssam		sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
2603109323Ssam		    &sc->iobase_rid, 0, ~0, (1 << 6),
2604109323Ssam		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
2605109323Ssam		if (!sc->iobase) {
2606109323Ssam			device_printf(dev, "No I/O space?!\n");
2607109323Ssam			return (ENXIO);
260898440Simp		}
2609109323Ssam
2610109323Ssam		sc->wi_io_addr = rman_get_start(sc->iobase);
2611109323Ssam		sc->wi_btag = rman_get_bustag(sc->iobase);
2612109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
2613109323Ssam	} else {
2614109323Ssam		sc->mem_rid = rid;
2615109323Ssam		sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY,
2616109323Ssam		    &sc->mem_rid, 0, ~0, 1, RF_ACTIVE);
2617109323Ssam
2618109323Ssam		if (!sc->mem) {
2619109323Ssam			device_printf(dev, "No Mem space on prism2.5?\n");
2620109323Ssam			return (ENXIO);
262177217Sphk		}
2622109323Ssam
2623109323Ssam		sc->wi_btag = rman_get_bustag(sc->mem);
2624109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->mem);
262577217Sphk	}
262677217Sphk
2627109323Ssam
2628109323Ssam	sc->irq_rid = 0;
2629109323Ssam	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
2630109323Ssam	    0, ~0, 1, RF_ACTIVE |
2631109323Ssam	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
2632109323Ssam
2633109323Ssam	if (!sc->irq) {
2634109323Ssam		wi_free(dev);
2635109323Ssam		device_printf(dev, "No irq?!\n");
2636109323Ssam		return (ENXIO);
263777217Sphk	}
2638109323Ssam
2639109323Ssam	sc->sc_dev = dev;
2640109323Ssam	sc->sc_unit = device_get_unit(dev);
2641109323Ssam
2642109323Ssam	return (0);
264377217Sphk}
264493359Simp
2645109323Ssamvoid
2646109323Ssamwi_free(device_t dev)
2647109323Ssam{
2648109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
2649109323Ssam
2650109323Ssam	if (sc->iobase != NULL) {
2651109323Ssam		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
2652109323Ssam		sc->iobase = NULL;
2653109323Ssam	}
2654109323Ssam	if (sc->irq != NULL) {
2655109323Ssam		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
2656109323Ssam		sc->irq = NULL;
2657109323Ssam	}
2658109323Ssam	if (sc->mem != NULL) {
2659109323Ssam		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
2660109323Ssam		sc->mem = NULL;
2661109323Ssam	}
2662109323Ssam
2663109323Ssam	return;
2664109323Ssam}
2665109323Ssam
266693359Simpstatic int
2667109323Ssamwi_get_debug(struct wi_softc *sc, struct wi_req *wreq)
266893359Simp{
2669109323Ssam	int error = 0;
267093359Simp
267193359Simp	wreq->wi_len = 1;
267293359Simp
267393359Simp	switch (wreq->wi_type) {
267493359Simp	case WI_DEBUG_SLEEP:
267593359Simp		wreq->wi_len++;
267693359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sleep;
267793359Simp		break;
267893359Simp	case WI_DEBUG_DELAYSUPP:
267993359Simp		wreq->wi_len++;
268093359Simp		wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
268193359Simp		break;
268293359Simp	case WI_DEBUG_TXSUPP:
268393359Simp		wreq->wi_len++;
268493359Simp		wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
268593359Simp		break;
268693359Simp	case WI_DEBUG_MONITOR:
268793359Simp		wreq->wi_len++;
268893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_monitor;
268993359Simp		break;
269093359Simp	case WI_DEBUG_LEDTEST:
269193359Simp		wreq->wi_len += 3;
269293359Simp		wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
269393359Simp		wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
269493359Simp		wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
269593359Simp		break;
269693359Simp	case WI_DEBUG_CONTTX:
269793359Simp		wreq->wi_len += 2;
269893359Simp		wreq->wi_val[0] = sc->wi_debug.wi_conttx;
269993359Simp		wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
270093359Simp		break;
270193359Simp	case WI_DEBUG_CONTRX:
270293359Simp		wreq->wi_len++;
270393359Simp		wreq->wi_val[0] = sc->wi_debug.wi_contrx;
270493359Simp		break;
270593359Simp	case WI_DEBUG_SIGSTATE:
270693359Simp		wreq->wi_len += 2;
270793359Simp		wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
270893359Simp		wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
270993359Simp		break;
271093359Simp	case WI_DEBUG_CONFBITS:
271193359Simp		wreq->wi_len += 2;
271293359Simp		wreq->wi_val[0] = sc->wi_debug.wi_confbits;
271393359Simp		wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
271493359Simp		break;
271593359Simp	default:
271693359Simp		error = EIO;
271793359Simp		break;
271893359Simp	}
271993359Simp
272093359Simp	return (error);
272193359Simp}
272293359Simp
272393359Simpstatic int
2724109323Ssamwi_set_debug(struct wi_softc *sc, struct wi_req *wreq)
272593359Simp{
2726109323Ssam	int error = 0;
272793359Simp	u_int16_t		cmd, param0 = 0, param1 = 0;
272893359Simp
272993359Simp	switch (wreq->wi_type) {
273093359Simp	case WI_DEBUG_RESET:
273193359Simp	case WI_DEBUG_INIT:
273293359Simp	case WI_DEBUG_CALENABLE:
273393359Simp		break;
273493359Simp	case WI_DEBUG_SLEEP:
273593359Simp		sc->wi_debug.wi_sleep = 1;
273693359Simp		break;
273793359Simp	case WI_DEBUG_WAKE:
273893359Simp		sc->wi_debug.wi_sleep = 0;
273993359Simp		break;
274093359Simp	case WI_DEBUG_CHAN:
274193359Simp		param0 = wreq->wi_val[0];
274293359Simp		break;
274393359Simp	case WI_DEBUG_DELAYSUPP:
274493359Simp		sc->wi_debug.wi_delaysupp = 1;
274593359Simp		break;
274693359Simp	case WI_DEBUG_TXSUPP:
274793359Simp		sc->wi_debug.wi_txsupp = 1;
274893359Simp		break;
274993359Simp	case WI_DEBUG_MONITOR:
275093359Simp		sc->wi_debug.wi_monitor = 1;
275193359Simp		break;
275293359Simp	case WI_DEBUG_LEDTEST:
275393359Simp		param0 = wreq->wi_val[0];
275493359Simp		param1 = wreq->wi_val[1];
275593359Simp		sc->wi_debug.wi_ledtest = 1;
275693359Simp		sc->wi_debug.wi_ledtest_param0 = param0;
275793359Simp		sc->wi_debug.wi_ledtest_param1 = param1;
275893359Simp		break;
275993359Simp	case WI_DEBUG_CONTTX:
276093359Simp		param0 = wreq->wi_val[0];
276193359Simp		sc->wi_debug.wi_conttx = 1;
276293359Simp		sc->wi_debug.wi_conttx_param0 = param0;
276393359Simp		break;
276493359Simp	case WI_DEBUG_STOPTEST:
276593359Simp		sc->wi_debug.wi_delaysupp = 0;
276693359Simp		sc->wi_debug.wi_txsupp = 0;
276793359Simp		sc->wi_debug.wi_monitor = 0;
276893359Simp		sc->wi_debug.wi_ledtest = 0;
276993359Simp		sc->wi_debug.wi_ledtest_param0 = 0;
277093359Simp		sc->wi_debug.wi_ledtest_param1 = 0;
277193359Simp		sc->wi_debug.wi_conttx = 0;
277293359Simp		sc->wi_debug.wi_conttx_param0 = 0;
277393359Simp		sc->wi_debug.wi_contrx = 0;
277493359Simp		sc->wi_debug.wi_sigstate = 0;
277593359Simp		sc->wi_debug.wi_sigstate_param0 = 0;
277693359Simp		break;
277793359Simp	case WI_DEBUG_CONTRX:
277893359Simp		sc->wi_debug.wi_contrx = 1;
277993359Simp		break;
278093359Simp	case WI_DEBUG_SIGSTATE:
278193359Simp		param0 = wreq->wi_val[0];
278293359Simp		sc->wi_debug.wi_sigstate = 1;
278393359Simp		sc->wi_debug.wi_sigstate_param0 = param0;
278493359Simp		break;
278593359Simp	case WI_DEBUG_CONFBITS:
278693359Simp		param0 = wreq->wi_val[0];
278793359Simp		param1 = wreq->wi_val[1];
278893359Simp		sc->wi_debug.wi_confbits = param0;
278993359Simp		sc->wi_debug.wi_confbits_param0 = param1;
279093359Simp		break;
279193359Simp	default:
279293359Simp		error = EIO;
279393359Simp		break;
279493359Simp	}
279593359Simp
279693359Simp	if (error)
279793359Simp		return (error);
279893359Simp
279993359Simp	cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
280093359Simp	error = wi_cmd(sc, cmd, param0, param1, 0);
280193359Simp
280293359Simp	return (error);
280393359Simp}
2804101903Simp
2805105076Simp#if __FreeBSD_version >= 500000
2806101903Simp/*
2807101903Simp * Special routines to download firmware for Symbol CF card.
2808101903Simp * XXX: This should be modified generic into any PRISM-2 based card.
2809101903Simp */
2810101903Simp
2811101903Simp#define	WI_SBCF_PDIADDR		0x3100
2812101903Simp
2813101903Simp/* unaligned load little endian */
2814101903Simp#define	GETLE32(p)	((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
2815101903Simp#define	GETLE16(p)	((p)[0] | ((p)[1]<<8))
2816101903Simp
2817101903Simpint
2818101903Simpwi_symbol_load_firm(struct wi_softc *sc, const void *primsym, int primlen,
2819101903Simp    const void *secsym, int seclen)
2820101903Simp{
2821101903Simp	uint8_t ebuf[256];
2822101903Simp	int i;
2823101903Simp
2824101903Simp	/* load primary code and run it */
2825101903Simp	wi_symbol_set_hcr(sc, WI_HCR_EEHOLD);
2826101903Simp	if (wi_symbol_write_firm(sc, primsym, primlen, NULL, 0))
2827101903Simp		return EIO;
2828101903Simp	wi_symbol_set_hcr(sc, WI_HCR_RUN);
2829101903Simp	for (i = 0; ; i++) {
2830101903Simp		if (i == 10)
2831101903Simp			return ETIMEDOUT;
2832101903Simp		tsleep(sc, PWAIT, "wiinit", 1);
2833101903Simp		if (CSR_READ_2(sc, WI_CNTL) == WI_CNTL_AUX_ENA_STAT)
2834101903Simp			break;
2835101903Simp		/* write the magic key value to unlock aux port */
2836101903Simp		CSR_WRITE_2(sc, WI_PARAM0, WI_AUX_KEY0);
2837101903Simp		CSR_WRITE_2(sc, WI_PARAM1, WI_AUX_KEY1);
2838101903Simp		CSR_WRITE_2(sc, WI_PARAM2, WI_AUX_KEY2);
2839101903Simp		CSR_WRITE_2(sc, WI_CNTL, WI_CNTL_AUX_ENA_CNTL);
2840101903Simp	}
2841101903Simp
2842101903Simp	/* issue read EEPROM command: XXX copied from wi_cmd() */
2843101903Simp	CSR_WRITE_2(sc, WI_PARAM0, 0);
2844101903Simp	CSR_WRITE_2(sc, WI_PARAM1, 0);
2845101903Simp	CSR_WRITE_2(sc, WI_PARAM2, 0);
2846101903Simp	CSR_WRITE_2(sc, WI_COMMAND, WI_CMD_READEE);
2847101903Simp        for (i = 0; i < WI_TIMEOUT; i++) {
2848101903Simp                if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD)
2849101903Simp                        break;
2850101903Simp                DELAY(1);
2851101903Simp        }
2852101903Simp        CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
2853101903Simp
2854101903Simp	CSR_WRITE_2(sc, WI_AUX_PAGE, WI_SBCF_PDIADDR / WI_AUX_PGSZ);
2855101903Simp	CSR_WRITE_2(sc, WI_AUX_OFFSET, WI_SBCF_PDIADDR % WI_AUX_PGSZ);
2856101903Simp	CSR_READ_MULTI_STREAM_2(sc, WI_AUX_DATA,
2857101903Simp	    (uint16_t *)ebuf, sizeof(ebuf) / 2);
2858101903Simp	if (GETLE16(ebuf) > sizeof(ebuf))
2859101903Simp		return EIO;
2860101903Simp	if (wi_symbol_write_firm(sc, secsym, seclen, ebuf + 4, GETLE16(ebuf)))
2861101903Simp		return EIO;
2862101903Simp	return 0;
2863101903Simp}
2864101903Simp
2865101903Simpstatic int
2866101903Simpwi_symbol_write_firm(struct wi_softc *sc, const void *buf, int buflen,
2867101903Simp    const void *ebuf, int ebuflen)
2868101903Simp{
2869101903Simp	const uint8_t *p, *ep, *q, *eq;
2870101903Simp	char *tp;
2871101903Simp	uint32_t addr, id, eid;
2872101903Simp	int i, len, elen, nblk, pdrlen;
2873101903Simp
2874101903Simp	/*
2875101903Simp	 * Parse the header of the firmware image.
2876101903Simp	 */
2877101903Simp	p = buf;
2878101903Simp	ep = p + buflen;
2879101903Simp	while (p < ep && *p++ != ' ');	/* FILE: */
2880101903Simp	while (p < ep && *p++ != ' ');	/* filename */
2881101903Simp	while (p < ep && *p++ != ' ');	/* type of the firmware */
2882101903Simp	nblk = strtoul(p, &tp, 10);
2883101903Simp	p = tp;
2884101903Simp	pdrlen = strtoul(p + 1, &tp, 10);
2885101903Simp	p = tp;
2886101903Simp	while (p < ep && *p++ != 0x1a);	/* skip rest of header */
2887101903Simp
2888101903Simp	/*
2889101903Simp	 * Block records: address[4], length[2], data[length];
2890101903Simp	 */
2891101903Simp	for (i = 0; i < nblk; i++) {
2892101903Simp		addr = GETLE32(p);	p += 4;
2893101903Simp		len  = GETLE16(p);	p += 2;
2894101903Simp		CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
2895101903Simp		CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
2896101903Simp		CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
2897101903Simp		    (const uint16_t *)p, len / 2);
2898101903Simp		p += len;
2899101903Simp	}
2900101903Simp
2901101903Simp	/*
2902101903Simp	 * PDR: id[4], address[4], length[4];
2903101903Simp	 */
2904101903Simp	for (i = 0; i < pdrlen; ) {
2905101903Simp		id   = GETLE32(p);	p += 4; i += 4;
2906101903Simp		addr = GETLE32(p);	p += 4; i += 4;
2907101903Simp		len  = GETLE32(p);	p += 4; i += 4;
2908101903Simp		/* replace PDR entry with the values from EEPROM, if any */
2909101903Simp		for (q = ebuf, eq = q + ebuflen; q < eq; q += elen * 2) {
2910101903Simp			elen = GETLE16(q);	q += 2;
2911101903Simp			eid  = GETLE16(q);	q += 2;
2912101903Simp			elen--;		/* elen includes eid */
2913101903Simp			if (eid == 0)
2914101903Simp				break;
2915101903Simp			if (eid != id)
2916101903Simp				continue;
2917101903Simp			CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
2918101903Simp			CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
2919101903Simp			CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
2920101903Simp			    (const uint16_t *)q, len / 2);
2921101903Simp			break;
2922101903Simp		}
2923101903Simp	}
2924101903Simp	return 0;
2925101903Simp}
2926101903Simp
2927101903Simpstatic int
2928101903Simpwi_symbol_set_hcr(struct wi_softc *sc, int mode)
2929101903Simp{
2930101903Simp	uint16_t hcr;
2931101903Simp
2932101903Simp	CSR_WRITE_2(sc, WI_COR, WI_COR_RESET);
2933101903Simp	tsleep(sc, PWAIT, "wiinit", 1);
2934101903Simp	hcr = CSR_READ_2(sc, WI_HCR);
2935101903Simp	hcr = (hcr & WI_HCR_4WIRE) | (mode & ~WI_HCR_4WIRE);
2936101903Simp	CSR_WRITE_2(sc, WI_HCR, hcr);
2937101903Simp	tsleep(sc, PWAIT, "wiinit", 1);
2938101903Simp	CSR_WRITE_2(sc, WI_COR, WI_COR_IOMODE);
2939101903Simp	tsleep(sc, PWAIT, "wiinit", 1);
2940101903Simp	return 0;
2941101903Simp}
2942105076Simp#endif
2943