if_wi.c revision 287197
1139749Simp/*-
246492Swpaul * Copyright (c) 1997, 1998, 1999
346492Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
446492Swpaul *
546492Swpaul * Redistribution and use in source and binary forms, with or without
646492Swpaul * modification, are permitted provided that the following conditions
746492Swpaul * are met:
846492Swpaul * 1. Redistributions of source code must retain the above copyright
946492Swpaul *    notice, this list of conditions and the following disclaimer.
1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1146492Swpaul *    notice, this list of conditions and the following disclaimer in the
1246492Swpaul *    documentation and/or other materials provided with the distribution.
1346492Swpaul * 3. All advertising materials mentioning features or use of this software
1446492Swpaul *    must display the following acknowledgement:
1546492Swpaul *	This product includes software developed by Bill Paul.
1646492Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1746492Swpaul *    may be used to endorse or promote products derived from this software
1846492Swpaul *    without specific prior written permission.
1946492Swpaul *
2046492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2146492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2246492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2346492Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2446492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2546492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2646492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2746492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2846492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2946492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3046492Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3146492Swpaul */
3246492Swpaul
3346492Swpaul/*
34109323Ssam * Lucent WaveLAN/IEEE 802.11 PCMCIA driver.
3546492Swpaul *
36109323Ssam * Original FreeBSD driver written by Bill Paul <wpaul@ctr.columbia.edu>
3746492Swpaul * Electrical Engineering Department
3846492Swpaul * Columbia University, New York City
3946492Swpaul */
4046492Swpaul
4146492Swpaul/*
4247401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN
4346492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed
4446492Swpaul * entirely via a firmware-driven controller called the Hermes.
4546492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual
4646492Swpaul * without an NDA (if at all). What they do release is an API library
4746492Swpaul * called the HCF (Hardware Control Functions) which is supposed to
4846492Swpaul * do the device-specific operations of a device driver for you. The
4946492Swpaul * publically available version of the HCF library (the 'HCF Light') is
5047401Swpaul * a) extremely gross, b) lacks certain features, particularly support
5146492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License.
5246492Swpaul *
5346492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it
5446492Swpaul * programs the Hermes controller directly, using information gleaned
5546492Swpaul * from the HCF Light code and corresponding documentation.
5646492Swpaul *
5795534Simp * This driver supports the ISA, PCMCIA and PCI versions of the Lucent
5895534Simp * WaveLan cards (based on the Hermes chipset), as well as the newer
5995534Simp * Prism 2 chipsets with firmware from Intersil and Symbol.
6046492Swpaul */
6146492Swpaul
62113038Sobrien#include <sys/cdefs.h>
63113038Sobrien__FBSDID("$FreeBSD: head/sys/dev/wi/if_wi.c 287197 2015-08-27 08:56:39Z glebius $");
64113038Sobrien
65230562Sadrian#include "opt_wlan.h"
66230562Sadrian
67109323Ssam#define WI_HERMES_STATS_WAR	/* Work around stats counter bug. */
68109323Ssam
6946492Swpaul#include <sys/param.h>
7046492Swpaul#include <sys/systm.h>
7195533Smike#include <sys/endian.h>
7246492Swpaul#include <sys/sockio.h>
7346492Swpaul#include <sys/mbuf.h>
74164033Srwatson#include <sys/priv.h>
7583366Sjulian#include <sys/proc.h>
7646492Swpaul#include <sys/kernel.h>
7746492Swpaul#include <sys/socket.h>
7853702Swpaul#include <sys/module.h>
7953702Swpaul#include <sys/bus.h>
8094486Simp#include <sys/random.h>
8153702Swpaul#include <sys/syslog.h>
8253702Swpaul#include <sys/sysctl.h>
8346492Swpaul
8453702Swpaul#include <machine/bus.h>
8553702Swpaul#include <machine/resource.h>
86116951Ssam#include <machine/atomic.h>
8753702Swpaul#include <sys/rman.h>
8853702Swpaul
8946492Swpaul#include <net/if.h>
90257176Sglebius#include <net/if_var.h>
9146492Swpaul#include <net/if_arp.h>
9246492Swpaul#include <net/ethernet.h>
9346492Swpaul#include <net/if_dl.h>
94190579Ssam#include <net/if_llc.h>
9546492Swpaul#include <net/if_media.h>
9646492Swpaul#include <net/if_types.h>
9746492Swpaul
98116951Ssam#include <net80211/ieee80211_var.h>
99116951Ssam#include <net80211/ieee80211_ioctl.h>
100119784Ssam#include <net80211/ieee80211_radiotap.h>
101116951Ssam
10246492Swpaul#include <netinet/in.h>
10346492Swpaul#include <netinet/in_systm.h>
10446492Swpaul#include <netinet/in_var.h>
10546492Swpaul#include <netinet/ip.h>
10646492Swpaul#include <netinet/if_ether.h>
10746492Swpaul
10846492Swpaul#include <net/bpf.h>
10946492Swpaul
11070808Speter#include <dev/wi/if_wavelan_ieee.h>
111119784Ssam#include <dev/wi/if_wireg.h>
11293611Simp#include <dev/wi/if_wivar.h>
11346492Swpaul
114228621Sbschmidtstatic struct ieee80211vap *wi_vap_create(struct ieee80211com *,
115228621Sbschmidt		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
116228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN],
117228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN]);
118178354Ssamstatic void wi_vap_delete(struct ieee80211vap *vap);
119287197Sglebiusstatic int  wi_transmit(struct ieee80211com *, struct mbuf *);
120287197Sglebiusstatic void wi_start(struct wi_softc *);
121287197Sglebiusstatic int  wi_start_tx(struct wi_softc *, struct wi_frame *, struct mbuf *);
122160991Ssamstatic int  wi_raw_xmit(struct ieee80211_node *, struct mbuf *,
123160991Ssam		const struct ieee80211_bpf_params *);
124178354Ssamstatic int  wi_newstate_sta(struct ieee80211vap *, enum ieee80211_state, int);
125192468Ssamstatic int  wi_newstate_hostap(struct ieee80211vap *, enum ieee80211_state,
126192468Ssam		int);
127178354Ssamstatic void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
128283535Sadrian		int subtype, const struct ieee80211_rx_stats *rxs,
129283535Sadrian		int rssi, int nf);
130178354Ssamstatic int  wi_reset(struct wi_softc *);
131165089Ssamstatic void wi_watchdog(void *);
132287197Sglebiusstatic void wi_parent(struct ieee80211com *);
133109323Ssamstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
134109323Ssamstatic void wi_rx_intr(struct wi_softc *);
135109323Ssamstatic void wi_tx_intr(struct wi_softc *);
136109323Ssamstatic void wi_tx_ex_intr(struct wi_softc *);
137178354Ssam
138109323Ssamstatic void wi_info_intr(struct wi_softc *);
13946492Swpaul
140178354Ssamstatic int  wi_write_txrate(struct wi_softc *, struct ieee80211vap *);
141178354Ssamstatic int  wi_write_wep(struct wi_softc *, struct ieee80211vap *);
142109323Ssamstatic int  wi_write_multi(struct wi_softc *);
143283540Sglebiusstatic void wi_update_mcast(struct ieee80211com *);
144283540Sglebiusstatic void wi_update_promisc(struct ieee80211com *);
145109323Ssamstatic int  wi_alloc_fid(struct wi_softc *, int, int *);
146109323Ssamstatic void wi_read_nicid(struct wi_softc *);
147109323Ssamstatic int  wi_write_ssid(struct wi_softc *, int, u_int8_t *, int);
14853702Swpaul
149192492Simpstatic int  wi_cmd(struct wi_softc *, int, int, int, int);
150109323Ssamstatic int  wi_seek_bap(struct wi_softc *, int, int);
151109323Ssamstatic int  wi_read_bap(struct wi_softc *, int, int, void *, int);
152287197Sglebiusstatic int  wi_write_bap(struct wi_softc *, int, int, const void *, int);
153109323Ssamstatic int  wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int);
154109323Ssamstatic int  wi_read_rid(struct wi_softc *, int, void *, int *);
155287197Sglebiusstatic int  wi_write_rid(struct wi_softc *, int, const void *, int);
156178354Ssamstatic int  wi_write_appie(struct wi_softc *, int, const struct ieee80211_appie *);
15777217Sphk
158170530Ssamstatic void wi_scan_start(struct ieee80211com *);
159170530Ssamstatic void wi_scan_end(struct ieee80211com *);
160170530Ssamstatic void wi_set_channel(struct ieee80211com *);
161170530Ssam
162109323Ssamstatic __inline int
163109323Ssamwi_write_val(struct wi_softc *sc, int rid, u_int16_t val)
164109323Ssam{
16553702Swpaul
166109323Ssam	val = htole16(val);
167109323Ssam	return wi_write_rid(sc, rid, &val, sizeof(val));
168109323Ssam}
169109323Ssam
170227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, wi, CTLFLAG_RD, 0,
171227309Sed	    "Wireless driver parameters");
172109592Ssam
173109323Ssamstatic	struct timeval lasttxerror;	/* time of last tx error msg */
174109323Ssamstatic	int curtxeps;			/* current tx error msgs/sec */
175111559Ssamstatic	int wi_txerate = 0;		/* tx error rate: max msgs/sec */
176109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate,
177111559Ssam	    0, "max tx error msgs/sec; 0 to disable msgs");
178109323Ssam
179109323Ssam#define	WI_DEBUG
180109323Ssam#ifdef WI_DEBUG
181109323Ssamstatic	int wi_debug = 0;
182109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug,
183109592Ssam	    0, "control debugging printfs");
184109323Ssam#define	DPRINTF(X)	if (wi_debug) printf X
185109323Ssam#else
186109323Ssam#define	DPRINTF(X)
187109323Ssam#endif
188109323Ssam
189109323Ssam#define WI_INTRS	(WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO)
190109323Ssam
19193825Simpstruct wi_card_ident wi_card_ident[] = {
19293825Simp	/* CARD_ID			CARD_NAME		FIRM_TYPE */
19393825Simp	{ WI_NIC_LUCENT_ID,		WI_NIC_LUCENT_STR,	WI_LUCENT },
19493825Simp	{ WI_NIC_SONY_ID,		WI_NIC_SONY_STR,	WI_LUCENT },
19593825Simp	{ WI_NIC_LUCENT_EMB_ID,		WI_NIC_LUCENT_EMB_STR,	WI_LUCENT },
19693825Simp	{ WI_NIC_EVB2_ID,		WI_NIC_EVB2_STR,	WI_INTERSIL },
19793825Simp	{ WI_NIC_HWB3763_ID,		WI_NIC_HWB3763_STR,	WI_INTERSIL },
19893825Simp	{ WI_NIC_HWB3163_ID,		WI_NIC_HWB3163_STR,	WI_INTERSIL },
19993825Simp	{ WI_NIC_HWB3163B_ID,		WI_NIC_HWB3163B_STR,	WI_INTERSIL },
20093825Simp	{ WI_NIC_EVB3_ID,		WI_NIC_EVB3_STR,	WI_INTERSIL },
20193825Simp	{ WI_NIC_HWB1153_ID,		WI_NIC_HWB1153_STR,	WI_INTERSIL },
20293825Simp	{ WI_NIC_P2_SST_ID,		WI_NIC_P2_SST_STR,	WI_INTERSIL },
20393825Simp	{ WI_NIC_EVB2_SST_ID,		WI_NIC_EVB2_SST_STR,	WI_INTERSIL },
20493825Simp	{ WI_NIC_3842_EVA_ID,		WI_NIC_3842_EVA_STR,	WI_INTERSIL },
20593825Simp	{ WI_NIC_3842_PCMCIA_AMD_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
20693825Simp	{ WI_NIC_3842_PCMCIA_SST_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
207101355Simp	{ WI_NIC_3842_PCMCIA_ATL_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
208101355Simp	{ WI_NIC_3842_PCMCIA_ATS_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
20993825Simp	{ WI_NIC_3842_MINI_AMD_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
21093825Simp	{ WI_NIC_3842_MINI_SST_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
211101355Simp	{ WI_NIC_3842_MINI_ATL_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
212101355Simp	{ WI_NIC_3842_MINI_ATS_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
21393825Simp	{ WI_NIC_3842_PCI_AMD_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
21493825Simp	{ WI_NIC_3842_PCI_SST_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
215101355Simp	{ WI_NIC_3842_PCI_ATS_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
216101355Simp	{ WI_NIC_3842_PCI_ATL_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
21793825Simp	{ WI_NIC_P3_PCMCIA_AMD_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
21893825Simp	{ WI_NIC_P3_PCMCIA_SST_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
219101355Simp	{ WI_NIC_P3_PCMCIA_ATL_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
220101355Simp	{ WI_NIC_P3_PCMCIA_ATS_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
22193825Simp	{ WI_NIC_P3_MINI_AMD_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
22293825Simp	{ WI_NIC_P3_MINI_SST_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
223101355Simp	{ WI_NIC_P3_MINI_ATL_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
224101355Simp	{ WI_NIC_P3_MINI_ATS_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
22593825Simp	{ 0,	NULL,	0 },
22693825Simp};
22793825Simp
228180919Simpstatic char *wi_firmware_names[] = { "none", "Hermes", "Intersil", "Symbol" };
229180919Simp
230109323Ssamdevclass_t wi_devclass;
23146492Swpaul
23293611Simpint
233109323Ssamwi_attach(device_t dev)
23474906Salfred{
235109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
236287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
237116951Ssam	int i, nrates, buflen;
238109323Ssam	u_int16_t val;
239109323Ssam	u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE];
240116951Ssam	struct ieee80211_rateset *rs;
241180919Simp	struct sysctl_ctx_list *sctx;
242180919Simp	struct sysctl_oid *soid;
243109323Ssam	static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
244109323Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
245109323Ssam	};
246109323Ssam	int error;
24774906Salfred
248138571Ssam	sc->sc_firmware_type = WI_NOTYPE;
249123339Simp	sc->wi_cmd_count = 500;
25046492Swpaul	/* Reset the NIC. */
251178354Ssam	if (wi_reset(sc) != 0) {
252178354Ssam		wi_free(dev);
253109323Ssam		return ENXIO;		/* XXX */
254178354Ssam	}
25546492Swpaul
256178354Ssam	/* Read NIC identification */
257178354Ssam	wi_read_nicid(sc);
258178354Ssam	switch (sc->sc_firmware_type) {
259178354Ssam	case WI_LUCENT:
260178354Ssam		if (sc->sc_sta_firmware_ver < 60006)
261178354Ssam			goto reject;
262178354Ssam		break;
263178354Ssam	case WI_INTERSIL:
264178354Ssam		if (sc->sc_sta_firmware_ver < 800)
265178354Ssam			goto reject;
266178354Ssam		break;
267178354Ssam	default:
268178354Ssam	reject:
269178354Ssam		device_printf(dev, "Sorry, this card is not supported "
270178354Ssam		    "(type %d, firmware ver %d)\n",
271178354Ssam		    sc->sc_firmware_type, sc->sc_sta_firmware_ver);
272178354Ssam		wi_free(dev);
273178354Ssam		return EOPNOTSUPP;
274178354Ssam	}
275178354Ssam
276180919Simp	/* Export info about the device via sysctl */
277180919Simp	sctx = device_get_sysctl_ctx(dev);
278180919Simp	soid = device_get_sysctl_tree(dev);
279180919Simp	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
280180919Simp	    "firmware_type", CTLFLAG_RD,
281180919Simp	    wi_firmware_names[sc->sc_firmware_type], 0,
282180919Simp	    "Firmware type string");
283180919Simp	SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "sta_version",
284180919Simp	    CTLFLAG_RD, &sc->sc_sta_firmware_ver, 0,
285180919Simp	    "Station Firmware version");
286180919Simp	if (sc->sc_firmware_type == WI_INTERSIL)
287180919Simp		SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
288180919Simp		    "pri_version", CTLFLAG_RD, &sc->sc_pri_firmware_ver, 0,
289180919Simp		    "Primary Firmware version");
290217586Smdf	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_id",
291180919Simp	    CTLFLAG_RD, &sc->sc_nic_id, 0, "NIC id");
292180919Simp	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_name",
293180919Simp	    CTLFLAG_RD, sc->sc_nic_name, 0, "NIC name");
294180919Simp
295178354Ssam	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
296178354Ssam	    MTX_DEF | MTX_RECURSE);
297178354Ssam	callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
298287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
299178354Ssam
30076438Swpaul	/*
30176438Swpaul	 * Read the station address.
30276438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
30376438Swpaul	 * an error when trying to read it the first time, which causes
30476438Swpaul	 * the probe to fail.
30576438Swpaul	 */
306109323Ssam	buflen = IEEE80211_ADDR_LEN;
307287197Sglebius	error = wi_read_rid(sc, WI_RID_MAC_NODE, &ic->ic_macaddr, &buflen);
308109323Ssam	if (error != 0) {
309109323Ssam		buflen = IEEE80211_ADDR_LEN;
310287197Sglebius		error = wi_read_rid(sc, WI_RID_MAC_NODE, &ic->ic_macaddr,
311287197Sglebius		    &buflen);
312109323Ssam	}
313287197Sglebius	if (error || IEEE80211_ADDR_EQ(&ic->ic_macaddr, empty_macaddr)) {
314109323Ssam		if (error != 0)
315109323Ssam			device_printf(dev, "mac read failed %d\n", error);
316148714Simp		else {
317109323Ssam			device_printf(dev, "mac read failed (all zeros)\n");
318148714Simp			error = ENXIO;
319148714Simp		}
32075149Simp		wi_free(dev);
32175149Simp		return (error);
32275149Simp	}
32346492Swpaul
324283537Sglebius	ic->ic_softc = sc;
325283527Sglebius	ic->ic_name = device_get_nameunit(dev);
326109323Ssam	ic->ic_phytype = IEEE80211_T_DS;
327109323Ssam	ic->ic_opmode = IEEE80211_M_STA;
328178957Ssam	ic->ic_caps = IEEE80211_C_STA
329178957Ssam		    | IEEE80211_C_PMGT
330178354Ssam		    | IEEE80211_C_MONITOR
331138571Ssam		    ;
33246492Swpaul
333116951Ssam	/*
334116951Ssam	 * Query the card for available channels and setup the
335116951Ssam	 * channel table.  We assume these are all 11b channels.
336116951Ssam	 */
337109323Ssam	buflen = sizeof(val);
338109323Ssam	if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0)
339109323Ssam		val = htole16(0x1fff);	/* assume 1-11 */
340116951Ssam	KASSERT(val != 0, ("wi_attach: no available channels listed!"));
341116951Ssam
342116951Ssam	val <<= 1;			/* shift for base 1 indices */
343116951Ssam	for (i = 1; i < 16; i++) {
344170530Ssam		struct ieee80211_channel *c;
345170530Ssam
346144986Smdodd		if (!isset((u_int8_t*)&val, i))
347144986Smdodd			continue;
348170530Ssam		c = &ic->ic_channels[ic->ic_nchans++];
349170530Ssam		c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
350170530Ssam		c->ic_flags = IEEE80211_CHAN_B;
351170530Ssam		c->ic_ieee = i;
352178354Ssam		/* XXX txpowers? */
353109323Ssam	}
35446492Swpaul
35546563Swpaul	/*
35698440Simp	 * Set flags based on firmware version.
35798440Simp	 */
35898440Simp	switch (sc->sc_firmware_type) {
35998440Simp	case WI_LUCENT:
360112363Simp		sc->sc_ntxbuf = 1;
361178354Ssam		ic->ic_caps |= IEEE80211_C_IBSS;
362119784Ssam
363178354Ssam		sc->sc_ibss_port = WI_PORTTYPE_BSS;
364178354Ssam		sc->sc_monitor_port = WI_PORTTYPE_ADHOC;
365119784Ssam		sc->sc_min_rssi = WI_LUCENT_MIN_RSSI;
366119784Ssam		sc->sc_max_rssi = WI_LUCENT_MAX_RSSI;
367119784Ssam		sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET;
36898440Simp		break;
36998440Simp	case WI_INTERSIL:
370112363Simp		sc->sc_ntxbuf = WI_NTXBUF;
371178354Ssam		sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR
372178354Ssam			     |  WI_FLAGS_HAS_ROAMING;
373123339Simp		/*
374123339Simp		 * Old firmware are slow, so give peace a chance.
375123339Simp		 */
376123339Simp		if (sc->sc_sta_firmware_ver < 10000)
377123339Simp			sc->wi_cmd_count = 5000;
378109323Ssam		if (sc->sc_sta_firmware_ver > 10101)
379109323Ssam			sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST;
380178354Ssam		ic->ic_caps |= IEEE80211_C_IBSS;
381109396Simp		/*
382109396Simp		 * version 0.8.3 and newer are the only ones that are known
383109396Simp		 * to currently work.  Earlier versions can be made to work,
384178354Ssam		 * at least according to the Linux driver but we require
385178354Ssam		 * monitor mode so this is irrelevant.
386109396Simp		 */
387178354Ssam		ic->ic_caps |= IEEE80211_C_HOSTAP;
388178354Ssam		if (sc->sc_sta_firmware_ver >= 10603)
389178354Ssam			sc->sc_flags |= WI_FLAGS_HAS_ENHSECURITY;
390178354Ssam		if (sc->sc_sta_firmware_ver >= 10700) {
391178354Ssam			/*
392178354Ssam			 * 1.7.0+ have the necessary support for sta mode WPA.
393178354Ssam			 */
394178354Ssam			sc->sc_flags |= WI_FLAGS_HAS_WPASUPPORT;
395178354Ssam			ic->ic_caps |= IEEE80211_C_WPA;
396178354Ssam		}
397119784Ssam
398178354Ssam		sc->sc_ibss_port = WI_PORTTYPE_IBSS;
399178354Ssam		sc->sc_monitor_port = WI_PORTTYPE_APSILENT;
400119784Ssam		sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
401119784Ssam		sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
402119784Ssam		sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
40398440Simp		break;
40498440Simp	}
40598440Simp
40698440Simp	/*
40756965Swpaul	 * Find out if we support WEP on this card.
40856965Swpaul	 */
409109323Ssam	buflen = sizeof(val);
410109323Ssam	if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 &&
411109323Ssam	    val != htole16(0))
412178354Ssam		ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
41356965Swpaul
414109323Ssam	/* Find supported rates. */
415109323Ssam	buflen = sizeof(ratebuf);
416116951Ssam	rs = &ic->ic_sup_rates[IEEE80211_MODE_11B];
417109323Ssam	if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) {
418116951Ssam		nrates = le16toh(*(u_int16_t *)ratebuf);
419116951Ssam		if (nrates > IEEE80211_RATE_MAXSIZE)
420116951Ssam			nrates = IEEE80211_RATE_MAXSIZE;
421116951Ssam		rs->rs_nrates = 0;
422116951Ssam		for (i = 0; i < nrates; i++)
423116951Ssam			if (ratebuf[2+i])
424116951Ssam				rs->rs_rates[rs->rs_nrates++] = ratebuf[2+i];
425109323Ssam	} else {
426109323Ssam		/* XXX fallback on error? */
42798440Simp	}
42877217Sphk
429109323Ssam	buflen = sizeof(val);
430109323Ssam	if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) &&
431109323Ssam	    wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) {
432119784Ssam		sc->sc_dbm_offset = le16toh(val);
433119784Ssam	}
43446492Swpaul
435109323Ssam	sc->sc_portnum = WI_DEFAULT_PORT;
43687383Simp
437287197Sglebius	ieee80211_ifattach(ic);
438160991Ssam	ic->ic_raw_xmit = wi_raw_xmit;
439170530Ssam	ic->ic_scan_start = wi_scan_start;
440170530Ssam	ic->ic_scan_end = wi_scan_end;
441170530Ssam	ic->ic_set_channel = wi_set_channel;
442178354Ssam	ic->ic_vap_create = wi_vap_create;
443178354Ssam	ic->ic_vap_delete = wi_vap_delete;
444178354Ssam	ic->ic_update_mcast = wi_update_mcast;
445192468Ssam	ic->ic_update_promisc = wi_update_promisc;
446287197Sglebius	ic->ic_transmit = wi_transmit;
447287197Sglebius	ic->ic_parent = wi_parent;
448109323Ssam
449192468Ssam	ieee80211_radiotap_attach(ic,
450192468Ssam	    &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
451192468Ssam		WI_TX_RADIOTAP_PRESENT,
452192468Ssam	    &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
453192468Ssam		WI_RX_RADIOTAP_PRESENT);
454119784Ssam
455138571Ssam	if (bootverbose)
456138571Ssam		ieee80211_announce(ic);
457138571Ssam
458180826Simp	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
459180826Simp	    NULL, wi_intr, sc, &sc->wi_intrhand);
460180826Simp	if (error) {
461180826Simp		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
462180826Simp		ieee80211_ifdetach(ic);
463180826Simp		wi_free(dev);
464180826Simp		return error;
465180826Simp	}
466180826Simp
467109323Ssam	return (0);
46887383Simp}
46987383Simp
470109323Ssamint
471109323Ssamwi_detach(device_t dev)
47246492Swpaul{
473109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
474287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
47546492Swpaul
476109323Ssam	WI_LOCK(sc);
47746492Swpaul
478109323Ssam	/* check if device was removed */
479123098Simp	sc->wi_gone |= !bus_child_present(dev);
48046492Swpaul
481287197Sglebius	wi_stop(sc, 0);
482150678Sru	WI_UNLOCK(sc);
483178354Ssam	ieee80211_ifdetach(ic);
48446492Swpaul
485109323Ssam	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
486109323Ssam	wi_free(dev);
487287197Sglebius	mbufq_drain(&sc->sc_snd);
488109323Ssam	mtx_destroy(&sc->sc_mtx);
489109323Ssam	return (0);
490109323Ssam}
49193359Simp
492178354Ssamstatic struct ieee80211vap *
493228621Sbschmidtwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
494228621Sbschmidt    enum ieee80211_opmode opmode, int flags,
495228621Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
496228621Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
497109323Ssam{
498286865Sadrian	struct wi_softc *sc = ic->ic_softc;
499178354Ssam	struct wi_vap *wvp;
500178354Ssam	struct ieee80211vap *vap;
50193359Simp
502178354Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
503178354Ssam		return NULL;
504287197Sglebius	wvp = malloc(sizeof(struct wi_vap), M_80211_VAP, M_WAITOK | M_ZERO);
50593359Simp
506178354Ssam	vap = &wvp->wv_vap;
507287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
50893359Simp
509178354Ssam	vap->iv_max_aid = WI_MAX_AID;
51093359Simp
511178354Ssam	switch (opmode) {
512178354Ssam	case IEEE80211_M_STA:
513178354Ssam		sc->sc_porttype = WI_PORTTYPE_BSS;
514178354Ssam		wvp->wv_newstate = vap->iv_newstate;
515178354Ssam		vap->iv_newstate = wi_newstate_sta;
516178354Ssam		/* need to filter mgt frames to avoid confusing state machine */
517178354Ssam		wvp->wv_recv_mgmt = vap->iv_recv_mgmt;
518178354Ssam		vap->iv_recv_mgmt = wi_recv_mgmt;
519109323Ssam		break;
520178354Ssam	case IEEE80211_M_IBSS:
521178354Ssam		sc->sc_porttype = sc->sc_ibss_port;
522178354Ssam		wvp->wv_newstate = vap->iv_newstate;
523178354Ssam		vap->iv_newstate = wi_newstate_sta;
524109323Ssam		break;
525178354Ssam	case IEEE80211_M_AHDEMO:
526178354Ssam		sc->sc_porttype = WI_PORTTYPE_ADHOC;
527109323Ssam		break;
528178354Ssam	case IEEE80211_M_HOSTAP:
529178354Ssam		sc->sc_porttype = WI_PORTTYPE_HOSTAP;
530178354Ssam		wvp->wv_newstate = vap->iv_newstate;
531178354Ssam		vap->iv_newstate = wi_newstate_hostap;
532178354Ssam		break;
533178354Ssam	case IEEE80211_M_MONITOR:
534178354Ssam		sc->sc_porttype = sc->sc_monitor_port;
535178354Ssam		break;
536178354Ssam	default:
537178354Ssam		break;
53893359Simp	}
539178354Ssam
540178354Ssam	/* complete setup */
541287197Sglebius	ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status, mac);
542178354Ssam	ic->ic_opmode = opmode;
543178354Ssam	return vap;
54446492Swpaul}
54546492Swpaul
546178354Ssamstatic void
547178354Ssamwi_vap_delete(struct ieee80211vap *vap)
548178354Ssam{
549178354Ssam	struct wi_vap *wvp = WI_VAP(vap);
550178354Ssam
551178354Ssam	ieee80211_vap_detach(vap);
552178354Ssam	free(wvp, M_80211_VAP);
553178354Ssam}
554178354Ssam
555194023Savgint
556109323Ssamwi_shutdown(device_t dev)
55746492Swpaul{
558109323Ssam	struct wi_softc *sc = device_get_softc(dev);
55946492Swpaul
560287197Sglebius	WI_LOCK(sc);
561178354Ssam	wi_stop(sc, 1);
562287197Sglebius	WI_UNLOCK(sc);
563194023Savg	return (0);
56446492Swpaul}
56546492Swpaul
566109323Ssamvoid
567109323Ssamwi_intr(void *arg)
56846492Swpaul{
569109323Ssam	struct wi_softc *sc = arg;
570112363Simp	u_int16_t status;
57146492Swpaul
572109323Ssam	WI_LOCK(sc);
57346492Swpaul
574287197Sglebius	if (sc->wi_gone || !sc->sc_enabled ||
575287197Sglebius	    (sc->sc_flags & WI_FLAGS_RUNNING) == 0) {
576113327Simp		CSR_WRITE_2(sc, WI_INT_EN, 0);
577123098Simp		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
578109323Ssam		WI_UNLOCK(sc);
57946492Swpaul		return;
580109323Ssam	}
58146492Swpaul
582112363Simp	/* Disable interrupts. */
583112363Simp	CSR_WRITE_2(sc, WI_INT_EN, 0);
58446492Swpaul
585112363Simp	status = CSR_READ_2(sc, WI_EVENT_STAT);
586112363Simp	if (status & WI_EV_RX)
587112363Simp		wi_rx_intr(sc);
588112363Simp	if (status & WI_EV_ALLOC)
589112363Simp		wi_tx_intr(sc);
590112363Simp	if (status & WI_EV_TX_EXC)
591112363Simp		wi_tx_ex_intr(sc);
592112363Simp	if (status & WI_EV_INFO)
593112363Simp		wi_info_intr(sc);
594287197Sglebius	if (mbufq_first(&sc->sc_snd) != NULL)
595287197Sglebius		wi_start(sc);
59646492Swpaul
597112363Simp	/* Re-enable interrupts. */
598112363Simp	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
59946492Swpaul
600109323Ssam	WI_UNLOCK(sc);
60146492Swpaul
60246492Swpaul	return;
60346492Swpaul}
60446492Swpaul
605178354Ssamstatic void
606178354Ssamwi_enable(struct wi_softc *sc)
607178354Ssam{
608178354Ssam	/* Enable interrupts */
609178354Ssam	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
610178354Ssam
611178354Ssam	/* enable port */
612178354Ssam	wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
613178354Ssam	sc->sc_enabled = 1;
614178354Ssam}
615178354Ssam
616178354Ssamstatic int
617178354Ssamwi_setup_locked(struct wi_softc *sc, int porttype, int mode,
618287197Sglebius	const uint8_t mac[IEEE80211_ADDR_LEN])
619178354Ssam{
620178354Ssam	int i;
621178354Ssam
622178354Ssam	wi_reset(sc);
623178354Ssam
624178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, porttype);
625178354Ssam	wi_write_val(sc, WI_RID_CREATE_IBSS, mode);
626178354Ssam	wi_write_val(sc, WI_RID_MAX_DATALEN, 2304);
627178354Ssam	/* XXX IEEE80211_BPF_NOACK wants 0 */
628178354Ssam	wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 2);
629178354Ssam	if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
630178354Ssam		wi_write_val(sc, WI_RID_ROAMING_MODE, 3); /* NB: disabled */
631178354Ssam
632178354Ssam	wi_write_rid(sc, WI_RID_MAC_NODE, mac, IEEE80211_ADDR_LEN);
633178354Ssam
634178354Ssam	/* Allocate fids for the card */
635178354Ssam	sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
636178354Ssam	for (i = 0; i < sc->sc_ntxbuf; i++) {
637178354Ssam		int error = wi_alloc_fid(sc, sc->sc_buflen,
638178354Ssam		    &sc->sc_txd[i].d_fid);
639178354Ssam		if (error) {
640178354Ssam			device_printf(sc->sc_dev,
641178354Ssam			    "tx buffer allocation failed (error %u)\n",
642178354Ssam			    error);
643178354Ssam			return error;
644178354Ssam		}
645178354Ssam		sc->sc_txd[i].d_len = 0;
646178354Ssam	}
647178354Ssam	sc->sc_txcur = sc->sc_txnext = 0;
648178354Ssam
649178354Ssam	return 0;
650178354Ssam}
651178354Ssam
652287197Sglebiusvoid
653287197Sglebiuswi_init(struct wi_softc *sc)
654178354Ssam{
655178354Ssam	int wasenabled;
656178354Ssam
657178354Ssam	WI_LOCK_ASSERT(sc);
658178354Ssam
659178354Ssam	wasenabled = sc->sc_enabled;
660178354Ssam	if (wasenabled)
661287197Sglebius		wi_stop(sc, 1);
662178354Ssam
663287197Sglebius	if (wi_setup_locked(sc, sc->sc_porttype, 3,
664287197Sglebius	    sc->sc_ic.ic_macaddr) != 0) {
665287197Sglebius		device_printf(sc->sc_dev, "interface not running\n");
666287197Sglebius		wi_stop(sc, 1);
667178354Ssam		return;
668178354Ssam	}
669178354Ssam
670287197Sglebius	sc->sc_flags |= WI_FLAGS_RUNNING;
671178354Ssam
672178354Ssam	callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
673178354Ssam
674178354Ssam	wi_enable(sc);			/* Enable desired port */
675178354Ssam}
676178354Ssam
677109323Ssamvoid
678287197Sglebiuswi_stop(struct wi_softc *sc, int disable)
67946492Swpaul{
68046492Swpaul
681178354Ssam	WI_LOCK_ASSERT(sc);
68246492Swpaul
683178354Ssam	if (sc->sc_enabled && !sc->wi_gone) {
684178354Ssam		CSR_WRITE_2(sc, WI_INT_EN, 0);
685178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
686178354Ssam		if (disable)
687178354Ssam			sc->sc_enabled = 0;
688178354Ssam	} else if (sc->wi_gone && disable)	/* gone --> not enabled */
689178354Ssam		sc->sc_enabled = 0;
690178354Ssam
691178354Ssam	callout_stop(&sc->sc_watchdog);
692178354Ssam	sc->sc_tx_timer = 0;
693178354Ssam	sc->sc_false_syns = 0;
694178354Ssam
695287197Sglebius	sc->sc_flags &= ~WI_FLAGS_RUNNING;
696178354Ssam}
697178354Ssam
698178354Ssamstatic void
699178354Ssamwi_set_channel(struct ieee80211com *ic)
700178354Ssam{
701286865Sadrian	struct wi_softc *sc = ic->ic_softc;
702160991Ssam
703178354Ssam	DPRINTF(("%s: channel %d, %sscanning\n", __func__,
704178354Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan),
705178354Ssam	    ic->ic_flags & IEEE80211_F_SCAN ? "" : "!"));
70646492Swpaul
707178354Ssam	WI_LOCK(sc);
708116951Ssam	wi_write_val(sc, WI_RID_OWN_CHNL,
709178354Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan));
710178354Ssam	WI_UNLOCK(sc);
711178354Ssam}
71275373Salfred
713178354Ssamstatic void
714178354Ssamwi_scan_start(struct ieee80211com *ic)
715178354Ssam{
716286865Sadrian	struct wi_softc *sc = ic->ic_softc;
717178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
71846492Swpaul
719178354Ssam	DPRINTF(("%s\n", __func__));
72046492Swpaul
721178354Ssam	WI_LOCK(sc);
722109323Ssam	/*
723178354Ssam	 * Switch device to monitor mode.
724109323Ssam	 */
725178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_monitor_port);
726178354Ssam	if (sc->sc_firmware_type == WI_INTERSIL) {
727178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
728178354Ssam		wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
72975373Salfred	}
730178354Ssam	/* force full dwell time to compensate for firmware overhead */
731178354Ssam	ss->ss_mindwell = ss->ss_maxdwell = msecs_to_ticks(400);
732178354Ssam	WI_UNLOCK(sc);
73346492Swpaul
734178354Ssam}
73567092Swpaul
736178354Ssamstatic void
737178354Ssamwi_scan_end(struct ieee80211com *ic)
738178354Ssam{
739286865Sadrian	struct wi_softc *sc = ic->ic_softc;
74046492Swpaul
741178354Ssam	DPRINTF(("%s: restore port type %d\n", __func__, sc->sc_porttype));
742178354Ssam
743178354Ssam	WI_LOCK(sc);
744178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_porttype);
745178354Ssam	if (sc->sc_firmware_type == WI_INTERSIL) {
746178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
747178354Ssam		wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
74870073Swpaul	}
749178354Ssam	WI_UNLOCK(sc);
750178354Ssam}
75170073Swpaul
752178354Ssamstatic void
753178354Ssamwi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
754283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
755178354Ssam{
756178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
75746492Swpaul
758178354Ssam	switch (subtype) {
759178354Ssam	case IEEE80211_FC0_SUBTYPE_AUTH:
760178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
761178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
762178354Ssam		/* NB: filter frames that trigger state changes */
763178354Ssam		return;
764170530Ssam	}
765283535Sadrian	WI_VAP(vap)->wv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
766178354Ssam}
76746492Swpaul
768178354Ssamstatic int
769178354Ssamwi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
770178354Ssam{
771178354Ssam	struct ieee80211com *ic = vap->iv_ic;
772178354Ssam	struct ieee80211_node *bss;
773286865Sadrian	struct wi_softc *sc = ic->ic_softc;
77494397Simp
775178354Ssam	DPRINTF(("%s: %s -> %s\n", __func__,
776178354Ssam		ieee80211_state_name[vap->iv_state],
777178354Ssam		ieee80211_state_name[nstate]));
778178354Ssam
779178354Ssam	if (nstate == IEEE80211_S_AUTH) {
780178354Ssam		WI_LOCK(sc);
781178354Ssam		wi_setup_locked(sc, WI_PORTTYPE_BSS, 3, vap->iv_myaddr);
782178354Ssam
783178354Ssam		if (vap->iv_flags & IEEE80211_F_PMGTON) {
784178354Ssam			wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
785178354Ssam			wi_write_val(sc, WI_RID_PM_ENABLED, 1);
786178354Ssam		}
787178354Ssam		wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
788178354Ssam		if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
789178354Ssam			wi_write_val(sc, WI_RID_FRAG_THRESH,
790178354Ssam			    vap->iv_fragthreshold);
791178354Ssam		wi_write_txrate(sc, vap);
792178354Ssam
793178354Ssam		bss = vap->iv_bss;
794178354Ssam		wi_write_ssid(sc, WI_RID_DESIRED_SSID, bss->ni_essid, bss->ni_esslen);
795178354Ssam		wi_write_val(sc, WI_RID_OWN_CHNL,
796178354Ssam		    ieee80211_chan2ieee(ic, bss->ni_chan));
797178354Ssam
798178354Ssam		/* Configure WEP. */
799178354Ssam		if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
800178354Ssam			wi_write_wep(sc, vap);
801178354Ssam		else
802178354Ssam			sc->sc_encryption = 0;
803178354Ssam
804178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
805178354Ssam		    (vap->iv_flags & IEEE80211_F_WPA)) {
806178354Ssam			wi_write_val(sc, WI_RID_WPA_HANDLING, 1);
807178354Ssam			if (vap->iv_appie_wpa != NULL)
808178354Ssam				wi_write_appie(sc, WI_RID_WPA_DATA,
809178354Ssam				    vap->iv_appie_wpa);
810178354Ssam		}
811178354Ssam
812178354Ssam		wi_enable(sc);		/* enable port */
813178354Ssam
814178354Ssam		/* Lucent firmware does not support the JOIN RID. */
815178354Ssam		if (sc->sc_firmware_type == WI_INTERSIL) {
816178354Ssam			struct wi_joinreq join;
817178354Ssam
818178354Ssam			memset(&join, 0, sizeof(join));
819178354Ssam			IEEE80211_ADDR_COPY(&join.wi_bssid, bss->ni_bssid);
820116951Ssam			join.wi_chan = htole16(
821178354Ssam			    ieee80211_chan2ieee(ic, bss->ni_chan));
822109323Ssam			wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
823178354Ssam		}
824178354Ssam		WI_UNLOCK(sc);
82575373Salfred
826178354Ssam		/*
827178354Ssam		 * NB: don't go through 802.11 layer, it'll send auth frame;
828178354Ssam		 * instead we drive the state machine from the link status
829178354Ssam		 * notification we get on association.
830178354Ssam		 */
831178354Ssam		vap->iv_state = nstate;
832191746Sthompsa		return (0);
83370073Swpaul	}
834178354Ssam	return WI_VAP(vap)->wv_newstate(vap, nstate, arg);
83546492Swpaul}
83646492Swpaul
837178354Ssamstatic int
838178354Ssamwi_newstate_hostap(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
83946492Swpaul{
840178354Ssam	struct ieee80211com *ic = vap->iv_ic;
841178354Ssam	struct ieee80211_node *bss;
842286865Sadrian	struct wi_softc *sc = ic->ic_softc;
843178354Ssam	int error;
84446492Swpaul
845178354Ssam	DPRINTF(("%s: %s -> %s\n", __func__,
846178354Ssam		ieee80211_state_name[vap->iv_state],
847178354Ssam		ieee80211_state_name[nstate]));
84874998Swpaul
849178354Ssam	error = WI_VAP(vap)->wv_newstate(vap, nstate, arg);
850178354Ssam	if (error == 0 && nstate == IEEE80211_S_RUN) {
851178354Ssam		WI_LOCK(sc);
852178354Ssam		wi_setup_locked(sc, WI_PORTTYPE_HOSTAP, 0, vap->iv_myaddr);
853178354Ssam
854178354Ssam		bss = vap->iv_bss;
855178354Ssam		wi_write_ssid(sc, WI_RID_OWN_SSID,
856178354Ssam		    bss->ni_essid, bss->ni_esslen);
857178354Ssam		wi_write_val(sc, WI_RID_OWN_CHNL,
858178354Ssam		    ieee80211_chan2ieee(ic, bss->ni_chan));
859178354Ssam		wi_write_val(sc, WI_RID_BASIC_RATE, 0x3);
860178354Ssam		wi_write_val(sc, WI_RID_SUPPORT_RATE, 0xf);
861178354Ssam		wi_write_txrate(sc, vap);
862178354Ssam
863178354Ssam		wi_write_val(sc, WI_RID_OWN_BEACON_INT, bss->ni_intval);
864178354Ssam		wi_write_val(sc, WI_RID_DTIM_PERIOD, vap->iv_dtim_period);
865178354Ssam
866178354Ssam		wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
867178354Ssam		if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
868178354Ssam			wi_write_val(sc, WI_RID_FRAG_THRESH,
869178354Ssam			    vap->iv_fragthreshold);
870178354Ssam
871178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_ENHSECURITY) &&
872178354Ssam		    (vap->iv_flags & IEEE80211_F_HIDESSID)) {
873178354Ssam			/*
874178354Ssam			 * bit 0 means hide SSID in beacons,
875178354Ssam			 * bit 1 means don't respond to bcast probe req
876178354Ssam			 */
877178354Ssam			wi_write_val(sc, WI_RID_ENH_SECURITY, 0x3);
87870073Swpaul		}
87970073Swpaul
880178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
881178354Ssam		    (vap->iv_flags & IEEE80211_F_WPA) &&
882178354Ssam		    vap->iv_appie_wpa != NULL)
883178354Ssam			wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa);
88446492Swpaul
885178354Ssam		wi_write_val(sc, WI_RID_PROMISC, 0);
886178354Ssam
887178354Ssam		/* Configure WEP. */
888178354Ssam		if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
889178354Ssam			wi_write_wep(sc, vap);
890178354Ssam		else
891178354Ssam			sc->sc_encryption = 0;
892178354Ssam
893178354Ssam		wi_enable(sc);		/* enable port */
894178354Ssam		WI_UNLOCK(sc);
895178354Ssam	}
896178354Ssam	return error;
89746492Swpaul}
89846492Swpaul
899287197Sglebiusstatic int
900287197Sglebiuswi_transmit(struct ieee80211com *ic, struct mbuf *m)
901287197Sglebius{
902287197Sglebius	struct wi_softc *sc = ic->ic_softc;
903287197Sglebius	int error;
904287197Sglebius
905287197Sglebius	WI_LOCK(sc);
906287197Sglebius	if ((sc->sc_flags & WI_FLAGS_RUNNING) == 0) {
907287197Sglebius		WI_UNLOCK(sc);
908287197Sglebius		return (ENXIO);
909287197Sglebius	}
910287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
911287197Sglebius	if (error) {
912287197Sglebius		WI_UNLOCK(sc);
913287197Sglebius		return (error);
914287197Sglebius	}
915287197Sglebius	wi_start(sc);
916287197Sglebius	WI_UNLOCK(sc);
917287197Sglebius	return (0);
918287197Sglebius}
919287197Sglebius
920109323Ssamstatic void
921287197Sglebiuswi_start(struct wi_softc *sc)
92246492Swpaul{
923119150Ssam	struct ieee80211_node *ni;
924109323Ssam	struct ieee80211_frame *wh;
925109323Ssam	struct mbuf *m0;
926178354Ssam	struct ieee80211_key *k;
927109323Ssam	struct wi_frame frmhdr;
928190579Ssam	const struct llc *llc;
929160991Ssam	int cur;
93046492Swpaul
931165087Ssam	WI_LOCK_ASSERT(sc);
93246492Swpaul
933165087Ssam	if (sc->wi_gone)
934109323Ssam		return;
93546492Swpaul
936109323Ssam	memset(&frmhdr, 0, sizeof(frmhdr));
937109323Ssam	cur = sc->sc_txnext;
938287197Sglebius	while (sc->sc_txd[cur].d_len == 0 &&
939287197Sglebius	    (m0 = mbufq_dequeue(&sc->sc_snd)) != NULL) {
940178354Ssam		ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
94146492Swpaul
942190579Ssam		/* reconstruct 802.3 header */
943178354Ssam		wh = mtod(m0, struct ieee80211_frame *);
944190579Ssam		switch (wh->i_fc[1]) {
945190579Ssam		case IEEE80211_FC1_DIR_TODS:
946190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
947190579Ssam			    wh->i_addr2);
948190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
949190579Ssam			    wh->i_addr3);
950190579Ssam			break;
951190579Ssam		case IEEE80211_FC1_DIR_NODS:
952190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
953190579Ssam			    wh->i_addr2);
954190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
955190579Ssam			    wh->i_addr1);
956190579Ssam			break;
957190579Ssam		case IEEE80211_FC1_DIR_FROMDS:
958190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
959190579Ssam			    wh->i_addr3);
960190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
961190579Ssam			    wh->i_addr1);
962190579Ssam			break;
963190579Ssam		}
964190579Ssam		llc = (const struct llc *)(
965190579Ssam		    mtod(m0, const uint8_t *) + ieee80211_hdrsize(wh));
966190579Ssam		frmhdr.wi_ehdr.ether_type = llc->llc_snap.ether_type;
967109323Ssam		frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
968260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
969178354Ssam			k = ieee80211_crypto_encap(ni, m0);
970138571Ssam			if (k == NULL) {
971170530Ssam				ieee80211_free_node(ni);
972143299Ssam				m_freem(m0);
973109323Ssam				continue;
974109323Ssam			}
975138952Ssam			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
976109323Ssam		}
977178354Ssam
978192468Ssam		if (ieee80211_radiotap_active_vap(ni->ni_vap)) {
979178354Ssam			sc->sc_tx_th.wt_rate = ni->ni_txrate;
980192468Ssam			ieee80211_radiotap_tx(ni->ni_vap, m0);
981109323Ssam		}
982178354Ssam
983127697Ssam		m_copydata(m0, 0, sizeof(struct ieee80211_frame),
984127697Ssam		    (caddr_t)&frmhdr.wi_whdr);
985127697Ssam		m_adj(m0, sizeof(struct ieee80211_frame));
986127697Ssam		frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
987170530Ssam		ieee80211_free_node(ni);
988287197Sglebius		if (wi_start_tx(sc, &frmhdr, m0))
989109323Ssam			continue;
990178354Ssam
991160991Ssam		sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
992160991Ssam	}
993165087Ssam}
994160991Ssam
995160991Ssamstatic int
996287197Sglebiuswi_start_tx(struct wi_softc *sc, struct wi_frame *frmhdr, struct mbuf *m0)
997160991Ssam{
998160991Ssam	int cur = sc->sc_txnext;
999160991Ssam	int fid, off, error;
1000160991Ssam
1001160991Ssam	fid = sc->sc_txd[cur].d_fid;
1002160991Ssam	off = sizeof(*frmhdr);
1003160991Ssam	error = wi_write_bap(sc, fid, 0, frmhdr, sizeof(*frmhdr)) != 0
1004160991Ssam	     || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0;
1005160991Ssam	m_freem(m0);
1006160991Ssam	if (error) {
1007287197Sglebius		counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1008160991Ssam		return -1;
1009160991Ssam	}
1010160991Ssam	sc->sc_txd[cur].d_len = off;
1011160991Ssam	if (sc->sc_txcur == cur) {
1012160991Ssam		if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) {
1013287197Sglebius			device_printf(sc->sc_dev, "xmit failed\n");
1014160991Ssam			sc->sc_txd[cur].d_len = 0;
1015160991Ssam			return -1;
1016109323Ssam		}
1017160991Ssam		sc->sc_tx_timer = 5;
1018160991Ssam	}
1019160991Ssam	return 0;
1020160991Ssam}
1021160991Ssam
1022160991Ssamstatic int
1023160991Ssamwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
1024160991Ssam	    const struct ieee80211_bpf_params *params)
1025160991Ssam{
1026160991Ssam	struct ieee80211com *ic = ni->ni_ic;
1027192468Ssam	struct ieee80211vap *vap = ni->ni_vap;
1028286865Sadrian	struct wi_softc	*sc = ic->ic_softc;
1029178354Ssam	struct ieee80211_key *k;
1030160991Ssam	struct ieee80211_frame *wh;
1031160991Ssam	struct wi_frame frmhdr;
1032160991Ssam	int cur;
1033160991Ssam	int rc = 0;
1034160991Ssam
1035160991Ssam	WI_LOCK(sc);
1036160991Ssam
1037160991Ssam	if (sc->wi_gone) {
1038160991Ssam		rc = ENETDOWN;
1039160991Ssam		goto out;
1040160991Ssam	}
1041160991Ssam	memset(&frmhdr, 0, sizeof(frmhdr));
1042160991Ssam	cur = sc->sc_txnext;
1043160991Ssam	if (sc->sc_txd[cur].d_len != 0) {
1044160991Ssam		rc = ENOBUFS;
1045160991Ssam		goto out;
1046160991Ssam	}
1047160991Ssam	m0->m_pkthdr.rcvif = NULL;
1048160991Ssam
1049160991Ssam	m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
1050160991Ssam	    (caddr_t)&frmhdr.wi_ehdr);
1051160991Ssam	frmhdr.wi_ehdr.ether_type = 0;
1052160991Ssam	wh = mtod(m0, struct ieee80211_frame *);
1053160991Ssam
1054160991Ssam	frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
1055160991Ssam	if (params && (params->ibp_flags & IEEE80211_BPF_NOACK))
1056160991Ssam		frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
1057260444Skevlo	if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
1058178354Ssam	    (!params || (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO)))) {
1059178354Ssam		k = ieee80211_crypto_encap(ni, m0);
1060178354Ssam		if (k == NULL) {
1061178354Ssam			rc = ENOMEM;
1062178354Ssam			goto out;
1063109323Ssam		}
1064178354Ssam		frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
106574838Salfred	}
1066192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
1067178354Ssam		sc->sc_tx_th.wt_rate = ni->ni_txrate;
1068192468Ssam		ieee80211_radiotap_tx(vap, m0);
1069160991Ssam	}
1070160991Ssam	m_copydata(m0, 0, sizeof(struct ieee80211_frame),
1071160991Ssam	    (caddr_t)&frmhdr.wi_whdr);
1072160991Ssam	m_adj(m0, sizeof(struct ieee80211_frame));
1073160991Ssam	frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
1074287197Sglebius	if (wi_start_tx(sc, &frmhdr, m0) < 0) {
1075168860Ssephe		m0 = NULL;
1076168860Ssephe		rc = EIO;
1077160991Ssam		goto out;
1078168860Ssephe	}
1079168860Ssephe	m0 = NULL;
108046492Swpaul
1081160991Ssam	sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
1082160991Ssamout:
1083109323Ssam	WI_UNLOCK(sc);
1084160991Ssam
1085168860Ssephe	if (m0 != NULL)
1086168860Ssephe		m_freem(m0);
1087168860Ssephe	ieee80211_free_node(ni);
1088160991Ssam	return rc;
108946492Swpaul}
109046492Swpaul
109188546Salfredstatic int
1092178354Ssamwi_reset(struct wi_softc *sc)
109346492Swpaul{
1094114124Simp#define WI_INIT_TRIES 3
1095178354Ssam	int i, error = 0;
1096112362Simp
1097178354Ssam	for (i = 0; i < WI_INIT_TRIES; i++) {
1098178354Ssam		error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0);
1099178354Ssam		if (error == 0)
110046492Swpaul			break;
1101109323Ssam		DELAY(WI_DELAY * 1000);
110246492Swpaul	}
1103114124Simp	sc->sc_reset = 1;
1104178354Ssam	if (i == WI_INIT_TRIES) {
1105287197Sglebius		device_printf(sc->sc_dev, "reset failed\n");
1106178354Ssam		return error;
110775373Salfred	}
110846492Swpaul
1109109323Ssam	CSR_WRITE_2(sc, WI_INT_EN, 0);
1110114124Simp	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
111146492Swpaul
1112109323Ssam	/* Calibrate timer. */
1113114124Simp	wi_write_val(sc, WI_RID_TICK_TIME, 8);
1114114124Simp
1115178354Ssam	return 0;
1116109323Ssam#undef WI_INIT_TRIES
111746492Swpaul}
111846492Swpaul
111988546Salfredstatic void
1120165089Ssamwi_watchdog(void *arg)
112146492Swpaul{
1122165089Ssam	struct wi_softc	*sc = arg;
112346492Swpaul
1124178354Ssam	WI_LOCK_ASSERT(sc);
1125178354Ssam
1126109323Ssam	if (!sc->sc_enabled)
1127109323Ssam		return;
112846492Swpaul
1129178354Ssam	if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) {
1130287197Sglebius		device_printf(sc->sc_dev, "device timeout\n");
1131287197Sglebius		counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1132287197Sglebius		wi_init(sc);
1133178354Ssam		return;
113446492Swpaul	}
1135165089Ssam	callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
113646492Swpaul}
113746492Swpaul
1138287197Sglebiusstatic void
1139287197Sglebiuswi_parent(struct ieee80211com *ic)
114046492Swpaul{
1141287197Sglebius	struct wi_softc *sc = ic->ic_softc;
1142287197Sglebius	int startall = 0;
114346492Swpaul
1144287197Sglebius	WI_LOCK(sc);
1145287197Sglebius	/*
1146287197Sglebius	 * Can't do promisc and hostap at the same time.  If all that's
1147287197Sglebius	 * changing is the promisc flag, try to short-circuit a call to
1148287197Sglebius	 * wi_init() by just setting PROMISC in the hardware.
1149287197Sglebius	 */
1150287197Sglebius	if (ic->ic_nrunning > 0) {
1151287197Sglebius		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
1152287197Sglebius		    sc->sc_flags & WI_FLAGS_RUNNING) {
1153287197Sglebius			if (ic->ic_promisc > 0 &&
1154287197Sglebius			    (sc->sc_flags & WI_FLAGS_PROMISC) == 0) {
1155287197Sglebius				wi_write_val(sc, WI_RID_PROMISC, 1);
1156287197Sglebius				sc->sc_flags |= WI_FLAGS_PROMISC;
1157287197Sglebius			} else if (ic->ic_promisc == 0 &&
1158287197Sglebius			    (sc->sc_flags & WI_FLAGS_PROMISC) != 0) {
1159287197Sglebius				wi_write_val(sc, WI_RID_PROMISC, 0);
1160287197Sglebius				sc->sc_flags &= ~WI_FLAGS_PROMISC;
1161100876Simp			} else {
1162287197Sglebius				wi_init(sc);
1163178354Ssam				startall = 1;
1164100876Simp			}
116546492Swpaul		} else {
1166287197Sglebius			wi_init(sc);
1167287197Sglebius			startall = 1;
116846492Swpaul		}
1169287197Sglebius	} else if (sc->sc_flags & WI_FLAGS_RUNNING) {
1170287197Sglebius		wi_stop(sc, 1);
1171287197Sglebius		sc->wi_gone = 0;
1172170530Ssam	}
1173287197Sglebius	WI_UNLOCK(sc);
1174287197Sglebius	if (startall)
1175287197Sglebius		ieee80211_start_all(ic);
1176109323Ssam}
117746492Swpaul
1178109323Ssamstatic void
1179109323Ssamwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1180109323Ssam{
1181178354Ssam	struct ieee80211vap *vap = ifp->if_softc;
1182178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1183286865Sadrian	struct wi_softc *sc = ic->ic_softc;
1184109323Ssam	u_int16_t val;
1185109323Ssam	int rate, len;
118646492Swpaul
1187109323Ssam	len = sizeof(val);
1188178354Ssam	if (sc->sc_enabled &&
1189178354Ssam	    wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
1190138571Ssam	    len == sizeof(val)) {
1191109323Ssam		/* convert to 802.11 rate */
1192138571Ssam		val = le16toh(val);
1193109323Ssam		rate = val * 2;
1194109323Ssam		if (sc->sc_firmware_type == WI_LUCENT) {
1195138571Ssam			if (rate == 10)
1196109323Ssam				rate = 11;	/* 5.5Mbps */
1197109323Ssam		} else {
1198109323Ssam			if (rate == 4*2)
1199109323Ssam				rate = 11;	/* 5.5Mbps */
1200109323Ssam			else if (rate == 8*2)
1201109323Ssam				rate = 22;	/* 11Mbps */
1202109323Ssam		}
1203178354Ssam		vap->iv_bss->ni_txrate = rate;
1204109323Ssam	}
1205178354Ssam	ieee80211_media_status(ifp, imr);
1206109323Ssam}
120746492Swpaul
1208109323Ssamstatic void
1209109323Ssamwi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
1210109323Ssam{
1211287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1212178354Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1213178354Ssam	struct ieee80211_node *ni = vap->iv_bss;
121498440Simp
1215109323Ssam	if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
1216109323Ssam		return;
121746492Swpaul
1218109323Ssam	DPRINTF(("wi_sync_bssid: bssid %s -> ", ether_sprintf(ni->ni_bssid)));
1219109323Ssam	DPRINTF(("%s ?\n", ether_sprintf(new_bssid)));
122046492Swpaul
1221109323Ssam	/* In promiscuous mode, the BSSID field is not a reliable
1222109323Ssam	 * indicator of the firmware's BSSID. Damp spurious
1223109323Ssam	 * change-of-BSSID indications.
1224109323Ssam	 */
1225287197Sglebius	if (ic->ic_promisc > 0 &&
1226138571Ssam	    !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns,
1227138571Ssam	                 WI_MAX_FALSE_SYNS))
1228109323Ssam		return;
122946492Swpaul
1230139542Ssam	sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1);
1231170530Ssam#if 0
1232139542Ssam	/*
1233139542Ssam	 * XXX hack; we should create a new node with the new bssid
1234139542Ssam	 * and replace the existing ic_bss with it but since we don't
1235139542Ssam	 * process management frames to collect state we cheat by
1236139542Ssam	 * reusing the existing node as we know wi_newstate will be
1237139542Ssam	 * called and it will overwrite the node state.
1238139542Ssam	 */
1239139542Ssam	ieee80211_sta_join(ic, ieee80211_ref_node(ni));
1240170530Ssam#endif
1241109323Ssam}
124246492Swpaul
1243178354Ssamstatic __noinline void
1244109323Ssamwi_rx_intr(struct wi_softc *sc)
1245109323Ssam{
1246287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1247109323Ssam	struct wi_frame frmhdr;
1248109323Ssam	struct mbuf *m;
1249109323Ssam	struct ieee80211_frame *wh;
1250119150Ssam	struct ieee80211_node *ni;
1251192468Ssam	int fid, len, off;
1252109323Ssam	u_int8_t dir;
1253109323Ssam	u_int16_t status;
1254192468Ssam	int8_t rssi, nf;
125546611Swpaul
1256109323Ssam	fid = CSR_READ_2(sc, WI_RX_FID);
125746611Swpaul
1258109323Ssam	/* First read in the frame header */
1259109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) {
1260109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1261287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1262109323Ssam		DPRINTF(("wi_rx_intr: read fid %x failed\n", fid));
1263109323Ssam		return;
1264109323Ssam	}
126591695Simp
1266109323Ssam	/*
1267109323Ssam	 * Drop undecryptable or packets with receive errors here
1268109323Ssam	 */
1269109323Ssam	status = le16toh(frmhdr.wi_status);
1270109323Ssam	if (status & WI_STAT_ERRSTAT) {
1271109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1272287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1273109323Ssam		DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status));
1274109323Ssam		return;
1275109323Ssam	}
127646492Swpaul
1277109323Ssam	len = le16toh(frmhdr.wi_dat_len);
1278109323Ssam	off = ALIGN(sizeof(struct ieee80211_frame));
127946563Swpaul
1280117855Ssam	/*
1281117855Ssam	 * Sometimes the PRISM2.x returns bogusly large frames. Except
1282117855Ssam	 * in monitor mode, just throw them away.
1283117855Ssam	 */
1284117855Ssam	if (off + len > MCLBYTES) {
1285117855Ssam		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1286117855Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1287287197Sglebius			counter_u64_add(ic->ic_ierrors, 1);
1288117855Ssam			DPRINTF(("wi_rx_intr: oversized packet\n"));
1289117855Ssam			return;
1290117855Ssam		} else
1291117855Ssam			len = 0;
1292117855Ssam	}
1293117855Ssam
1294178354Ssam	if (off + len > MHLEN)
1295243857Sglebius		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1296178354Ssam	else
1297243857Sglebius		m = m_gethdr(M_NOWAIT, MT_DATA);
1298109323Ssam	if (m == NULL) {
1299109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1300287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1301109323Ssam		DPRINTF(("wi_rx_intr: MGET failed\n"));
1302109323Ssam		return;
1303109323Ssam	}
1304109323Ssam	m->m_data += off - sizeof(struct ieee80211_frame);
1305109323Ssam	memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame));
1306109323Ssam	wi_read_bap(sc, fid, sizeof(frmhdr),
1307109323Ssam	    m->m_data + sizeof(struct ieee80211_frame), len);
1308109323Ssam	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len;
130994405Simp
1310109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
131146492Swpaul
1312192468Ssam	rssi = frmhdr.wi_rx_signal;
1313192468Ssam	nf = frmhdr.wi_rx_silence;
1314192468Ssam	if (ieee80211_radiotap_active(ic)) {
1315192468Ssam		struct wi_rx_radiotap_header *tap = &sc->sc_rx_th;
1316192468Ssam		uint32_t rstamp;
1317192468Ssam
1318192468Ssam		rstamp = (le16toh(frmhdr.wi_rx_tstamp0) << 16) |
1319192468Ssam		    le16toh(frmhdr.wi_rx_tstamp1);
1320192517Ssam		tap->wr_tsf = htole64((uint64_t)rstamp);
1321123927Ssam		/* XXX replace divide by table */
1322192468Ssam		tap->wr_rate = frmhdr.wi_rx_rate / 5;
1323192468Ssam		tap->wr_flags = 0;
1324123927Ssam		if (frmhdr.wi_status & WI_STAT_PCF)
1325192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_CFP;
1326178354Ssam		if (m->m_flags & M_WEP)
1327192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_WEP;
1328192468Ssam		tap->wr_antsignal = rssi;
1329192468Ssam		tap->wr_antnoise = nf;
133056965Swpaul	}
133156965Swpaul
1332109323Ssam	/* synchronize driver's BSSID with firmware's BSSID */
1333178354Ssam	wh = mtod(m, struct ieee80211_frame *);
1334109323Ssam	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
1335109323Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
1336109323Ssam		wi_sync_bssid(sc, wh->i_addr3);
133746492Swpaul
1338165088Ssam	WI_UNLOCK(sc);
1339165088Ssam
1340178354Ssam	ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
1341178354Ssam	if (ni != NULL) {
1342192468Ssam		(void) ieee80211_input(ni, m, rssi, nf);
1343178354Ssam		ieee80211_free_node(ni);
1344178354Ssam	} else
1345192468Ssam		(void) ieee80211_input_all(ic, m, rssi, nf);
1346178354Ssam
1347165088Ssam	WI_LOCK(sc);
1348109323Ssam}
134946492Swpaul
1350178354Ssamstatic __noinline void
1351109323Ssamwi_tx_ex_intr(struct wi_softc *sc)
1352109323Ssam{
1353109323Ssam	struct wi_frame frmhdr;
1354109323Ssam	int fid;
135546492Swpaul
1356109323Ssam	fid = CSR_READ_2(sc, WI_TX_CMP_FID);
1357109323Ssam	/* Read in the frame header */
1358109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
1359109323Ssam		u_int16_t status = le16toh(frmhdr.wi_status);
1360109323Ssam		/*
1361109323Ssam		 * Spontaneous station disconnects appear as xmit
1362109323Ssam		 * errors.  Don't announce them and/or count them
1363109323Ssam		 * as an output error.
1364109323Ssam		 */
1365109323Ssam		if ((status & WI_TXSTAT_DISCONNECT) == 0) {
1366109323Ssam			if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
1367287197Sglebius				device_printf(sc->sc_dev, "tx failed");
1368109323Ssam				if (status & WI_TXSTAT_RET_ERR)
1369109323Ssam					printf(", retry limit exceeded");
1370109323Ssam				if (status & WI_TXSTAT_AGED_ERR)
1371109323Ssam					printf(", max transmit lifetime exceeded");
1372109323Ssam				if (status & WI_TXSTAT_DISCONNECT)
1373109323Ssam					printf(", port disconnected");
1374109323Ssam				if (status & WI_TXSTAT_FORM_ERR)
1375109323Ssam					printf(", invalid format (data len %u src %6D)",
1376109323Ssam						le16toh(frmhdr.wi_dat_len),
1377109323Ssam						frmhdr.wi_ehdr.ether_shost, ":");
1378109323Ssam				if (status & ~0xf)
1379109323Ssam					printf(", status=0x%x", status);
1380109323Ssam				printf("\n");
1381109323Ssam			}
1382287197Sglebius			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1383272068Sglebius		} else
1384109323Ssam			DPRINTF(("port disconnected\n"));
1385109323Ssam	} else
1386109323Ssam		DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
1387109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
1388109323Ssam}
138946492Swpaul
1390178354Ssamstatic __noinline void
1391109323Ssamwi_tx_intr(struct wi_softc *sc)
1392109323Ssam{
1393109323Ssam	int fid, cur;
139494405Simp
1395123098Simp	if (sc->wi_gone)
1396123098Simp		return;
1397123098Simp
1398109323Ssam	fid = CSR_READ_2(sc, WI_ALLOC_FID);
1399109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
140046492Swpaul
1401109323Ssam	cur = sc->sc_txcur;
1402109323Ssam	if (sc->sc_txd[cur].d_fid != fid) {
1403287197Sglebius		device_printf(sc->sc_dev, "bad alloc %x != %x, cur %d nxt %d\n",
1404109323Ssam		    fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext);
1405109323Ssam		return;
1406109323Ssam	}
1407109323Ssam	sc->sc_tx_timer = 0;
1408109323Ssam	sc->sc_txd[cur].d_len = 0;
1409112363Simp	sc->sc_txcur = cur = (cur + 1) % sc->sc_ntxbuf;
1410287197Sglebius	if (sc->sc_txd[cur].d_len != 0) {
1411109323Ssam		if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid,
1412109323Ssam		    0, 0)) {
1413287197Sglebius			device_printf(sc->sc_dev, "xmit failed\n");
1414109323Ssam			sc->sc_txd[cur].d_len = 0;
1415109323Ssam		} else {
1416109323Ssam			sc->sc_tx_timer = 5;
1417109323Ssam		}
1418109323Ssam	}
141946492Swpaul}
142046492Swpaul
1421178354Ssamstatic __noinline void
1422109323Ssamwi_info_intr(struct wi_softc *sc)
142394405Simp{
1424287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1425178354Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1426109323Ssam	int i, fid, len, off;
1427109323Ssam	u_int16_t ltbuf[2];
1428109323Ssam	u_int16_t stat;
1429109323Ssam	u_int32_t *ptr;
143094405Simp
1431109323Ssam	fid = CSR_READ_2(sc, WI_INFO_FID);
1432109323Ssam	wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf));
143394405Simp
1434109323Ssam	switch (le16toh(ltbuf[1])) {
1435109323Ssam	case WI_INFO_LINK_STAT:
1436109323Ssam		wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat));
1437109323Ssam		DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat)));
1438232147Sadrian
1439232147Sadrian		if (vap == NULL)
1440232147Sadrian			goto finish;
1441232147Sadrian
1442109323Ssam		switch (le16toh(stat)) {
1443109323Ssam		case WI_INFO_LINK_STAT_CONNECTED:
1444178354Ssam			if (vap->iv_state == IEEE80211_S_RUN &&
1445178354Ssam			    vap->iv_opmode != IEEE80211_M_IBSS)
1446109323Ssam				break;
1447178354Ssam			/* fall thru... */
1448109323Ssam		case WI_INFO_LINK_STAT_AP_CHG:
1449191746Sthompsa			IEEE80211_LOCK(ic);
1450191746Sthompsa			vap->iv_bss->ni_associd = 1 | 0xc000;	/* NB: anything will do */
1451191746Sthompsa			ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
1452191746Sthompsa			IEEE80211_UNLOCK(ic);
1453109323Ssam			break;
1454109323Ssam		case WI_INFO_LINK_STAT_AP_INR:
1455109323Ssam			break;
1456178354Ssam		case WI_INFO_LINK_STAT_DISCONNECTED:
1457178354Ssam			/* we dropped off the net; e.g. due to deauth/disassoc */
1458191746Sthompsa			IEEE80211_LOCK(ic);
1459191746Sthompsa			vap->iv_bss->ni_associd = 0;
1460191746Sthompsa			vap->iv_stats.is_rx_deauth++;
1461191746Sthompsa			ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
1462191746Sthompsa			IEEE80211_UNLOCK(ic);
1463178354Ssam			break;
1464109323Ssam		case WI_INFO_LINK_STAT_AP_OOR:
1465178354Ssam			/* XXX does this need to be per-vap? */
1466191746Sthompsa			ieee80211_beacon_miss(ic);
1467109323Ssam			break;
1468109323Ssam		case WI_INFO_LINK_STAT_ASSOC_FAILED:
1469178354Ssam			if (vap->iv_opmode == IEEE80211_M_STA)
1470191746Sthompsa				ieee80211_new_state(vap, IEEE80211_S_SCAN,
1471191746Sthompsa				    IEEE80211_SCAN_FAIL_TIMEOUT);
1472109323Ssam			break;
1473109323Ssam		}
1474109323Ssam		break;
1475109323Ssam	case WI_INFO_COUNTERS:
1476109323Ssam		/* some card versions have a larger stats structure */
1477109323Ssam		len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4);
1478109323Ssam		ptr = (u_int32_t *)&sc->sc_stats;
1479109323Ssam		off = sizeof(ltbuf);
1480109323Ssam		for (i = 0; i < len; i++, off += 2, ptr++) {
1481109323Ssam			wi_read_bap(sc, fid, off, &stat, sizeof(stat));
1482109323Ssam#ifdef WI_HERMES_STATS_WAR
1483109323Ssam			if (stat & 0xf000)
1484109323Ssam				stat = ~stat;
1485109323Ssam#endif
1486109323Ssam			*ptr += stat;
1487109323Ssam		}
1488109323Ssam		break;
1489109323Ssam	default:
1490109323Ssam		DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid,
1491109323Ssam		    le16toh(ltbuf[1]), le16toh(ltbuf[0])));
1492109323Ssam		break;
149394405Simp	}
1494232147Sadrianfinish:
1495109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
1496109323Ssam}
149794405Simp
1498109323Ssamstatic int
1499109323Ssamwi_write_multi(struct wi_softc *sc)
1500109323Ssam{
1501287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1502287197Sglebius	struct ieee80211vap *vap;
1503287197Sglebius	struct wi_mcast mlist;
1504286437Sadrian	int n;
150594472Simp
1506287197Sglebius	if (ic->ic_allmulti > 0 || ic->ic_promisc > 0) {
1507109323Ssamallmulti:
1508109323Ssam		memset(&mlist, 0, sizeof(mlist));
1509109323Ssam		return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1510109323Ssam		    sizeof(mlist));
151194405Simp	}
151294405Simp
1513109323Ssam	n = 0;
1514287197Sglebius	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1515287197Sglebius		struct ifnet *ifp;
1516287197Sglebius		struct ifmultiaddr *ifma;
1517287197Sglebius
1518287197Sglebius		ifp = vap->iv_ifp;
1519287197Sglebius		if_maddr_rlock(ifp);
1520287197Sglebius		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1521287197Sglebius			if (ifma->ifma_addr->sa_family != AF_LINK)
1522287197Sglebius				continue;
1523287197Sglebius			if (n >= 16)
1524287197Sglebius				goto allmulti;
1525287197Sglebius			IEEE80211_ADDR_COPY(&mlist.wi_mcast[n],
1526287197Sglebius			    (LLADDR((struct sockaddr_dl *)ifma->ifma_addr)));
1527287197Sglebius			n++;
1528287197Sglebius		}
1529287197Sglebius		if_maddr_runlock(ifp);
153094405Simp	}
1531109323Ssam	return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1532109323Ssam	    IEEE80211_ADDR_LEN * n);
153394405Simp}
153494405Simp
153594405Simpstatic void
1536283540Sglebiuswi_update_mcast(struct ieee80211com *ic)
1537178354Ssam{
1538283540Sglebius
1539283540Sglebius	wi_write_multi(ic->ic_softc);
1540178354Ssam}
1541178354Ssam
1542178354Ssamstatic void
1543283540Sglebiuswi_update_promisc(struct ieee80211com *ic)
1544192468Ssam{
1545283540Sglebius	struct wi_softc *sc = ic->ic_softc;
1546192468Ssam
1547192468Ssam	WI_LOCK(sc);
1548192468Ssam	/* XXX handle WEP special case handling? */
1549192468Ssam	wi_write_val(sc, WI_RID_PROMISC,
1550192468Ssam	    (ic->ic_opmode == IEEE80211_M_MONITOR ||
1551287197Sglebius	     (ic->ic_promisc > 0)));
1552192468Ssam	WI_UNLOCK(sc);
1553192468Ssam}
1554192468Ssam
1555192468Ssamstatic void
1556109323Ssamwi_read_nicid(struct wi_softc *sc)
155746492Swpaul{
1558109323Ssam	struct wi_card_ident *id;
1559109323Ssam	char *p;
1560109323Ssam	int len;
1561109323Ssam	u_int16_t ver[4];
156246492Swpaul
1563109323Ssam	/* getting chip identity */
1564109323Ssam	memset(ver, 0, sizeof(ver));
1565109323Ssam	len = sizeof(ver);
1566109323Ssam	wi_read_rid(sc, WI_RID_CARD_ID, ver, &len);
156746492Swpaul
1568109323Ssam	sc->sc_firmware_type = WI_NOTYPE;
1569180919Simp	sc->sc_nic_id = le16toh(ver[0]);
1570109323Ssam	for (id = wi_card_ident; id->card_name != NULL; id++) {
1571180919Simp		if (sc->sc_nic_id == id->card_id) {
1572180919Simp			sc->sc_nic_name = id->card_name;
1573109323Ssam			sc->sc_firmware_type = id->firm_type;
1574109323Ssam			break;
1575109323Ssam		}
157667092Swpaul	}
1577109323Ssam	if (sc->sc_firmware_type == WI_NOTYPE) {
1578180919Simp		if (sc->sc_nic_id & 0x8000) {
1579109323Ssam			sc->sc_firmware_type = WI_INTERSIL;
1580180919Simp			sc->sc_nic_name = "Unknown Prism chip";
1581109323Ssam		} else {
1582109323Ssam			sc->sc_firmware_type = WI_LUCENT;
1583180919Simp			sc->sc_nic_name = "Unknown Lucent chip";
1584109323Ssam		}
158567092Swpaul	}
1586181210Simp	if (bootverbose)
1587181210Simp		device_printf(sc->sc_dev, "using %s\n", sc->sc_nic_name);
158846492Swpaul
1589109323Ssam	/* get primary firmware version (Only Prism chips) */
1590109323Ssam	if (sc->sc_firmware_type != WI_LUCENT) {
1591109323Ssam		memset(ver, 0, sizeof(ver));
1592109323Ssam		len = sizeof(ver);
1593109323Ssam		wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len);
1594109323Ssam		sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 +
1595109323Ssam		    le16toh(ver[3]) * 100 + le16toh(ver[1]);
159667092Swpaul	}
159746492Swpaul
1598109323Ssam	/* get station firmware version */
1599109323Ssam	memset(ver, 0, sizeof(ver));
1600109323Ssam	len = sizeof(ver);
1601109323Ssam	wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len);
1602109323Ssam	sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 +
1603109323Ssam	    le16toh(ver[3]) * 100 + le16toh(ver[1]);
1604109323Ssam	if (sc->sc_firmware_type == WI_INTERSIL &&
1605109323Ssam	    (sc->sc_sta_firmware_ver == 10102 ||
1606109323Ssam	     sc->sc_sta_firmware_ver == 20102)) {
1607109323Ssam		char ident[12];
1608109323Ssam		memset(ident, 0, sizeof(ident));
1609109323Ssam		len = sizeof(ident);
1610109323Ssam		/* value should be the format like "V2.00-11" */
1611109323Ssam		if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 &&
1612109323Ssam		    *(p = (char *)ident) >= 'A' &&
1613109323Ssam		    p[2] == '.' && p[5] == '-' && p[8] == '\0') {
1614109323Ssam			sc->sc_firmware_type = WI_SYMBOL;
1615109323Ssam			sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 +
1616109323Ssam			    (p[3] - '0') * 1000 + (p[4] - '0') * 100 +
1617109323Ssam			    (p[6] - '0') * 10 + (p[7] - '0');
161894405Simp		}
161994405Simp	}
1620180919Simp	if (bootverbose) {
1621180919Simp		device_printf(sc->sc_dev, "%s Firmware: ",
1622180919Simp		    wi_firmware_names[sc->sc_firmware_type]);
1623180919Simp		if (sc->sc_firmware_type != WI_LUCENT)	/* XXX */
1624180919Simp			printf("Primary (%u.%u.%u), ",
1625180919Simp			    sc->sc_pri_firmware_ver / 10000,
1626180919Simp			    (sc->sc_pri_firmware_ver % 10000) / 100,
1627180919Simp			    sc->sc_pri_firmware_ver % 100);
1628180919Simp		printf("Station (%u.%u.%u)\n",
1629180919Simp		    sc->sc_sta_firmware_ver / 10000,
1630180919Simp		    (sc->sc_sta_firmware_ver % 10000) / 100,
1631180919Simp		    sc->sc_sta_firmware_ver % 100);
1632180919Simp	}
1633109323Ssam}
163446492Swpaul
1635109323Ssamstatic int
1636109323Ssamwi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen)
1637109323Ssam{
1638109323Ssam	struct wi_ssid ssid;
163946492Swpaul
1640109323Ssam	if (buflen > IEEE80211_NWID_LEN)
1641109323Ssam		return ENOBUFS;
1642109323Ssam	memset(&ssid, 0, sizeof(ssid));
1643109323Ssam	ssid.wi_len = htole16(buflen);
1644109323Ssam	memcpy(ssid.wi_ssid, buf, buflen);
1645109323Ssam	return wi_write_rid(sc, rid, &ssid, sizeof(ssid));
1646109323Ssam}
164746492Swpaul
1648109323Ssamstatic int
1649178354Ssamwi_write_txrate(struct wi_softc *sc, struct ieee80211vap *vap)
1650109323Ssam{
1651178354Ssam	static const uint16_t lucent_rates[12] = {
1652178354Ssam	    [ 0] = 3,	/* auto */
1653178354Ssam	    [ 1] = 1,	/* 1Mb/s */
1654178354Ssam	    [ 2] = 2,	/* 2Mb/s */
1655178354Ssam	    [ 5] = 4,	/* 5.5Mb/s */
1656178354Ssam	    [11] = 5	/* 11Mb/s */
1657178354Ssam	};
1658178354Ssam	static const uint16_t intersil_rates[12] = {
1659178354Ssam	    [ 0] = 0xf,	/* auto */
1660178354Ssam	    [ 1] = 0,	/* 1Mb/s */
1661178354Ssam	    [ 2] = 1,	/* 2Mb/s */
1662178354Ssam	    [ 5] = 2,	/* 5.5Mb/s */
1663178354Ssam	    [11] = 3,	/* 11Mb/s */
1664178354Ssam	};
1665178354Ssam	const uint16_t *rates = sc->sc_firmware_type == WI_LUCENT ?
1666178354Ssam	    lucent_rates : intersil_rates;
1667178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1668178354Ssam	const struct ieee80211_txparam *tp;
166946492Swpaul
1670178354Ssam	tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
1671178354Ssam	return wi_write_val(sc, WI_RID_TX_RATE,
1672178354Ssam	    (tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
1673178354Ssam		rates[0] : rates[tp->ucastrate / 2]));
167446492Swpaul}
167546492Swpaul
1676109323Ssamstatic int
1677178354Ssamwi_write_wep(struct wi_softc *sc, struct ieee80211vap *vap)
167846492Swpaul{
1679109323Ssam	int error = 0;
1680109323Ssam	int i, keylen;
1681109323Ssam	u_int16_t val;
1682109323Ssam	struct wi_key wkey[IEEE80211_WEP_NKID];
168346492Swpaul
1684109323Ssam	switch (sc->sc_firmware_type) {
1685109323Ssam	case WI_LUCENT:
1686178354Ssam		val = (vap->iv_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
1687109323Ssam		error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
1688109323Ssam		if (error)
1689109323Ssam			break;
1690178354Ssam		if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
1691138988Smdodd			break;
1692178354Ssam		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, vap->iv_def_txkey);
1693138988Smdodd		if (error)
1694138988Smdodd			break;
1695138988Smdodd		memset(wkey, 0, sizeof(wkey));
1696138988Smdodd		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
1697178354Ssam			keylen = vap->iv_nw_keys[i].wk_keylen;
1698138988Smdodd			wkey[i].wi_keylen = htole16(keylen);
1699178354Ssam			memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key,
1700138988Smdodd			    keylen);
1701109323Ssam		}
1702138988Smdodd		error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS,
1703138988Smdodd		    wkey, sizeof(wkey));
1704150798Savatar		sc->sc_encryption = 0;
1705109323Ssam		break;
1706109323Ssam
1707109323Ssam	case WI_INTERSIL:
1708178354Ssam		val = HOST_ENCRYPT | HOST_DECRYPT;
1709178354Ssam		if (vap->iv_flags & IEEE80211_F_PRIVACY) {
1710109323Ssam			/*
1711109323Ssam			 * ONLY HWB3163 EVAL-CARD Firmware version
1712109323Ssam			 * less than 0.8 variant2
1713109323Ssam			 *
1714109323Ssam			 *   If promiscuous mode disable, Prism2 chip
1715109323Ssam			 *  does not work with WEP .
1716109323Ssam			 * It is under investigation for details.
1717109323Ssam			 * (ichiro@netbsd.org)
1718109323Ssam			 */
1719178354Ssam			if (sc->sc_sta_firmware_ver < 802 ) {
1720109323Ssam				/* firm ver < 0.8 variant 2 */
1721109323Ssam				wi_write_val(sc, WI_RID_PROMISC, 1);
1722109323Ssam			}
1723109323Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE,
1724178354Ssam			    vap->iv_bss->ni_authmode);
1725178354Ssam			val |= PRIVACY_INVOKED;
1726109323Ssam		} else {
1727178354Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN);
1728109323Ssam		}
1729109323Ssam		error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
1730109323Ssam		if (error)
1731109323Ssam			break;
1732150798Savatar		sc->sc_encryption = val;
1733138988Smdodd		if ((val & PRIVACY_INVOKED) == 0)
1734138988Smdodd			break;
1735178354Ssam		error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, vap->iv_def_txkey);
1736109323Ssam		break;
1737109323Ssam	}
1738109323Ssam	return error;
173946492Swpaul}
174046492Swpaul
1741192492Simpstatic int
1742109323Ssamwi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
174346492Swpaul{
1744178354Ssam	int i, s = 0;
1745178354Ssam
1746123098Simp	if (sc->wi_gone)
1747123098Simp		return (ENODEV);
1748123098Simp
1749109323Ssam	/* wait for the busy bit to clear */
1750123339Simp	for (i = sc->wi_cmd_count; i > 0; i--) {	/* 500ms */
1751116206Simp		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY))
1752109323Ssam			break;
1753123098Simp		DELAY(1*1000);	/* 1ms */
1754109323Ssam	}
1755109323Ssam	if (i == 0) {
1756178354Ssam		device_printf(sc->sc_dev, "%s: busy bit won't clear, cmd 0x%x\n",
1757178354Ssam		   __func__, cmd);
1758123098Simp		sc->wi_gone = 1;
1759109323Ssam		return(ETIMEDOUT);
1760109323Ssam	}
176190580Sbrooks
1762109323Ssam	CSR_WRITE_2(sc, WI_PARAM0, val0);
1763109323Ssam	CSR_WRITE_2(sc, WI_PARAM1, val1);
1764109323Ssam	CSR_WRITE_2(sc, WI_PARAM2, val2);
1765109323Ssam	CSR_WRITE_2(sc, WI_COMMAND, cmd);
176690580Sbrooks
1767109323Ssam	if (cmd == WI_CMD_INI) {
1768109323Ssam		/* XXX: should sleep here. */
1769116206Simp		DELAY(100*1000);		/* 100ms delay for init */
1770109323Ssam	}
1771109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
1772109323Ssam		/*
1773109323Ssam		 * Wait for 'command complete' bit to be
1774109323Ssam		 * set in the event status register.
1775109323Ssam		 */
1776109323Ssam		s = CSR_READ_2(sc, WI_EVENT_STAT);
1777109323Ssam		if (s & WI_EV_CMD) {
1778109323Ssam			/* Ack the event and read result code. */
1779109323Ssam			s = CSR_READ_2(sc, WI_STATUS);
1780109323Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
1781109323Ssam			if (s & WI_STAT_CMD_RESULT) {
1782109323Ssam				return(EIO);
1783109323Ssam			}
1784109323Ssam			break;
178590580Sbrooks		}
1786109323Ssam		DELAY(WI_DELAY);
1787109323Ssam	}
178890580Sbrooks
1789109323Ssam	if (i == WI_TIMEOUT) {
1790178354Ssam		device_printf(sc->sc_dev, "%s: timeout on cmd 0x%04x; "
1791178354Ssam		    "event status 0x%04x\n", __func__, cmd, s);
1792123098Simp		if (s == 0xffff)
1793123098Simp			sc->wi_gone = 1;
1794109323Ssam		return(ETIMEDOUT);
179553702Swpaul	}
1796109323Ssam	return (0);
1797109323Ssam}
179853702Swpaul
1799109323Ssamstatic int
1800109323Ssamwi_seek_bap(struct wi_softc *sc, int id, int off)
1801109323Ssam{
1802109323Ssam	int i, status;
180390580Sbrooks
1804109323Ssam	CSR_WRITE_2(sc, WI_SEL0, id);
1805109323Ssam	CSR_WRITE_2(sc, WI_OFF0, off);
180690580Sbrooks
1807109323Ssam	for (i = 0; ; i++) {
1808109323Ssam		status = CSR_READ_2(sc, WI_OFF0);
1809109323Ssam		if ((status & WI_OFF_BUSY) == 0)
1810109323Ssam			break;
1811109323Ssam		if (i == WI_TIMEOUT) {
1812178354Ssam			device_printf(sc->sc_dev, "%s: timeout, id %x off %x\n",
1813178354Ssam			    __func__, id, off);
1814109323Ssam			sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
1815123098Simp			if (status == 0xffff)
1816123098Simp				sc->wi_gone = 1;
1817109323Ssam			return ETIMEDOUT;
1818109323Ssam		}
1819109323Ssam		DELAY(1);
182053702Swpaul	}
1821109323Ssam	if (status & WI_OFF_ERR) {
1822178354Ssam		device_printf(sc->sc_dev, "%s: error, id %x off %x\n",
1823178354Ssam		    __func__, id, off);
1824109323Ssam		sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
1825109323Ssam		return EIO;
1826109323Ssam	}
1827109323Ssam	sc->sc_bap_id = id;
1828109323Ssam	sc->sc_bap_off = off;
1829109323Ssam	return 0;
183053702Swpaul}
183153702Swpaul
1832109323Ssamstatic int
1833109323Ssamwi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
183453702Swpaul{
1835253756Sjhibbits	int error, cnt;
183653702Swpaul
1837109323Ssam	if (buflen == 0)
1838109323Ssam		return 0;
1839109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
1840109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
1841109323Ssam			return error;
184275219Salfred	}
1843109323Ssam	cnt = (buflen + 1) / 2;
1844253756Sjhibbits	CSR_READ_MULTI_STREAM_2(sc, WI_DATA0, (u_int16_t *)buf, cnt);
1845109323Ssam	sc->sc_bap_off += cnt * 2;
1846109323Ssam	return 0;
184753702Swpaul}
184853702Swpaul
1849109323Ssamstatic int
1850287197Sglebiuswi_write_bap(struct wi_softc *sc, int id, int off, const void *buf, int buflen)
185153702Swpaul{
1852253756Sjhibbits	int error, cnt;
185346492Swpaul
1854109323Ssam	if (buflen == 0)
1855109323Ssam		return 0;
185646492Swpaul
1857109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
1858109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
1859109323Ssam			return error;
1860109323Ssam	}
1861109323Ssam	cnt = (buflen + 1) / 2;
1862287197Sglebius	CSR_WRITE_MULTI_STREAM_2(sc, WI_DATA0, (const uint16_t *)buf, cnt);
1863109323Ssam	sc->sc_bap_off += cnt * 2;
1864109323Ssam
1865109323Ssam	return 0;
186646492Swpaul}
186753702Swpaul
1868109323Ssamstatic int
1869109323Ssamwi_mwrite_bap(struct wi_softc *sc, int id, int off, struct mbuf *m0, int totlen)
1870109323Ssam{
1871109323Ssam	int error, len;
1872109323Ssam	struct mbuf *m;
187353702Swpaul
1874109323Ssam	for (m = m0; m != NULL && totlen > 0; m = m->m_next) {
1875109323Ssam		if (m->m_len == 0)
1876109323Ssam			continue;
187753702Swpaul
1878109323Ssam		len = min(m->m_len, totlen);
187953702Swpaul
1880109323Ssam		if (((u_long)m->m_data) % 2 != 0 || len % 2 != 0) {
1881109323Ssam			m_copydata(m, 0, totlen, (caddr_t)&sc->sc_txbuf);
1882109323Ssam			return wi_write_bap(sc, id, off, (caddr_t)&sc->sc_txbuf,
1883109323Ssam			    totlen);
1884109323Ssam		}
188553702Swpaul
1886109323Ssam		if ((error = wi_write_bap(sc, id, off, m->m_data, len)) != 0)
1887109323Ssam			return error;
188853702Swpaul
1889109323Ssam		off += m->m_len;
1890109323Ssam		totlen -= len;
1891109323Ssam	}
1892109323Ssam	return 0;
1893109323Ssam}
189453702Swpaul
1895109323Ssamstatic int
1896109323Ssamwi_alloc_fid(struct wi_softc *sc, int len, int *idp)
189753702Swpaul{
189853702Swpaul	int i;
189953702Swpaul
1900109323Ssam	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
1901178354Ssam		device_printf(sc->sc_dev, "%s: failed to allocate %d bytes on NIC\n",
1902178354Ssam		    __func__, len);
1903109323Ssam		return ENOMEM;
190453702Swpaul	}
190553702Swpaul
1906109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
1907109323Ssam		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
1908109323Ssam			break;
1909109323Ssam		DELAY(1);
191053702Swpaul	}
1911144167Ssam	if (i == WI_TIMEOUT) {
1912178354Ssam		device_printf(sc->sc_dev, "%s: timeout in alloc\n", __func__);
1913144167Ssam		return ETIMEDOUT;
1914144167Ssam	}
1915109323Ssam	*idp = CSR_READ_2(sc, WI_ALLOC_FID);
1916109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
1917109323Ssam	return 0;
1918109323Ssam}
191953702Swpaul
1920109323Ssamstatic int
1921109323Ssamwi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp)
1922109323Ssam{
1923109323Ssam	int error, len;
1924109323Ssam	u_int16_t ltbuf[2];
192553702Swpaul
1926109323Ssam	/* Tell the NIC to enter record read mode. */
1927109323Ssam	error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0);
1928109323Ssam	if (error)
1929109323Ssam		return error;
193053702Swpaul
1931109323Ssam	error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
1932109323Ssam	if (error)
1933109323Ssam		return error;
193453702Swpaul
1935109323Ssam	if (le16toh(ltbuf[1]) != rid) {
1936109323Ssam		device_printf(sc->sc_dev, "record read mismatch, rid=%x, got=%x\n",
1937109323Ssam		    rid, le16toh(ltbuf[1]));
1938109323Ssam		return EIO;
193953702Swpaul	}
1940109323Ssam	len = (le16toh(ltbuf[0]) - 1) * 2;	 /* already got rid */
1941109323Ssam	if (*buflenp < len) {
1942109323Ssam		device_printf(sc->sc_dev, "record buffer is too small, "
1943109323Ssam		    "rid=%x, size=%d, len=%d\n",
1944109323Ssam		    rid, *buflenp, len);
1945109323Ssam		return ENOSPC;
194653702Swpaul	}
1947109323Ssam	*buflenp = len;
1948109323Ssam	return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len);
1949109323Ssam}
195053702Swpaul
1951109323Ssamstatic int
1952287197Sglebiuswi_write_rid(struct wi_softc *sc, int rid, const void *buf, int buflen)
1953109323Ssam{
1954109323Ssam	int error;
1955109323Ssam	u_int16_t ltbuf[2];
195653702Swpaul
1957109323Ssam	ltbuf[0] = htole16((buflen + 1) / 2 + 1);	 /* includes rid */
1958109323Ssam	ltbuf[1] = htole16(rid);
195953702Swpaul
1960109323Ssam	error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
1961178354Ssam	if (error) {
1962178354Ssam		device_printf(sc->sc_dev, "%s: bap0 write failure, rid 0x%x\n",
1963178354Ssam		    __func__, rid);
1964109323Ssam		return error;
1965178354Ssam	}
1966109323Ssam	error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen);
1967178354Ssam	if (error) {
1968178354Ssam		device_printf(sc->sc_dev, "%s: bap1 write failure, rid 0x%x\n",
1969178354Ssam		    __func__, rid);
1970109323Ssam		return error;
1971178354Ssam	}
1972102206Simp
1973109323Ssam	return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0);
197453702Swpaul}
197577217Sphk
197688546Salfredstatic int
1977178354Ssamwi_write_appie(struct wi_softc *sc, int rid, const struct ieee80211_appie *ie)
197877217Sphk{
1979178354Ssam	/* NB: 42 bytes is probably ok to have on the stack */
1980178354Ssam	char buf[sizeof(uint16_t) + 40];
198177217Sphk
1982178354Ssam	if (ie->ie_len > 40)
1983178354Ssam		return EINVAL;
1984178354Ssam	/* NB: firmware requires 16-bit ie length before ie data */
1985178354Ssam	*(uint16_t *) buf = htole16(ie->ie_len);
1986178354Ssam	memcpy(buf + sizeof(uint16_t), ie->ie_data, ie->ie_len);
1987178354Ssam	return wi_write_rid(sc, rid, buf, ie->ie_len + sizeof(uint16_t));
198877217Sphk}
198977217Sphk
1990109323Ssamint
1991109323Ssamwi_alloc(device_t dev, int rid)
1992109323Ssam{
1993109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
1994109323Ssam
1995109323Ssam	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
1996109323Ssam		sc->iobase_rid = rid;
1997109323Ssam		sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
1998109323Ssam		    &sc->iobase_rid, 0, ~0, (1 << 6),
1999109323Ssam		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
2000178354Ssam		if (sc->iobase == NULL) {
2001109323Ssam			device_printf(dev, "No I/O space?!\n");
2002178354Ssam			return ENXIO;
200398440Simp		}
2004109323Ssam
2005109323Ssam		sc->wi_io_addr = rman_get_start(sc->iobase);
2006109323Ssam		sc->wi_btag = rman_get_bustag(sc->iobase);
2007109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
2008109323Ssam	} else {
2009109323Ssam		sc->mem_rid = rid;
2010127135Snjl		sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
2011127135Snjl		    &sc->mem_rid, RF_ACTIVE);
2012178354Ssam		if (sc->mem == NULL) {
2013109323Ssam			device_printf(dev, "No Mem space on prism2.5?\n");
2014178354Ssam			return ENXIO;
201577217Sphk		}
2016109323Ssam
2017109323Ssam		sc->wi_btag = rman_get_bustag(sc->mem);
2018109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->mem);
201977217Sphk	}
202077217Sphk
2021109323Ssam	sc->irq_rid = 0;
2022127135Snjl	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
2023127135Snjl	    RF_ACTIVE |
2024109323Ssam	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
2025178354Ssam	if (sc->irq == NULL) {
2026109323Ssam		wi_free(dev);
2027109323Ssam		device_printf(dev, "No irq?!\n");
2028178354Ssam		return ENXIO;
202977217Sphk	}
2030109323Ssam
2031109323Ssam	sc->sc_dev = dev;
2032109323Ssam	sc->sc_unit = device_get_unit(dev);
2033178354Ssam	return 0;
203477217Sphk}
203593359Simp
2036109323Ssamvoid
2037109323Ssamwi_free(device_t dev)
2038109323Ssam{
2039109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
2040109323Ssam
2041109323Ssam	if (sc->iobase != NULL) {
2042109323Ssam		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
2043109323Ssam		sc->iobase = NULL;
2044109323Ssam	}
2045109323Ssam	if (sc->irq != NULL) {
2046109323Ssam		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
2047109323Ssam		sc->irq = NULL;
2048109323Ssam	}
2049109323Ssam	if (sc->mem != NULL) {
2050109323Ssam		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
2051109323Ssam		sc->mem = NULL;
2052109323Ssam	}
2053109323Ssam}
2054