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
49298955Spfg * publicly 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$");
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>
77295126Sglebius#include <sys/malloc.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>
87116951Ssam#include <machine/atomic.h>
8853702Swpaul#include <sys/rman.h>
8953702Swpaul
9046492Swpaul#include <net/if.h>
91257176Sglebius#include <net/if_var.h>
9246492Swpaul#include <net/if_arp.h>
9346492Swpaul#include <net/ethernet.h>
9446492Swpaul#include <net/if_dl.h>
95190579Ssam#include <net/if_llc.h>
9646492Swpaul#include <net/if_media.h>
9746492Swpaul#include <net/if_types.h>
9846492Swpaul
99116951Ssam#include <net80211/ieee80211_var.h>
100116951Ssam#include <net80211/ieee80211_ioctl.h>
101119784Ssam#include <net80211/ieee80211_radiotap.h>
102116951Ssam
10346492Swpaul#include <netinet/in.h>
10446492Swpaul#include <netinet/in_systm.h>
10546492Swpaul#include <netinet/in_var.h>
10646492Swpaul#include <netinet/ip.h>
10746492Swpaul#include <netinet/if_ether.h>
10846492Swpaul
10946492Swpaul#include <net/bpf.h>
11046492Swpaul
11170808Speter#include <dev/wi/if_wavelan_ieee.h>
112119784Ssam#include <dev/wi/if_wireg.h>
11393611Simp#include <dev/wi/if_wivar.h>
11446492Swpaul
115228621Sbschmidtstatic struct ieee80211vap *wi_vap_create(struct ieee80211com *,
116228621Sbschmidt		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
117228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN],
118228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN]);
119178354Ssamstatic void wi_vap_delete(struct ieee80211vap *vap);
120287197Sglebiusstatic int  wi_transmit(struct ieee80211com *, struct mbuf *);
121287197Sglebiusstatic void wi_start(struct wi_softc *);
122287197Sglebiusstatic int  wi_start_tx(struct wi_softc *, struct wi_frame *, struct mbuf *);
123160991Ssamstatic int  wi_raw_xmit(struct ieee80211_node *, struct mbuf *,
124160991Ssam		const struct ieee80211_bpf_params *);
125178354Ssamstatic int  wi_newstate_sta(struct ieee80211vap *, enum ieee80211_state, int);
126192468Ssamstatic int  wi_newstate_hostap(struct ieee80211vap *, enum ieee80211_state,
127192468Ssam		int);
128178354Ssamstatic void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
129283535Sadrian		int subtype, const struct ieee80211_rx_stats *rxs,
130283535Sadrian		int rssi, int nf);
131178354Ssamstatic int  wi_reset(struct wi_softc *);
132165089Ssamstatic void wi_watchdog(void *);
133287197Sglebiusstatic void wi_parent(struct ieee80211com *);
134109323Ssamstatic void wi_media_status(struct ifnet *, struct ifmediareq *);
135109323Ssamstatic void wi_rx_intr(struct wi_softc *);
136109323Ssamstatic void wi_tx_intr(struct wi_softc *);
137109323Ssamstatic void wi_tx_ex_intr(struct wi_softc *);
138178354Ssam
139109323Ssamstatic void wi_info_intr(struct wi_softc *);
14046492Swpaul
141178354Ssamstatic int  wi_write_txrate(struct wi_softc *, struct ieee80211vap *);
142178354Ssamstatic int  wi_write_wep(struct wi_softc *, struct ieee80211vap *);
143109323Ssamstatic int  wi_write_multi(struct wi_softc *);
144283540Sglebiusstatic void wi_update_mcast(struct ieee80211com *);
145283540Sglebiusstatic void wi_update_promisc(struct ieee80211com *);
146109323Ssamstatic int  wi_alloc_fid(struct wi_softc *, int, int *);
147109323Ssamstatic void wi_read_nicid(struct wi_softc *);
148109323Ssamstatic int  wi_write_ssid(struct wi_softc *, int, u_int8_t *, int);
14953702Swpaul
150192492Simpstatic int  wi_cmd(struct wi_softc *, int, int, int, int);
151109323Ssamstatic int  wi_seek_bap(struct wi_softc *, int, int);
152109323Ssamstatic int  wi_read_bap(struct wi_softc *, int, int, void *, int);
153287197Sglebiusstatic int  wi_write_bap(struct wi_softc *, int, int, const void *, int);
154109323Ssamstatic int  wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int);
155109323Ssamstatic int  wi_read_rid(struct wi_softc *, int, void *, int *);
156287197Sglebiusstatic int  wi_write_rid(struct wi_softc *, int, const void *, int);
157178354Ssamstatic int  wi_write_appie(struct wi_softc *, int, const struct ieee80211_appie *);
158300238Savosstatic u_int16_t wi_read_chanmask(struct wi_softc *);
15977217Sphk
160170530Ssamstatic void wi_scan_start(struct ieee80211com *);
161170530Ssamstatic void wi_scan_end(struct ieee80211com *);
162300238Savosstatic void wi_getradiocaps(struct ieee80211com *, int, int *,
163300238Savos		struct ieee80211_channel[]);
164170530Ssamstatic void wi_set_channel(struct ieee80211com *);
165170530Ssam
166109323Ssamstatic __inline int
167109323Ssamwi_write_val(struct wi_softc *sc, int rid, u_int16_t val)
168109323Ssam{
16953702Swpaul
170109323Ssam	val = htole16(val);
171109323Ssam	return wi_write_rid(sc, rid, &val, sizeof(val));
172109323Ssam}
173109323Ssam
174227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, wi, CTLFLAG_RD, 0,
175227309Sed	    "Wireless driver parameters");
176109592Ssam
177109323Ssamstatic	struct timeval lasttxerror;	/* time of last tx error msg */
178109323Ssamstatic	int curtxeps;			/* current tx error msgs/sec */
179111559Ssamstatic	int wi_txerate = 0;		/* tx error rate: max msgs/sec */
180109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate,
181111559Ssam	    0, "max tx error msgs/sec; 0 to disable msgs");
182109323Ssam
183109323Ssam#define	WI_DEBUG
184109323Ssam#ifdef WI_DEBUG
185109323Ssamstatic	int wi_debug = 0;
186109592SsamSYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug,
187109592Ssam	    0, "control debugging printfs");
188109323Ssam#define	DPRINTF(X)	if (wi_debug) printf X
189109323Ssam#else
190109323Ssam#define	DPRINTF(X)
191109323Ssam#endif
192109323Ssam
193109323Ssam#define WI_INTRS	(WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO)
194109323Ssam
19593825Simpstruct wi_card_ident wi_card_ident[] = {
19693825Simp	/* CARD_ID			CARD_NAME		FIRM_TYPE */
19793825Simp	{ WI_NIC_LUCENT_ID,		WI_NIC_LUCENT_STR,	WI_LUCENT },
19893825Simp	{ WI_NIC_SONY_ID,		WI_NIC_SONY_STR,	WI_LUCENT },
19993825Simp	{ WI_NIC_LUCENT_EMB_ID,		WI_NIC_LUCENT_EMB_STR,	WI_LUCENT },
20093825Simp	{ WI_NIC_EVB2_ID,		WI_NIC_EVB2_STR,	WI_INTERSIL },
20193825Simp	{ WI_NIC_HWB3763_ID,		WI_NIC_HWB3763_STR,	WI_INTERSIL },
20293825Simp	{ WI_NIC_HWB3163_ID,		WI_NIC_HWB3163_STR,	WI_INTERSIL },
20393825Simp	{ WI_NIC_HWB3163B_ID,		WI_NIC_HWB3163B_STR,	WI_INTERSIL },
20493825Simp	{ WI_NIC_EVB3_ID,		WI_NIC_EVB3_STR,	WI_INTERSIL },
20593825Simp	{ WI_NIC_HWB1153_ID,		WI_NIC_HWB1153_STR,	WI_INTERSIL },
20693825Simp	{ WI_NIC_P2_SST_ID,		WI_NIC_P2_SST_STR,	WI_INTERSIL },
20793825Simp	{ WI_NIC_EVB2_SST_ID,		WI_NIC_EVB2_SST_STR,	WI_INTERSIL },
20893825Simp	{ WI_NIC_3842_EVA_ID,		WI_NIC_3842_EVA_STR,	WI_INTERSIL },
20993825Simp	{ WI_NIC_3842_PCMCIA_AMD_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
21093825Simp	{ WI_NIC_3842_PCMCIA_SST_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
211101355Simp	{ WI_NIC_3842_PCMCIA_ATL_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
212101355Simp	{ WI_NIC_3842_PCMCIA_ATS_ID,	WI_NIC_3842_PCMCIA_STR,	WI_INTERSIL },
21393825Simp	{ WI_NIC_3842_MINI_AMD_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
21493825Simp	{ WI_NIC_3842_MINI_SST_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
215101355Simp	{ WI_NIC_3842_MINI_ATL_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
216101355Simp	{ WI_NIC_3842_MINI_ATS_ID,	WI_NIC_3842_MINI_STR,	WI_INTERSIL },
21793825Simp	{ WI_NIC_3842_PCI_AMD_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
21893825Simp	{ WI_NIC_3842_PCI_SST_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
219101355Simp	{ WI_NIC_3842_PCI_ATS_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
220101355Simp	{ WI_NIC_3842_PCI_ATL_ID,	WI_NIC_3842_PCI_STR,	WI_INTERSIL },
22193825Simp	{ WI_NIC_P3_PCMCIA_AMD_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
22293825Simp	{ WI_NIC_P3_PCMCIA_SST_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
223101355Simp	{ WI_NIC_P3_PCMCIA_ATL_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
224101355Simp	{ WI_NIC_P3_PCMCIA_ATS_ID,	WI_NIC_P3_PCMCIA_STR,	WI_INTERSIL },
22593825Simp	{ WI_NIC_P3_MINI_AMD_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
22693825Simp	{ WI_NIC_P3_MINI_SST_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
227101355Simp	{ WI_NIC_P3_MINI_ATL_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
228101355Simp	{ WI_NIC_P3_MINI_ATS_ID,	WI_NIC_P3_MINI_STR,	WI_INTERSIL },
22993825Simp	{ 0,	NULL,	0 },
23093825Simp};
23193825Simp
232180919Simpstatic char *wi_firmware_names[] = { "none", "Hermes", "Intersil", "Symbol" };
233180919Simp
234109323Ssamdevclass_t wi_devclass;
23546492Swpaul
23693611Simpint
237109323Ssamwi_attach(device_t dev)
23874906Salfred{
239109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
240287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
241116951Ssam	int i, nrates, buflen;
242109323Ssam	u_int16_t val;
243109323Ssam	u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE];
244116951Ssam	struct ieee80211_rateset *rs;
245180919Simp	struct sysctl_ctx_list *sctx;
246180919Simp	struct sysctl_oid *soid;
247109323Ssam	static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
248109323Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
249109323Ssam	};
250109323Ssam	int error;
25174906Salfred
252138571Ssam	sc->sc_firmware_type = WI_NOTYPE;
253123339Simp	sc->wi_cmd_count = 500;
25446492Swpaul	/* Reset the NIC. */
255178354Ssam	if (wi_reset(sc) != 0) {
256178354Ssam		wi_free(dev);
257109323Ssam		return ENXIO;		/* XXX */
258178354Ssam	}
25946492Swpaul
260178354Ssam	/* Read NIC identification */
261178354Ssam	wi_read_nicid(sc);
262178354Ssam	switch (sc->sc_firmware_type) {
263178354Ssam	case WI_LUCENT:
264178354Ssam		if (sc->sc_sta_firmware_ver < 60006)
265178354Ssam			goto reject;
266178354Ssam		break;
267178354Ssam	case WI_INTERSIL:
268178354Ssam		if (sc->sc_sta_firmware_ver < 800)
269178354Ssam			goto reject;
270178354Ssam		break;
271178354Ssam	default:
272178354Ssam	reject:
273178354Ssam		device_printf(dev, "Sorry, this card is not supported "
274178354Ssam		    "(type %d, firmware ver %d)\n",
275178354Ssam		    sc->sc_firmware_type, sc->sc_sta_firmware_ver);
276178354Ssam		wi_free(dev);
277178354Ssam		return EOPNOTSUPP;
278178354Ssam	}
279178354Ssam
280180919Simp	/* Export info about the device via sysctl */
281180919Simp	sctx = device_get_sysctl_ctx(dev);
282180919Simp	soid = device_get_sysctl_tree(dev);
283180919Simp	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
284180919Simp	    "firmware_type", CTLFLAG_RD,
285180919Simp	    wi_firmware_names[sc->sc_firmware_type], 0,
286180919Simp	    "Firmware type string");
287180919Simp	SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "sta_version",
288180919Simp	    CTLFLAG_RD, &sc->sc_sta_firmware_ver, 0,
289180919Simp	    "Station Firmware version");
290180919Simp	if (sc->sc_firmware_type == WI_INTERSIL)
291180919Simp		SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
292180919Simp		    "pri_version", CTLFLAG_RD, &sc->sc_pri_firmware_ver, 0,
293180919Simp		    "Primary Firmware version");
294217586Smdf	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_id",
295180919Simp	    CTLFLAG_RD, &sc->sc_nic_id, 0, "NIC id");
296180919Simp	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_name",
297180919Simp	    CTLFLAG_RD, sc->sc_nic_name, 0, "NIC name");
298180919Simp
299178354Ssam	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
300178354Ssam	    MTX_DEF | MTX_RECURSE);
301178354Ssam	callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
302287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
303178354Ssam
30476438Swpaul	/*
30576438Swpaul	 * Read the station address.
30676438Swpaul	 * And do it twice. I've seen PRISM-based cards that return
30776438Swpaul	 * an error when trying to read it the first time, which causes
30876438Swpaul	 * the probe to fail.
30976438Swpaul	 */
310109323Ssam	buflen = IEEE80211_ADDR_LEN;
311287197Sglebius	error = wi_read_rid(sc, WI_RID_MAC_NODE, &ic->ic_macaddr, &buflen);
312109323Ssam	if (error != 0) {
313109323Ssam		buflen = IEEE80211_ADDR_LEN;
314287197Sglebius		error = wi_read_rid(sc, WI_RID_MAC_NODE, &ic->ic_macaddr,
315287197Sglebius		    &buflen);
316109323Ssam	}
317287197Sglebius	if (error || IEEE80211_ADDR_EQ(&ic->ic_macaddr, empty_macaddr)) {
318109323Ssam		if (error != 0)
319109323Ssam			device_printf(dev, "mac read failed %d\n", error);
320148714Simp		else {
321109323Ssam			device_printf(dev, "mac read failed (all zeros)\n");
322148714Simp			error = ENXIO;
323148714Simp		}
32475149Simp		wi_free(dev);
32575149Simp		return (error);
32675149Simp	}
32746492Swpaul
328283537Sglebius	ic->ic_softc = sc;
329283527Sglebius	ic->ic_name = device_get_nameunit(dev);
330109323Ssam	ic->ic_phytype = IEEE80211_T_DS;
331109323Ssam	ic->ic_opmode = IEEE80211_M_STA;
332178957Ssam	ic->ic_caps = IEEE80211_C_STA
333178957Ssam		    | IEEE80211_C_PMGT
334178354Ssam		    | IEEE80211_C_MONITOR
335138571Ssam		    ;
33646492Swpaul
337116951Ssam	/*
338116951Ssam	 * Query the card for available channels and setup the
339116951Ssam	 * channel table.  We assume these are all 11b channels.
340116951Ssam	 */
341300238Savos	sc->sc_chanmask = wi_read_chanmask(sc);
342300238Savos	wi_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
343300238Savos	    ic->ic_channels);
344116951Ssam
34546563Swpaul	/*
34698440Simp	 * Set flags based on firmware version.
34798440Simp	 */
34898440Simp	switch (sc->sc_firmware_type) {
34998440Simp	case WI_LUCENT:
350112363Simp		sc->sc_ntxbuf = 1;
351178354Ssam		ic->ic_caps |= IEEE80211_C_IBSS;
352119784Ssam
353178354Ssam		sc->sc_ibss_port = WI_PORTTYPE_BSS;
354178354Ssam		sc->sc_monitor_port = WI_PORTTYPE_ADHOC;
355119784Ssam		sc->sc_min_rssi = WI_LUCENT_MIN_RSSI;
356119784Ssam		sc->sc_max_rssi = WI_LUCENT_MAX_RSSI;
357119784Ssam		sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET;
35898440Simp		break;
35998440Simp	case WI_INTERSIL:
360112363Simp		sc->sc_ntxbuf = WI_NTXBUF;
361178354Ssam		sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR
362178354Ssam			     |  WI_FLAGS_HAS_ROAMING;
363123339Simp		/*
364123339Simp		 * Old firmware are slow, so give peace a chance.
365123339Simp		 */
366123339Simp		if (sc->sc_sta_firmware_ver < 10000)
367123339Simp			sc->wi_cmd_count = 5000;
368109323Ssam		if (sc->sc_sta_firmware_ver > 10101)
369109323Ssam			sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST;
370178354Ssam		ic->ic_caps |= IEEE80211_C_IBSS;
371109396Simp		/*
372109396Simp		 * version 0.8.3 and newer are the only ones that are known
373109396Simp		 * to currently work.  Earlier versions can be made to work,
374178354Ssam		 * at least according to the Linux driver but we require
375178354Ssam		 * monitor mode so this is irrelevant.
376109396Simp		 */
377178354Ssam		ic->ic_caps |= IEEE80211_C_HOSTAP;
378178354Ssam		if (sc->sc_sta_firmware_ver >= 10603)
379178354Ssam			sc->sc_flags |= WI_FLAGS_HAS_ENHSECURITY;
380178354Ssam		if (sc->sc_sta_firmware_ver >= 10700) {
381178354Ssam			/*
382178354Ssam			 * 1.7.0+ have the necessary support for sta mode WPA.
383178354Ssam			 */
384178354Ssam			sc->sc_flags |= WI_FLAGS_HAS_WPASUPPORT;
385178354Ssam			ic->ic_caps |= IEEE80211_C_WPA;
386178354Ssam		}
387119784Ssam
388178354Ssam		sc->sc_ibss_port = WI_PORTTYPE_IBSS;
389178354Ssam		sc->sc_monitor_port = WI_PORTTYPE_APSILENT;
390119784Ssam		sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
391119784Ssam		sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
392119784Ssam		sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
39398440Simp		break;
39498440Simp	}
39598440Simp
39698440Simp	/*
39756965Swpaul	 * Find out if we support WEP on this card.
39856965Swpaul	 */
399109323Ssam	buflen = sizeof(val);
400109323Ssam	if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 &&
401109323Ssam	    val != htole16(0))
402178354Ssam		ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
40356965Swpaul
404109323Ssam	/* Find supported rates. */
405109323Ssam	buflen = sizeof(ratebuf);
406116951Ssam	rs = &ic->ic_sup_rates[IEEE80211_MODE_11B];
407109323Ssam	if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) {
408116951Ssam		nrates = le16toh(*(u_int16_t *)ratebuf);
409116951Ssam		if (nrates > IEEE80211_RATE_MAXSIZE)
410116951Ssam			nrates = IEEE80211_RATE_MAXSIZE;
411116951Ssam		rs->rs_nrates = 0;
412116951Ssam		for (i = 0; i < nrates; i++)
413116951Ssam			if (ratebuf[2+i])
414116951Ssam				rs->rs_rates[rs->rs_nrates++] = ratebuf[2+i];
415109323Ssam	} else {
416109323Ssam		/* XXX fallback on error? */
41798440Simp	}
41877217Sphk
419109323Ssam	buflen = sizeof(val);
420109323Ssam	if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) &&
421109323Ssam	    wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) {
422119784Ssam		sc->sc_dbm_offset = le16toh(val);
423119784Ssam	}
42446492Swpaul
425109323Ssam	sc->sc_portnum = WI_DEFAULT_PORT;
42687383Simp
427287197Sglebius	ieee80211_ifattach(ic);
428160991Ssam	ic->ic_raw_xmit = wi_raw_xmit;
429170530Ssam	ic->ic_scan_start = wi_scan_start;
430170530Ssam	ic->ic_scan_end = wi_scan_end;
431300238Savos	ic->ic_getradiocaps = wi_getradiocaps;
432170530Ssam	ic->ic_set_channel = wi_set_channel;
433178354Ssam	ic->ic_vap_create = wi_vap_create;
434178354Ssam	ic->ic_vap_delete = wi_vap_delete;
435178354Ssam	ic->ic_update_mcast = wi_update_mcast;
436192468Ssam	ic->ic_update_promisc = wi_update_promisc;
437287197Sglebius	ic->ic_transmit = wi_transmit;
438287197Sglebius	ic->ic_parent = wi_parent;
439109323Ssam
440192468Ssam	ieee80211_radiotap_attach(ic,
441192468Ssam	    &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
442192468Ssam		WI_TX_RADIOTAP_PRESENT,
443192468Ssam	    &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
444192468Ssam		WI_RX_RADIOTAP_PRESENT);
445119784Ssam
446138571Ssam	if (bootverbose)
447138571Ssam		ieee80211_announce(ic);
448138571Ssam
449180826Simp	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
450180826Simp	    NULL, wi_intr, sc, &sc->wi_intrhand);
451180826Simp	if (error) {
452180826Simp		device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
453180826Simp		ieee80211_ifdetach(ic);
454180826Simp		wi_free(dev);
455180826Simp		return error;
456180826Simp	}
457180826Simp
458109323Ssam	return (0);
45987383Simp}
46087383Simp
461109323Ssamint
462109323Ssamwi_detach(device_t dev)
46346492Swpaul{
464109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
465287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
46646492Swpaul
467109323Ssam	WI_LOCK(sc);
46846492Swpaul
469109323Ssam	/* check if device was removed */
470123098Simp	sc->wi_gone |= !bus_child_present(dev);
47146492Swpaul
472287197Sglebius	wi_stop(sc, 0);
473150678Sru	WI_UNLOCK(sc);
474178354Ssam	ieee80211_ifdetach(ic);
47546492Swpaul
476109323Ssam	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
477109323Ssam	wi_free(dev);
478287197Sglebius	mbufq_drain(&sc->sc_snd);
479109323Ssam	mtx_destroy(&sc->sc_mtx);
480109323Ssam	return (0);
481109323Ssam}
48293359Simp
483178354Ssamstatic struct ieee80211vap *
484228621Sbschmidtwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
485228621Sbschmidt    enum ieee80211_opmode opmode, int flags,
486228621Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
487228621Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
488109323Ssam{
489286865Sadrian	struct wi_softc *sc = ic->ic_softc;
490178354Ssam	struct wi_vap *wvp;
491178354Ssam	struct ieee80211vap *vap;
49293359Simp
493178354Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
494178354Ssam		return NULL;
495287197Sglebius	wvp = malloc(sizeof(struct wi_vap), M_80211_VAP, M_WAITOK | M_ZERO);
49693359Simp
497178354Ssam	vap = &wvp->wv_vap;
498287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
49993359Simp
500178354Ssam	vap->iv_max_aid = WI_MAX_AID;
50193359Simp
502178354Ssam	switch (opmode) {
503178354Ssam	case IEEE80211_M_STA:
504178354Ssam		sc->sc_porttype = WI_PORTTYPE_BSS;
505178354Ssam		wvp->wv_newstate = vap->iv_newstate;
506178354Ssam		vap->iv_newstate = wi_newstate_sta;
507178354Ssam		/* need to filter mgt frames to avoid confusing state machine */
508178354Ssam		wvp->wv_recv_mgmt = vap->iv_recv_mgmt;
509178354Ssam		vap->iv_recv_mgmt = wi_recv_mgmt;
510109323Ssam		break;
511178354Ssam	case IEEE80211_M_IBSS:
512178354Ssam		sc->sc_porttype = sc->sc_ibss_port;
513178354Ssam		wvp->wv_newstate = vap->iv_newstate;
514178354Ssam		vap->iv_newstate = wi_newstate_sta;
515109323Ssam		break;
516178354Ssam	case IEEE80211_M_AHDEMO:
517178354Ssam		sc->sc_porttype = WI_PORTTYPE_ADHOC;
518109323Ssam		break;
519178354Ssam	case IEEE80211_M_HOSTAP:
520178354Ssam		sc->sc_porttype = WI_PORTTYPE_HOSTAP;
521178354Ssam		wvp->wv_newstate = vap->iv_newstate;
522178354Ssam		vap->iv_newstate = wi_newstate_hostap;
523178354Ssam		break;
524178354Ssam	case IEEE80211_M_MONITOR:
525178354Ssam		sc->sc_porttype = sc->sc_monitor_port;
526178354Ssam		break;
527178354Ssam	default:
528178354Ssam		break;
52993359Simp	}
530178354Ssam
531178354Ssam	/* complete setup */
532287197Sglebius	ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status, mac);
533178354Ssam	ic->ic_opmode = opmode;
534178354Ssam	return vap;
53546492Swpaul}
53646492Swpaul
537178354Ssamstatic void
538178354Ssamwi_vap_delete(struct ieee80211vap *vap)
539178354Ssam{
540178354Ssam	struct wi_vap *wvp = WI_VAP(vap);
541178354Ssam
542178354Ssam	ieee80211_vap_detach(vap);
543178354Ssam	free(wvp, M_80211_VAP);
544178354Ssam}
545178354Ssam
546194023Savgint
547109323Ssamwi_shutdown(device_t dev)
54846492Swpaul{
549109323Ssam	struct wi_softc *sc = device_get_softc(dev);
55046492Swpaul
551287197Sglebius	WI_LOCK(sc);
552178354Ssam	wi_stop(sc, 1);
553287197Sglebius	WI_UNLOCK(sc);
554194023Savg	return (0);
55546492Swpaul}
55646492Swpaul
557109323Ssamvoid
558109323Ssamwi_intr(void *arg)
55946492Swpaul{
560109323Ssam	struct wi_softc *sc = arg;
561112363Simp	u_int16_t status;
56246492Swpaul
563109323Ssam	WI_LOCK(sc);
56446492Swpaul
565287197Sglebius	if (sc->wi_gone || !sc->sc_enabled ||
566287197Sglebius	    (sc->sc_flags & WI_FLAGS_RUNNING) == 0) {
567113327Simp		CSR_WRITE_2(sc, WI_INT_EN, 0);
568123098Simp		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
569109323Ssam		WI_UNLOCK(sc);
57046492Swpaul		return;
571109323Ssam	}
57246492Swpaul
573112363Simp	/* Disable interrupts. */
574112363Simp	CSR_WRITE_2(sc, WI_INT_EN, 0);
57546492Swpaul
576112363Simp	status = CSR_READ_2(sc, WI_EVENT_STAT);
577112363Simp	if (status & WI_EV_RX)
578112363Simp		wi_rx_intr(sc);
579112363Simp	if (status & WI_EV_ALLOC)
580112363Simp		wi_tx_intr(sc);
581112363Simp	if (status & WI_EV_TX_EXC)
582112363Simp		wi_tx_ex_intr(sc);
583112363Simp	if (status & WI_EV_INFO)
584112363Simp		wi_info_intr(sc);
585287197Sglebius	if (mbufq_first(&sc->sc_snd) != NULL)
586287197Sglebius		wi_start(sc);
58746492Swpaul
588112363Simp	/* Re-enable interrupts. */
589112363Simp	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
59046492Swpaul
591109323Ssam	WI_UNLOCK(sc);
59246492Swpaul
59346492Swpaul	return;
59446492Swpaul}
59546492Swpaul
596178354Ssamstatic void
597178354Ssamwi_enable(struct wi_softc *sc)
598178354Ssam{
599178354Ssam	/* Enable interrupts */
600178354Ssam	CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
601178354Ssam
602178354Ssam	/* enable port */
603178354Ssam	wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
604178354Ssam	sc->sc_enabled = 1;
605178354Ssam}
606178354Ssam
607178354Ssamstatic int
608178354Ssamwi_setup_locked(struct wi_softc *sc, int porttype, int mode,
609287197Sglebius	const uint8_t mac[IEEE80211_ADDR_LEN])
610178354Ssam{
611178354Ssam	int i;
612178354Ssam
613178354Ssam	wi_reset(sc);
614178354Ssam
615178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, porttype);
616178354Ssam	wi_write_val(sc, WI_RID_CREATE_IBSS, mode);
617178354Ssam	wi_write_val(sc, WI_RID_MAX_DATALEN, 2304);
618178354Ssam	/* XXX IEEE80211_BPF_NOACK wants 0 */
619178354Ssam	wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 2);
620178354Ssam	if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
621178354Ssam		wi_write_val(sc, WI_RID_ROAMING_MODE, 3); /* NB: disabled */
622178354Ssam
623178354Ssam	wi_write_rid(sc, WI_RID_MAC_NODE, mac, IEEE80211_ADDR_LEN);
624178354Ssam
625178354Ssam	/* Allocate fids for the card */
626178354Ssam	sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
627178354Ssam	for (i = 0; i < sc->sc_ntxbuf; i++) {
628178354Ssam		int error = wi_alloc_fid(sc, sc->sc_buflen,
629178354Ssam		    &sc->sc_txd[i].d_fid);
630178354Ssam		if (error) {
631178354Ssam			device_printf(sc->sc_dev,
632178354Ssam			    "tx buffer allocation failed (error %u)\n",
633178354Ssam			    error);
634178354Ssam			return error;
635178354Ssam		}
636178354Ssam		sc->sc_txd[i].d_len = 0;
637178354Ssam	}
638178354Ssam	sc->sc_txcur = sc->sc_txnext = 0;
639178354Ssam
640178354Ssam	return 0;
641178354Ssam}
642178354Ssam
643287197Sglebiusvoid
644287197Sglebiuswi_init(struct wi_softc *sc)
645178354Ssam{
646178354Ssam	int wasenabled;
647178354Ssam
648178354Ssam	WI_LOCK_ASSERT(sc);
649178354Ssam
650178354Ssam	wasenabled = sc->sc_enabled;
651178354Ssam	if (wasenabled)
652287197Sglebius		wi_stop(sc, 1);
653178354Ssam
654287197Sglebius	if (wi_setup_locked(sc, sc->sc_porttype, 3,
655287197Sglebius	    sc->sc_ic.ic_macaddr) != 0) {
656287197Sglebius		device_printf(sc->sc_dev, "interface not running\n");
657287197Sglebius		wi_stop(sc, 1);
658178354Ssam		return;
659178354Ssam	}
660178354Ssam
661287197Sglebius	sc->sc_flags |= WI_FLAGS_RUNNING;
662178354Ssam
663178354Ssam	callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
664178354Ssam
665178354Ssam	wi_enable(sc);			/* Enable desired port */
666178354Ssam}
667178354Ssam
668109323Ssamvoid
669287197Sglebiuswi_stop(struct wi_softc *sc, int disable)
67046492Swpaul{
67146492Swpaul
672178354Ssam	WI_LOCK_ASSERT(sc);
67346492Swpaul
674178354Ssam	if (sc->sc_enabled && !sc->wi_gone) {
675178354Ssam		CSR_WRITE_2(sc, WI_INT_EN, 0);
676178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
677178354Ssam		if (disable)
678178354Ssam			sc->sc_enabled = 0;
679178354Ssam	} else if (sc->wi_gone && disable)	/* gone --> not enabled */
680178354Ssam		sc->sc_enabled = 0;
681178354Ssam
682178354Ssam	callout_stop(&sc->sc_watchdog);
683178354Ssam	sc->sc_tx_timer = 0;
684178354Ssam	sc->sc_false_syns = 0;
685178354Ssam
686287197Sglebius	sc->sc_flags &= ~WI_FLAGS_RUNNING;
687178354Ssam}
688178354Ssam
689178354Ssamstatic void
690300238Savoswi_getradiocaps(struct ieee80211com *ic,
691300238Savos    int maxchans, int *nchans, struct ieee80211_channel chans[])
692300238Savos{
693300238Savos	struct wi_softc *sc = ic->ic_softc;
694300292Savos	u_int8_t bands[IEEE80211_MODE_BYTES];
695300238Savos	int i;
696300238Savos
697300238Savos	memset(bands, 0, sizeof(bands));
698300238Savos	setbit(bands, IEEE80211_MODE_11B);
699300238Savos
700300238Savos	for (i = 1; i < 16; i++) {
701300238Savos		if (sc->sc_chanmask & (1 << i)) {
702300238Savos			/* XXX txpowers? */
703300238Savos			ieee80211_add_channel(chans, maxchans, nchans,
704300238Savos			    i, 0, 0, 0, bands);
705300238Savos		}
706300238Savos	}
707300238Savos}
708300238Savos
709300238Savosstatic void
710178354Ssamwi_set_channel(struct ieee80211com *ic)
711178354Ssam{
712286865Sadrian	struct wi_softc *sc = ic->ic_softc;
713160991Ssam
714178354Ssam	DPRINTF(("%s: channel %d, %sscanning\n", __func__,
715178354Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan),
716178354Ssam	    ic->ic_flags & IEEE80211_F_SCAN ? "" : "!"));
71746492Swpaul
718178354Ssam	WI_LOCK(sc);
719116951Ssam	wi_write_val(sc, WI_RID_OWN_CHNL,
720178354Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan));
721178354Ssam	WI_UNLOCK(sc);
722178354Ssam}
72375373Salfred
724178354Ssamstatic void
725178354Ssamwi_scan_start(struct ieee80211com *ic)
726178354Ssam{
727286865Sadrian	struct wi_softc *sc = ic->ic_softc;
728178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
72946492Swpaul
730178354Ssam	DPRINTF(("%s\n", __func__));
73146492Swpaul
732178354Ssam	WI_LOCK(sc);
733109323Ssam	/*
734178354Ssam	 * Switch device to monitor mode.
735109323Ssam	 */
736178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_monitor_port);
737178354Ssam	if (sc->sc_firmware_type == WI_INTERSIL) {
738178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
739178354Ssam		wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
74075373Salfred	}
741178354Ssam	/* force full dwell time to compensate for firmware overhead */
742178354Ssam	ss->ss_mindwell = ss->ss_maxdwell = msecs_to_ticks(400);
743178354Ssam	WI_UNLOCK(sc);
74446492Swpaul
745178354Ssam}
74667092Swpaul
747178354Ssamstatic void
748178354Ssamwi_scan_end(struct ieee80211com *ic)
749178354Ssam{
750286865Sadrian	struct wi_softc *sc = ic->ic_softc;
75146492Swpaul
752178354Ssam	DPRINTF(("%s: restore port type %d\n", __func__, sc->sc_porttype));
753178354Ssam
754178354Ssam	WI_LOCK(sc);
755178354Ssam	wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_porttype);
756178354Ssam	if (sc->sc_firmware_type == WI_INTERSIL) {
757178354Ssam		wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
758178354Ssam		wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
75970073Swpaul	}
760178354Ssam	WI_UNLOCK(sc);
761178354Ssam}
76270073Swpaul
763178354Ssamstatic void
764178354Ssamwi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
765283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
766178354Ssam{
767178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
76846492Swpaul
769178354Ssam	switch (subtype) {
770178354Ssam	case IEEE80211_FC0_SUBTYPE_AUTH:
771178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
772178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
773178354Ssam		/* NB: filter frames that trigger state changes */
774178354Ssam		return;
775170530Ssam	}
776283535Sadrian	WI_VAP(vap)->wv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
777178354Ssam}
77846492Swpaul
779178354Ssamstatic int
780178354Ssamwi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
781178354Ssam{
782178354Ssam	struct ieee80211com *ic = vap->iv_ic;
783178354Ssam	struct ieee80211_node *bss;
784286865Sadrian	struct wi_softc *sc = ic->ic_softc;
78594397Simp
786178354Ssam	DPRINTF(("%s: %s -> %s\n", __func__,
787178354Ssam		ieee80211_state_name[vap->iv_state],
788178354Ssam		ieee80211_state_name[nstate]));
789178354Ssam
790178354Ssam	if (nstate == IEEE80211_S_AUTH) {
791178354Ssam		WI_LOCK(sc);
792178354Ssam		wi_setup_locked(sc, WI_PORTTYPE_BSS, 3, vap->iv_myaddr);
793178354Ssam
794178354Ssam		if (vap->iv_flags & IEEE80211_F_PMGTON) {
795178354Ssam			wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
796178354Ssam			wi_write_val(sc, WI_RID_PM_ENABLED, 1);
797178354Ssam		}
798178354Ssam		wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
799178354Ssam		if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
800178354Ssam			wi_write_val(sc, WI_RID_FRAG_THRESH,
801178354Ssam			    vap->iv_fragthreshold);
802178354Ssam		wi_write_txrate(sc, vap);
803178354Ssam
804178354Ssam		bss = vap->iv_bss;
805178354Ssam		wi_write_ssid(sc, WI_RID_DESIRED_SSID, bss->ni_essid, bss->ni_esslen);
806178354Ssam		wi_write_val(sc, WI_RID_OWN_CHNL,
807178354Ssam		    ieee80211_chan2ieee(ic, bss->ni_chan));
808178354Ssam
809178354Ssam		/* Configure WEP. */
810178354Ssam		if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
811178354Ssam			wi_write_wep(sc, vap);
812178354Ssam		else
813178354Ssam			sc->sc_encryption = 0;
814178354Ssam
815178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
816178354Ssam		    (vap->iv_flags & IEEE80211_F_WPA)) {
817178354Ssam			wi_write_val(sc, WI_RID_WPA_HANDLING, 1);
818178354Ssam			if (vap->iv_appie_wpa != NULL)
819178354Ssam				wi_write_appie(sc, WI_RID_WPA_DATA,
820178354Ssam				    vap->iv_appie_wpa);
821178354Ssam		}
822178354Ssam
823178354Ssam		wi_enable(sc);		/* enable port */
824178354Ssam
825178354Ssam		/* Lucent firmware does not support the JOIN RID. */
826178354Ssam		if (sc->sc_firmware_type == WI_INTERSIL) {
827178354Ssam			struct wi_joinreq join;
828178354Ssam
829178354Ssam			memset(&join, 0, sizeof(join));
830178354Ssam			IEEE80211_ADDR_COPY(&join.wi_bssid, bss->ni_bssid);
831116951Ssam			join.wi_chan = htole16(
832178354Ssam			    ieee80211_chan2ieee(ic, bss->ni_chan));
833109323Ssam			wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
834178354Ssam		}
835178354Ssam		WI_UNLOCK(sc);
83675373Salfred
837178354Ssam		/*
838178354Ssam		 * NB: don't go through 802.11 layer, it'll send auth frame;
839178354Ssam		 * instead we drive the state machine from the link status
840178354Ssam		 * notification we get on association.
841178354Ssam		 */
842178354Ssam		vap->iv_state = nstate;
843191746Sthompsa		return (0);
84470073Swpaul	}
845178354Ssam	return WI_VAP(vap)->wv_newstate(vap, nstate, arg);
84646492Swpaul}
84746492Swpaul
848178354Ssamstatic int
849178354Ssamwi_newstate_hostap(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
85046492Swpaul{
851178354Ssam	struct ieee80211com *ic = vap->iv_ic;
852178354Ssam	struct ieee80211_node *bss;
853286865Sadrian	struct wi_softc *sc = ic->ic_softc;
854178354Ssam	int error;
85546492Swpaul
856178354Ssam	DPRINTF(("%s: %s -> %s\n", __func__,
857178354Ssam		ieee80211_state_name[vap->iv_state],
858178354Ssam		ieee80211_state_name[nstate]));
85974998Swpaul
860178354Ssam	error = WI_VAP(vap)->wv_newstate(vap, nstate, arg);
861178354Ssam	if (error == 0 && nstate == IEEE80211_S_RUN) {
862178354Ssam		WI_LOCK(sc);
863178354Ssam		wi_setup_locked(sc, WI_PORTTYPE_HOSTAP, 0, vap->iv_myaddr);
864178354Ssam
865178354Ssam		bss = vap->iv_bss;
866178354Ssam		wi_write_ssid(sc, WI_RID_OWN_SSID,
867178354Ssam		    bss->ni_essid, bss->ni_esslen);
868178354Ssam		wi_write_val(sc, WI_RID_OWN_CHNL,
869178354Ssam		    ieee80211_chan2ieee(ic, bss->ni_chan));
870178354Ssam		wi_write_val(sc, WI_RID_BASIC_RATE, 0x3);
871178354Ssam		wi_write_val(sc, WI_RID_SUPPORT_RATE, 0xf);
872178354Ssam		wi_write_txrate(sc, vap);
873178354Ssam
874178354Ssam		wi_write_val(sc, WI_RID_OWN_BEACON_INT, bss->ni_intval);
875178354Ssam		wi_write_val(sc, WI_RID_DTIM_PERIOD, vap->iv_dtim_period);
876178354Ssam
877178354Ssam		wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
878178354Ssam		if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
879178354Ssam			wi_write_val(sc, WI_RID_FRAG_THRESH,
880178354Ssam			    vap->iv_fragthreshold);
881178354Ssam
882178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_ENHSECURITY) &&
883178354Ssam		    (vap->iv_flags & IEEE80211_F_HIDESSID)) {
884178354Ssam			/*
885178354Ssam			 * bit 0 means hide SSID in beacons,
886178354Ssam			 * bit 1 means don't respond to bcast probe req
887178354Ssam			 */
888178354Ssam			wi_write_val(sc, WI_RID_ENH_SECURITY, 0x3);
88970073Swpaul		}
89070073Swpaul
891178354Ssam		if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
892178354Ssam		    (vap->iv_flags & IEEE80211_F_WPA) &&
893178354Ssam		    vap->iv_appie_wpa != NULL)
894178354Ssam			wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa);
89546492Swpaul
896178354Ssam		wi_write_val(sc, WI_RID_PROMISC, 0);
897178354Ssam
898178354Ssam		/* Configure WEP. */
899178354Ssam		if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
900178354Ssam			wi_write_wep(sc, vap);
901178354Ssam		else
902178354Ssam			sc->sc_encryption = 0;
903178354Ssam
904178354Ssam		wi_enable(sc);		/* enable port */
905178354Ssam		WI_UNLOCK(sc);
906178354Ssam	}
907178354Ssam	return error;
90846492Swpaul}
90946492Swpaul
910287197Sglebiusstatic int
911287197Sglebiuswi_transmit(struct ieee80211com *ic, struct mbuf *m)
912287197Sglebius{
913287197Sglebius	struct wi_softc *sc = ic->ic_softc;
914287197Sglebius	int error;
915287197Sglebius
916287197Sglebius	WI_LOCK(sc);
917287197Sglebius	if ((sc->sc_flags & WI_FLAGS_RUNNING) == 0) {
918287197Sglebius		WI_UNLOCK(sc);
919287197Sglebius		return (ENXIO);
920287197Sglebius	}
921287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
922287197Sglebius	if (error) {
923287197Sglebius		WI_UNLOCK(sc);
924287197Sglebius		return (error);
925287197Sglebius	}
926287197Sglebius	wi_start(sc);
927287197Sglebius	WI_UNLOCK(sc);
928287197Sglebius	return (0);
929287197Sglebius}
930287197Sglebius
931109323Ssamstatic void
932287197Sglebiuswi_start(struct wi_softc *sc)
93346492Swpaul{
934119150Ssam	struct ieee80211_node *ni;
935109323Ssam	struct ieee80211_frame *wh;
936109323Ssam	struct mbuf *m0;
937178354Ssam	struct ieee80211_key *k;
938109323Ssam	struct wi_frame frmhdr;
939190579Ssam	const struct llc *llc;
940160991Ssam	int cur;
94146492Swpaul
942165087Ssam	WI_LOCK_ASSERT(sc);
94346492Swpaul
944165087Ssam	if (sc->wi_gone)
945109323Ssam		return;
94646492Swpaul
947109323Ssam	memset(&frmhdr, 0, sizeof(frmhdr));
948109323Ssam	cur = sc->sc_txnext;
949287197Sglebius	while (sc->sc_txd[cur].d_len == 0 &&
950287197Sglebius	    (m0 = mbufq_dequeue(&sc->sc_snd)) != NULL) {
951178354Ssam		ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
95246492Swpaul
953190579Ssam		/* reconstruct 802.3 header */
954178354Ssam		wh = mtod(m0, struct ieee80211_frame *);
955190579Ssam		switch (wh->i_fc[1]) {
956190579Ssam		case IEEE80211_FC1_DIR_TODS:
957190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
958190579Ssam			    wh->i_addr2);
959190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
960190579Ssam			    wh->i_addr3);
961190579Ssam			break;
962190579Ssam		case IEEE80211_FC1_DIR_NODS:
963190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
964190579Ssam			    wh->i_addr2);
965190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
966190579Ssam			    wh->i_addr1);
967190579Ssam			break;
968190579Ssam		case IEEE80211_FC1_DIR_FROMDS:
969190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_shost,
970190579Ssam			    wh->i_addr3);
971190579Ssam			IEEE80211_ADDR_COPY(frmhdr.wi_ehdr.ether_dhost,
972190579Ssam			    wh->i_addr1);
973190579Ssam			break;
974190579Ssam		}
975190579Ssam		llc = (const struct llc *)(
976190579Ssam		    mtod(m0, const uint8_t *) + ieee80211_hdrsize(wh));
977190579Ssam		frmhdr.wi_ehdr.ether_type = llc->llc_snap.ether_type;
978109323Ssam		frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
979260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
980178354Ssam			k = ieee80211_crypto_encap(ni, m0);
981138571Ssam			if (k == NULL) {
982170530Ssam				ieee80211_free_node(ni);
983143299Ssam				m_freem(m0);
984109323Ssam				continue;
985109323Ssam			}
986138952Ssam			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
987109323Ssam		}
988178354Ssam
989192468Ssam		if (ieee80211_radiotap_active_vap(ni->ni_vap)) {
990178354Ssam			sc->sc_tx_th.wt_rate = ni->ni_txrate;
991192468Ssam			ieee80211_radiotap_tx(ni->ni_vap, m0);
992109323Ssam		}
993178354Ssam
994127697Ssam		m_copydata(m0, 0, sizeof(struct ieee80211_frame),
995127697Ssam		    (caddr_t)&frmhdr.wi_whdr);
996127697Ssam		m_adj(m0, sizeof(struct ieee80211_frame));
997127697Ssam		frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
998170530Ssam		ieee80211_free_node(ni);
999287197Sglebius		if (wi_start_tx(sc, &frmhdr, m0))
1000109323Ssam			continue;
1001178354Ssam
1002160991Ssam		sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
1003160991Ssam	}
1004165087Ssam}
1005160991Ssam
1006160991Ssamstatic int
1007287197Sglebiuswi_start_tx(struct wi_softc *sc, struct wi_frame *frmhdr, struct mbuf *m0)
1008160991Ssam{
1009160991Ssam	int cur = sc->sc_txnext;
1010160991Ssam	int fid, off, error;
1011160991Ssam
1012160991Ssam	fid = sc->sc_txd[cur].d_fid;
1013160991Ssam	off = sizeof(*frmhdr);
1014160991Ssam	error = wi_write_bap(sc, fid, 0, frmhdr, sizeof(*frmhdr)) != 0
1015160991Ssam	     || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0;
1016160991Ssam	m_freem(m0);
1017160991Ssam	if (error) {
1018287197Sglebius		counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1019160991Ssam		return -1;
1020160991Ssam	}
1021160991Ssam	sc->sc_txd[cur].d_len = off;
1022160991Ssam	if (sc->sc_txcur == cur) {
1023160991Ssam		if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) {
1024287197Sglebius			device_printf(sc->sc_dev, "xmit failed\n");
1025160991Ssam			sc->sc_txd[cur].d_len = 0;
1026160991Ssam			return -1;
1027109323Ssam		}
1028160991Ssam		sc->sc_tx_timer = 5;
1029160991Ssam	}
1030160991Ssam	return 0;
1031160991Ssam}
1032160991Ssam
1033160991Ssamstatic int
1034160991Ssamwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
1035160991Ssam	    const struct ieee80211_bpf_params *params)
1036160991Ssam{
1037160991Ssam	struct ieee80211com *ic = ni->ni_ic;
1038192468Ssam	struct ieee80211vap *vap = ni->ni_vap;
1039286865Sadrian	struct wi_softc	*sc = ic->ic_softc;
1040178354Ssam	struct ieee80211_key *k;
1041160991Ssam	struct ieee80211_frame *wh;
1042160991Ssam	struct wi_frame frmhdr;
1043160991Ssam	int cur;
1044160991Ssam	int rc = 0;
1045160991Ssam
1046160991Ssam	WI_LOCK(sc);
1047160991Ssam
1048160991Ssam	if (sc->wi_gone) {
1049160991Ssam		rc = ENETDOWN;
1050160991Ssam		goto out;
1051160991Ssam	}
1052160991Ssam	memset(&frmhdr, 0, sizeof(frmhdr));
1053160991Ssam	cur = sc->sc_txnext;
1054160991Ssam	if (sc->sc_txd[cur].d_len != 0) {
1055160991Ssam		rc = ENOBUFS;
1056160991Ssam		goto out;
1057160991Ssam	}
1058160991Ssam	m0->m_pkthdr.rcvif = NULL;
1059160991Ssam
1060160991Ssam	m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
1061160991Ssam	    (caddr_t)&frmhdr.wi_ehdr);
1062160991Ssam	frmhdr.wi_ehdr.ether_type = 0;
1063160991Ssam	wh = mtod(m0, struct ieee80211_frame *);
1064160991Ssam
1065160991Ssam	frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
1066160991Ssam	if (params && (params->ibp_flags & IEEE80211_BPF_NOACK))
1067160991Ssam		frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
1068260444Skevlo	if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
1069178354Ssam	    (!params || (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO)))) {
1070178354Ssam		k = ieee80211_crypto_encap(ni, m0);
1071178354Ssam		if (k == NULL) {
1072178354Ssam			rc = ENOMEM;
1073178354Ssam			goto out;
1074109323Ssam		}
1075178354Ssam		frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
107674838Salfred	}
1077192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
1078178354Ssam		sc->sc_tx_th.wt_rate = ni->ni_txrate;
1079192468Ssam		ieee80211_radiotap_tx(vap, m0);
1080160991Ssam	}
1081160991Ssam	m_copydata(m0, 0, sizeof(struct ieee80211_frame),
1082160991Ssam	    (caddr_t)&frmhdr.wi_whdr);
1083160991Ssam	m_adj(m0, sizeof(struct ieee80211_frame));
1084160991Ssam	frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
1085287197Sglebius	if (wi_start_tx(sc, &frmhdr, m0) < 0) {
1086168860Ssephe		m0 = NULL;
1087168860Ssephe		rc = EIO;
1088160991Ssam		goto out;
1089168860Ssephe	}
1090168860Ssephe	m0 = NULL;
1091289165Sadrian	ieee80211_free_node(ni);
109246492Swpaul
1093160991Ssam	sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
1094160991Ssamout:
1095109323Ssam	WI_UNLOCK(sc);
1096160991Ssam
1097168860Ssephe	if (m0 != NULL)
1098168860Ssephe		m_freem(m0);
1099160991Ssam	return rc;
110046492Swpaul}
110146492Swpaul
110288546Salfredstatic int
1103178354Ssamwi_reset(struct wi_softc *sc)
110446492Swpaul{
1105114124Simp#define WI_INIT_TRIES 3
1106178354Ssam	int i, error = 0;
1107112362Simp
1108178354Ssam	for (i = 0; i < WI_INIT_TRIES; i++) {
1109178354Ssam		error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0);
1110178354Ssam		if (error == 0)
111146492Swpaul			break;
1112109323Ssam		DELAY(WI_DELAY * 1000);
111346492Swpaul	}
1114114124Simp	sc->sc_reset = 1;
1115178354Ssam	if (i == WI_INIT_TRIES) {
1116287197Sglebius		device_printf(sc->sc_dev, "reset failed\n");
1117178354Ssam		return error;
111875373Salfred	}
111946492Swpaul
1120109323Ssam	CSR_WRITE_2(sc, WI_INT_EN, 0);
1121114124Simp	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
112246492Swpaul
1123109323Ssam	/* Calibrate timer. */
1124114124Simp	wi_write_val(sc, WI_RID_TICK_TIME, 8);
1125114124Simp
1126178354Ssam	return 0;
1127109323Ssam#undef WI_INIT_TRIES
112846492Swpaul}
112946492Swpaul
113088546Salfredstatic void
1131165089Ssamwi_watchdog(void *arg)
113246492Swpaul{
1133165089Ssam	struct wi_softc	*sc = arg;
113446492Swpaul
1135178354Ssam	WI_LOCK_ASSERT(sc);
1136178354Ssam
1137109323Ssam	if (!sc->sc_enabled)
1138109323Ssam		return;
113946492Swpaul
1140178354Ssam	if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) {
1141287197Sglebius		device_printf(sc->sc_dev, "device timeout\n");
1142287197Sglebius		counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1143287197Sglebius		wi_init(sc);
1144178354Ssam		return;
114546492Swpaul	}
1146165089Ssam	callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
114746492Swpaul}
114846492Swpaul
1149287197Sglebiusstatic void
1150287197Sglebiuswi_parent(struct ieee80211com *ic)
115146492Swpaul{
1152287197Sglebius	struct wi_softc *sc = ic->ic_softc;
1153287197Sglebius	int startall = 0;
115446492Swpaul
1155287197Sglebius	WI_LOCK(sc);
1156287197Sglebius	/*
1157287197Sglebius	 * Can't do promisc and hostap at the same time.  If all that's
1158287197Sglebius	 * changing is the promisc flag, try to short-circuit a call to
1159287197Sglebius	 * wi_init() by just setting PROMISC in the hardware.
1160287197Sglebius	 */
1161287197Sglebius	if (ic->ic_nrunning > 0) {
1162287197Sglebius		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
1163287197Sglebius		    sc->sc_flags & WI_FLAGS_RUNNING) {
1164287197Sglebius			if (ic->ic_promisc > 0 &&
1165287197Sglebius			    (sc->sc_flags & WI_FLAGS_PROMISC) == 0) {
1166287197Sglebius				wi_write_val(sc, WI_RID_PROMISC, 1);
1167287197Sglebius				sc->sc_flags |= WI_FLAGS_PROMISC;
1168287197Sglebius			} else if (ic->ic_promisc == 0 &&
1169287197Sglebius			    (sc->sc_flags & WI_FLAGS_PROMISC) != 0) {
1170287197Sglebius				wi_write_val(sc, WI_RID_PROMISC, 0);
1171287197Sglebius				sc->sc_flags &= ~WI_FLAGS_PROMISC;
1172100876Simp			} else {
1173287197Sglebius				wi_init(sc);
1174178354Ssam				startall = 1;
1175100876Simp			}
117646492Swpaul		} else {
1177287197Sglebius			wi_init(sc);
1178287197Sglebius			startall = 1;
117946492Swpaul		}
1180287197Sglebius	} else if (sc->sc_flags & WI_FLAGS_RUNNING) {
1181287197Sglebius		wi_stop(sc, 1);
1182287197Sglebius		sc->wi_gone = 0;
1183170530Ssam	}
1184287197Sglebius	WI_UNLOCK(sc);
1185287197Sglebius	if (startall)
1186287197Sglebius		ieee80211_start_all(ic);
1187109323Ssam}
118846492Swpaul
1189109323Ssamstatic void
1190109323Ssamwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1191109323Ssam{
1192178354Ssam	struct ieee80211vap *vap = ifp->if_softc;
1193178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1194286865Sadrian	struct wi_softc *sc = ic->ic_softc;
1195109323Ssam	u_int16_t val;
1196109323Ssam	int rate, len;
119746492Swpaul
1198109323Ssam	len = sizeof(val);
1199178354Ssam	if (sc->sc_enabled &&
1200178354Ssam	    wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
1201138571Ssam	    len == sizeof(val)) {
1202109323Ssam		/* convert to 802.11 rate */
1203138571Ssam		val = le16toh(val);
1204109323Ssam		rate = val * 2;
1205109323Ssam		if (sc->sc_firmware_type == WI_LUCENT) {
1206138571Ssam			if (rate == 10)
1207109323Ssam				rate = 11;	/* 5.5Mbps */
1208109323Ssam		} else {
1209109323Ssam			if (rate == 4*2)
1210109323Ssam				rate = 11;	/* 5.5Mbps */
1211109323Ssam			else if (rate == 8*2)
1212109323Ssam				rate = 22;	/* 11Mbps */
1213109323Ssam		}
1214178354Ssam		vap->iv_bss->ni_txrate = rate;
1215109323Ssam	}
1216178354Ssam	ieee80211_media_status(ifp, imr);
1217109323Ssam}
121846492Swpaul
1219109323Ssamstatic void
1220109323Ssamwi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
1221109323Ssam{
1222287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1223178354Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1224178354Ssam	struct ieee80211_node *ni = vap->iv_bss;
122598440Simp
1226109323Ssam	if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
1227109323Ssam		return;
122846492Swpaul
1229109323Ssam	DPRINTF(("wi_sync_bssid: bssid %s -> ", ether_sprintf(ni->ni_bssid)));
1230109323Ssam	DPRINTF(("%s ?\n", ether_sprintf(new_bssid)));
123146492Swpaul
1232109323Ssam	/* In promiscuous mode, the BSSID field is not a reliable
1233109323Ssam	 * indicator of the firmware's BSSID. Damp spurious
1234109323Ssam	 * change-of-BSSID indications.
1235109323Ssam	 */
1236287197Sglebius	if (ic->ic_promisc > 0 &&
1237138571Ssam	    !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns,
1238138571Ssam	                 WI_MAX_FALSE_SYNS))
1239109323Ssam		return;
124046492Swpaul
1241139542Ssam	sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1);
1242170530Ssam#if 0
1243139542Ssam	/*
1244139542Ssam	 * XXX hack; we should create a new node with the new bssid
1245139542Ssam	 * and replace the existing ic_bss with it but since we don't
1246139542Ssam	 * process management frames to collect state we cheat by
1247139542Ssam	 * reusing the existing node as we know wi_newstate will be
1248139542Ssam	 * called and it will overwrite the node state.
1249139542Ssam	 */
1250139542Ssam	ieee80211_sta_join(ic, ieee80211_ref_node(ni));
1251170530Ssam#endif
1252109323Ssam}
125346492Swpaul
1254178354Ssamstatic __noinline void
1255109323Ssamwi_rx_intr(struct wi_softc *sc)
1256109323Ssam{
1257287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1258109323Ssam	struct wi_frame frmhdr;
1259109323Ssam	struct mbuf *m;
1260109323Ssam	struct ieee80211_frame *wh;
1261119150Ssam	struct ieee80211_node *ni;
1262192468Ssam	int fid, len, off;
1263109323Ssam	u_int8_t dir;
1264109323Ssam	u_int16_t status;
1265192468Ssam	int8_t rssi, nf;
126646611Swpaul
1267109323Ssam	fid = CSR_READ_2(sc, WI_RX_FID);
126846611Swpaul
1269109323Ssam	/* First read in the frame header */
1270109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) {
1271109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1272287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1273109323Ssam		DPRINTF(("wi_rx_intr: read fid %x failed\n", fid));
1274109323Ssam		return;
1275109323Ssam	}
127691695Simp
1277109323Ssam	/*
1278109323Ssam	 * Drop undecryptable or packets with receive errors here
1279109323Ssam	 */
1280109323Ssam	status = le16toh(frmhdr.wi_status);
1281109323Ssam	if (status & WI_STAT_ERRSTAT) {
1282109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1283287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1284109323Ssam		DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status));
1285109323Ssam		return;
1286109323Ssam	}
128746492Swpaul
1288109323Ssam	len = le16toh(frmhdr.wi_dat_len);
1289109323Ssam	off = ALIGN(sizeof(struct ieee80211_frame));
129046563Swpaul
1291117855Ssam	/*
1292117855Ssam	 * Sometimes the PRISM2.x returns bogusly large frames. Except
1293117855Ssam	 * in monitor mode, just throw them away.
1294117855Ssam	 */
1295117855Ssam	if (off + len > MCLBYTES) {
1296117855Ssam		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1297117855Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1298287197Sglebius			counter_u64_add(ic->ic_ierrors, 1);
1299117855Ssam			DPRINTF(("wi_rx_intr: oversized packet\n"));
1300117855Ssam			return;
1301117855Ssam		} else
1302117855Ssam			len = 0;
1303117855Ssam	}
1304117855Ssam
1305178354Ssam	if (off + len > MHLEN)
1306243857Sglebius		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1307178354Ssam	else
1308243857Sglebius		m = m_gethdr(M_NOWAIT, MT_DATA);
1309109323Ssam	if (m == NULL) {
1310109323Ssam		CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
1311287197Sglebius		counter_u64_add(ic->ic_ierrors, 1);
1312109323Ssam		DPRINTF(("wi_rx_intr: MGET failed\n"));
1313109323Ssam		return;
1314109323Ssam	}
1315109323Ssam	m->m_data += off - sizeof(struct ieee80211_frame);
1316109323Ssam	memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame));
1317109323Ssam	wi_read_bap(sc, fid, sizeof(frmhdr),
1318109323Ssam	    m->m_data + sizeof(struct ieee80211_frame), len);
1319109323Ssam	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len;
132094405Simp
1321109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
132246492Swpaul
1323192468Ssam	rssi = frmhdr.wi_rx_signal;
1324192468Ssam	nf = frmhdr.wi_rx_silence;
1325192468Ssam	if (ieee80211_radiotap_active(ic)) {
1326192468Ssam		struct wi_rx_radiotap_header *tap = &sc->sc_rx_th;
1327192468Ssam		uint32_t rstamp;
1328192468Ssam
1329192468Ssam		rstamp = (le16toh(frmhdr.wi_rx_tstamp0) << 16) |
1330192468Ssam		    le16toh(frmhdr.wi_rx_tstamp1);
1331192517Ssam		tap->wr_tsf = htole64((uint64_t)rstamp);
1332123927Ssam		/* XXX replace divide by table */
1333192468Ssam		tap->wr_rate = frmhdr.wi_rx_rate / 5;
1334192468Ssam		tap->wr_flags = 0;
1335123927Ssam		if (frmhdr.wi_status & WI_STAT_PCF)
1336192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_CFP;
1337178354Ssam		if (m->m_flags & M_WEP)
1338192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_WEP;
1339192468Ssam		tap->wr_antsignal = rssi;
1340192468Ssam		tap->wr_antnoise = nf;
134156965Swpaul	}
134256965Swpaul
1343109323Ssam	/* synchronize driver's BSSID with firmware's BSSID */
1344178354Ssam	wh = mtod(m, struct ieee80211_frame *);
1345109323Ssam	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
1346109323Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
1347109323Ssam		wi_sync_bssid(sc, wh->i_addr3);
134846492Swpaul
1349165088Ssam	WI_UNLOCK(sc);
1350165088Ssam
1351178354Ssam	ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
1352178354Ssam	if (ni != NULL) {
1353192468Ssam		(void) ieee80211_input(ni, m, rssi, nf);
1354178354Ssam		ieee80211_free_node(ni);
1355178354Ssam	} else
1356192468Ssam		(void) ieee80211_input_all(ic, m, rssi, nf);
1357178354Ssam
1358165088Ssam	WI_LOCK(sc);
1359109323Ssam}
136046492Swpaul
1361178354Ssamstatic __noinline void
1362109323Ssamwi_tx_ex_intr(struct wi_softc *sc)
1363109323Ssam{
1364109323Ssam	struct wi_frame frmhdr;
1365109323Ssam	int fid;
136646492Swpaul
1367109323Ssam	fid = CSR_READ_2(sc, WI_TX_CMP_FID);
1368109323Ssam	/* Read in the frame header */
1369109323Ssam	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
1370109323Ssam		u_int16_t status = le16toh(frmhdr.wi_status);
1371109323Ssam		/*
1372109323Ssam		 * Spontaneous station disconnects appear as xmit
1373109323Ssam		 * errors.  Don't announce them and/or count them
1374109323Ssam		 * as an output error.
1375109323Ssam		 */
1376109323Ssam		if ((status & WI_TXSTAT_DISCONNECT) == 0) {
1377109323Ssam			if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
1378287197Sglebius				device_printf(sc->sc_dev, "tx failed");
1379109323Ssam				if (status & WI_TXSTAT_RET_ERR)
1380109323Ssam					printf(", retry limit exceeded");
1381109323Ssam				if (status & WI_TXSTAT_AGED_ERR)
1382109323Ssam					printf(", max transmit lifetime exceeded");
1383109323Ssam				if (status & WI_TXSTAT_DISCONNECT)
1384109323Ssam					printf(", port disconnected");
1385109323Ssam				if (status & WI_TXSTAT_FORM_ERR)
1386109323Ssam					printf(", invalid format (data len %u src %6D)",
1387109323Ssam						le16toh(frmhdr.wi_dat_len),
1388109323Ssam						frmhdr.wi_ehdr.ether_shost, ":");
1389109323Ssam				if (status & ~0xf)
1390109323Ssam					printf(", status=0x%x", status);
1391109323Ssam				printf("\n");
1392109323Ssam			}
1393287197Sglebius			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
1394272068Sglebius		} else
1395109323Ssam			DPRINTF(("port disconnected\n"));
1396109323Ssam	} else
1397109323Ssam		DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
1398109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
1399109323Ssam}
140046492Swpaul
1401178354Ssamstatic __noinline void
1402109323Ssamwi_tx_intr(struct wi_softc *sc)
1403109323Ssam{
1404109323Ssam	int fid, cur;
140594405Simp
1406123098Simp	if (sc->wi_gone)
1407123098Simp		return;
1408123098Simp
1409109323Ssam	fid = CSR_READ_2(sc, WI_ALLOC_FID);
1410109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
141146492Swpaul
1412109323Ssam	cur = sc->sc_txcur;
1413109323Ssam	if (sc->sc_txd[cur].d_fid != fid) {
1414287197Sglebius		device_printf(sc->sc_dev, "bad alloc %x != %x, cur %d nxt %d\n",
1415109323Ssam		    fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext);
1416109323Ssam		return;
1417109323Ssam	}
1418109323Ssam	sc->sc_tx_timer = 0;
1419109323Ssam	sc->sc_txd[cur].d_len = 0;
1420112363Simp	sc->sc_txcur = cur = (cur + 1) % sc->sc_ntxbuf;
1421287197Sglebius	if (sc->sc_txd[cur].d_len != 0) {
1422109323Ssam		if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid,
1423109323Ssam		    0, 0)) {
1424287197Sglebius			device_printf(sc->sc_dev, "xmit failed\n");
1425109323Ssam			sc->sc_txd[cur].d_len = 0;
1426109323Ssam		} else {
1427109323Ssam			sc->sc_tx_timer = 5;
1428109323Ssam		}
1429109323Ssam	}
143046492Swpaul}
143146492Swpaul
1432178354Ssamstatic __noinline void
1433109323Ssamwi_info_intr(struct wi_softc *sc)
143494405Simp{
1435287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1436178354Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1437109323Ssam	int i, fid, len, off;
1438109323Ssam	u_int16_t ltbuf[2];
1439109323Ssam	u_int16_t stat;
1440109323Ssam	u_int32_t *ptr;
144194405Simp
1442109323Ssam	fid = CSR_READ_2(sc, WI_INFO_FID);
1443109323Ssam	wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf));
144494405Simp
1445109323Ssam	switch (le16toh(ltbuf[1])) {
1446109323Ssam	case WI_INFO_LINK_STAT:
1447109323Ssam		wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat));
1448109323Ssam		DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat)));
1449232147Sadrian
1450232147Sadrian		if (vap == NULL)
1451232147Sadrian			goto finish;
1452232147Sadrian
1453109323Ssam		switch (le16toh(stat)) {
1454109323Ssam		case WI_INFO_LINK_STAT_CONNECTED:
1455178354Ssam			if (vap->iv_state == IEEE80211_S_RUN &&
1456178354Ssam			    vap->iv_opmode != IEEE80211_M_IBSS)
1457109323Ssam				break;
1458178354Ssam			/* fall thru... */
1459109323Ssam		case WI_INFO_LINK_STAT_AP_CHG:
1460191746Sthompsa			IEEE80211_LOCK(ic);
1461191746Sthompsa			vap->iv_bss->ni_associd = 1 | 0xc000;	/* NB: anything will do */
1462191746Sthompsa			ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
1463191746Sthompsa			IEEE80211_UNLOCK(ic);
1464109323Ssam			break;
1465109323Ssam		case WI_INFO_LINK_STAT_AP_INR:
1466109323Ssam			break;
1467178354Ssam		case WI_INFO_LINK_STAT_DISCONNECTED:
1468178354Ssam			/* we dropped off the net; e.g. due to deauth/disassoc */
1469191746Sthompsa			IEEE80211_LOCK(ic);
1470191746Sthompsa			vap->iv_bss->ni_associd = 0;
1471191746Sthompsa			vap->iv_stats.is_rx_deauth++;
1472191746Sthompsa			ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
1473191746Sthompsa			IEEE80211_UNLOCK(ic);
1474178354Ssam			break;
1475109323Ssam		case WI_INFO_LINK_STAT_AP_OOR:
1476178354Ssam			/* XXX does this need to be per-vap? */
1477191746Sthompsa			ieee80211_beacon_miss(ic);
1478109323Ssam			break;
1479109323Ssam		case WI_INFO_LINK_STAT_ASSOC_FAILED:
1480178354Ssam			if (vap->iv_opmode == IEEE80211_M_STA)
1481191746Sthompsa				ieee80211_new_state(vap, IEEE80211_S_SCAN,
1482191746Sthompsa				    IEEE80211_SCAN_FAIL_TIMEOUT);
1483109323Ssam			break;
1484109323Ssam		}
1485109323Ssam		break;
1486109323Ssam	case WI_INFO_COUNTERS:
1487109323Ssam		/* some card versions have a larger stats structure */
1488109323Ssam		len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4);
1489109323Ssam		ptr = (u_int32_t *)&sc->sc_stats;
1490109323Ssam		off = sizeof(ltbuf);
1491109323Ssam		for (i = 0; i < len; i++, off += 2, ptr++) {
1492109323Ssam			wi_read_bap(sc, fid, off, &stat, sizeof(stat));
1493109323Ssam#ifdef WI_HERMES_STATS_WAR
1494109323Ssam			if (stat & 0xf000)
1495109323Ssam				stat = ~stat;
1496109323Ssam#endif
1497109323Ssam			*ptr += stat;
1498109323Ssam		}
1499109323Ssam		break;
1500109323Ssam	default:
1501109323Ssam		DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid,
1502109323Ssam		    le16toh(ltbuf[1]), le16toh(ltbuf[0])));
1503109323Ssam		break;
150494405Simp	}
1505232147Sadrianfinish:
1506109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO);
1507109323Ssam}
150894405Simp
1509109323Ssamstatic int
1510109323Ssamwi_write_multi(struct wi_softc *sc)
1511109323Ssam{
1512287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1513287197Sglebius	struct ieee80211vap *vap;
1514287197Sglebius	struct wi_mcast mlist;
1515286437Sadrian	int n;
151694472Simp
1517287197Sglebius	if (ic->ic_allmulti > 0 || ic->ic_promisc > 0) {
1518109323Ssamallmulti:
1519109323Ssam		memset(&mlist, 0, sizeof(mlist));
1520109323Ssam		return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1521109323Ssam		    sizeof(mlist));
152294405Simp	}
152394405Simp
1524109323Ssam	n = 0;
1525287197Sglebius	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1526287197Sglebius		struct ifnet *ifp;
1527287197Sglebius		struct ifmultiaddr *ifma;
1528287197Sglebius
1529287197Sglebius		ifp = vap->iv_ifp;
1530287197Sglebius		if_maddr_rlock(ifp);
1531287197Sglebius		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1532287197Sglebius			if (ifma->ifma_addr->sa_family != AF_LINK)
1533287197Sglebius				continue;
1534287197Sglebius			if (n >= 16)
1535287197Sglebius				goto allmulti;
1536287197Sglebius			IEEE80211_ADDR_COPY(&mlist.wi_mcast[n],
1537287197Sglebius			    (LLADDR((struct sockaddr_dl *)ifma->ifma_addr)));
1538287197Sglebius			n++;
1539287197Sglebius		}
1540287197Sglebius		if_maddr_runlock(ifp);
154194405Simp	}
1542109323Ssam	return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist,
1543109323Ssam	    IEEE80211_ADDR_LEN * n);
154494405Simp}
154594405Simp
154694405Simpstatic void
1547283540Sglebiuswi_update_mcast(struct ieee80211com *ic)
1548178354Ssam{
1549283540Sglebius
1550283540Sglebius	wi_write_multi(ic->ic_softc);
1551178354Ssam}
1552178354Ssam
1553178354Ssamstatic void
1554283540Sglebiuswi_update_promisc(struct ieee80211com *ic)
1555192468Ssam{
1556283540Sglebius	struct wi_softc *sc = ic->ic_softc;
1557192468Ssam
1558192468Ssam	WI_LOCK(sc);
1559192468Ssam	/* XXX handle WEP special case handling? */
1560192468Ssam	wi_write_val(sc, WI_RID_PROMISC,
1561192468Ssam	    (ic->ic_opmode == IEEE80211_M_MONITOR ||
1562287197Sglebius	     (ic->ic_promisc > 0)));
1563192468Ssam	WI_UNLOCK(sc);
1564192468Ssam}
1565192468Ssam
1566192468Ssamstatic void
1567109323Ssamwi_read_nicid(struct wi_softc *sc)
156846492Swpaul{
1569109323Ssam	struct wi_card_ident *id;
1570109323Ssam	char *p;
1571109323Ssam	int len;
1572109323Ssam	u_int16_t ver[4];
157346492Swpaul
1574109323Ssam	/* getting chip identity */
1575109323Ssam	memset(ver, 0, sizeof(ver));
1576109323Ssam	len = sizeof(ver);
1577109323Ssam	wi_read_rid(sc, WI_RID_CARD_ID, ver, &len);
157846492Swpaul
1579109323Ssam	sc->sc_firmware_type = WI_NOTYPE;
1580180919Simp	sc->sc_nic_id = le16toh(ver[0]);
1581109323Ssam	for (id = wi_card_ident; id->card_name != NULL; id++) {
1582180919Simp		if (sc->sc_nic_id == id->card_id) {
1583180919Simp			sc->sc_nic_name = id->card_name;
1584109323Ssam			sc->sc_firmware_type = id->firm_type;
1585109323Ssam			break;
1586109323Ssam		}
158767092Swpaul	}
1588109323Ssam	if (sc->sc_firmware_type == WI_NOTYPE) {
1589180919Simp		if (sc->sc_nic_id & 0x8000) {
1590109323Ssam			sc->sc_firmware_type = WI_INTERSIL;
1591180919Simp			sc->sc_nic_name = "Unknown Prism chip";
1592109323Ssam		} else {
1593109323Ssam			sc->sc_firmware_type = WI_LUCENT;
1594180919Simp			sc->sc_nic_name = "Unknown Lucent chip";
1595109323Ssam		}
159667092Swpaul	}
1597181210Simp	if (bootverbose)
1598181210Simp		device_printf(sc->sc_dev, "using %s\n", sc->sc_nic_name);
159946492Swpaul
1600109323Ssam	/* get primary firmware version (Only Prism chips) */
1601109323Ssam	if (sc->sc_firmware_type != WI_LUCENT) {
1602109323Ssam		memset(ver, 0, sizeof(ver));
1603109323Ssam		len = sizeof(ver);
1604109323Ssam		wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len);
1605109323Ssam		sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 +
1606109323Ssam		    le16toh(ver[3]) * 100 + le16toh(ver[1]);
160767092Swpaul	}
160846492Swpaul
1609109323Ssam	/* get station firmware version */
1610109323Ssam	memset(ver, 0, sizeof(ver));
1611109323Ssam	len = sizeof(ver);
1612109323Ssam	wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len);
1613109323Ssam	sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 +
1614109323Ssam	    le16toh(ver[3]) * 100 + le16toh(ver[1]);
1615109323Ssam	if (sc->sc_firmware_type == WI_INTERSIL &&
1616109323Ssam	    (sc->sc_sta_firmware_ver == 10102 ||
1617109323Ssam	     sc->sc_sta_firmware_ver == 20102)) {
1618109323Ssam		char ident[12];
1619109323Ssam		memset(ident, 0, sizeof(ident));
1620109323Ssam		len = sizeof(ident);
1621109323Ssam		/* value should be the format like "V2.00-11" */
1622109323Ssam		if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 &&
1623109323Ssam		    *(p = (char *)ident) >= 'A' &&
1624109323Ssam		    p[2] == '.' && p[5] == '-' && p[8] == '\0') {
1625109323Ssam			sc->sc_firmware_type = WI_SYMBOL;
1626109323Ssam			sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 +
1627109323Ssam			    (p[3] - '0') * 1000 + (p[4] - '0') * 100 +
1628109323Ssam			    (p[6] - '0') * 10 + (p[7] - '0');
162994405Simp		}
163094405Simp	}
1631180919Simp	if (bootverbose) {
1632180919Simp		device_printf(sc->sc_dev, "%s Firmware: ",
1633180919Simp		    wi_firmware_names[sc->sc_firmware_type]);
1634180919Simp		if (sc->sc_firmware_type != WI_LUCENT)	/* XXX */
1635180919Simp			printf("Primary (%u.%u.%u), ",
1636180919Simp			    sc->sc_pri_firmware_ver / 10000,
1637180919Simp			    (sc->sc_pri_firmware_ver % 10000) / 100,
1638180919Simp			    sc->sc_pri_firmware_ver % 100);
1639180919Simp		printf("Station (%u.%u.%u)\n",
1640180919Simp		    sc->sc_sta_firmware_ver / 10000,
1641180919Simp		    (sc->sc_sta_firmware_ver % 10000) / 100,
1642180919Simp		    sc->sc_sta_firmware_ver % 100);
1643180919Simp	}
1644109323Ssam}
164546492Swpaul
1646109323Ssamstatic int
1647109323Ssamwi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen)
1648109323Ssam{
1649109323Ssam	struct wi_ssid ssid;
165046492Swpaul
1651109323Ssam	if (buflen > IEEE80211_NWID_LEN)
1652109323Ssam		return ENOBUFS;
1653109323Ssam	memset(&ssid, 0, sizeof(ssid));
1654109323Ssam	ssid.wi_len = htole16(buflen);
1655109323Ssam	memcpy(ssid.wi_ssid, buf, buflen);
1656109323Ssam	return wi_write_rid(sc, rid, &ssid, sizeof(ssid));
1657109323Ssam}
165846492Swpaul
1659109323Ssamstatic int
1660178354Ssamwi_write_txrate(struct wi_softc *sc, struct ieee80211vap *vap)
1661109323Ssam{
1662178354Ssam	static const uint16_t lucent_rates[12] = {
1663178354Ssam	    [ 0] = 3,	/* auto */
1664178354Ssam	    [ 1] = 1,	/* 1Mb/s */
1665178354Ssam	    [ 2] = 2,	/* 2Mb/s */
1666178354Ssam	    [ 5] = 4,	/* 5.5Mb/s */
1667178354Ssam	    [11] = 5	/* 11Mb/s */
1668178354Ssam	};
1669178354Ssam	static const uint16_t intersil_rates[12] = {
1670178354Ssam	    [ 0] = 0xf,	/* auto */
1671178354Ssam	    [ 1] = 0,	/* 1Mb/s */
1672178354Ssam	    [ 2] = 1,	/* 2Mb/s */
1673178354Ssam	    [ 5] = 2,	/* 5.5Mb/s */
1674178354Ssam	    [11] = 3,	/* 11Mb/s */
1675178354Ssam	};
1676178354Ssam	const uint16_t *rates = sc->sc_firmware_type == WI_LUCENT ?
1677178354Ssam	    lucent_rates : intersil_rates;
1678178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1679178354Ssam	const struct ieee80211_txparam *tp;
168046492Swpaul
1681178354Ssam	tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
1682178354Ssam	return wi_write_val(sc, WI_RID_TX_RATE,
1683178354Ssam	    (tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
1684178354Ssam		rates[0] : rates[tp->ucastrate / 2]));
168546492Swpaul}
168646492Swpaul
1687109323Ssamstatic int
1688178354Ssamwi_write_wep(struct wi_softc *sc, struct ieee80211vap *vap)
168946492Swpaul{
1690109323Ssam	int error = 0;
1691109323Ssam	int i, keylen;
1692109323Ssam	u_int16_t val;
1693109323Ssam	struct wi_key wkey[IEEE80211_WEP_NKID];
169446492Swpaul
1695109323Ssam	switch (sc->sc_firmware_type) {
1696109323Ssam	case WI_LUCENT:
1697178354Ssam		val = (vap->iv_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
1698109323Ssam		error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
1699109323Ssam		if (error)
1700109323Ssam			break;
1701178354Ssam		if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
1702138988Smdodd			break;
1703178354Ssam		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, vap->iv_def_txkey);
1704138988Smdodd		if (error)
1705138988Smdodd			break;
1706138988Smdodd		memset(wkey, 0, sizeof(wkey));
1707138988Smdodd		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
1708178354Ssam			keylen = vap->iv_nw_keys[i].wk_keylen;
1709138988Smdodd			wkey[i].wi_keylen = htole16(keylen);
1710178354Ssam			memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key,
1711138988Smdodd			    keylen);
1712109323Ssam		}
1713138988Smdodd		error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS,
1714138988Smdodd		    wkey, sizeof(wkey));
1715150798Savatar		sc->sc_encryption = 0;
1716109323Ssam		break;
1717109323Ssam
1718109323Ssam	case WI_INTERSIL:
1719178354Ssam		val = HOST_ENCRYPT | HOST_DECRYPT;
1720178354Ssam		if (vap->iv_flags & IEEE80211_F_PRIVACY) {
1721109323Ssam			/*
1722109323Ssam			 * ONLY HWB3163 EVAL-CARD Firmware version
1723109323Ssam			 * less than 0.8 variant2
1724109323Ssam			 *
1725109323Ssam			 *   If promiscuous mode disable, Prism2 chip
1726109323Ssam			 *  does not work with WEP .
1727109323Ssam			 * It is under investigation for details.
1728109323Ssam			 * (ichiro@netbsd.org)
1729109323Ssam			 */
1730178354Ssam			if (sc->sc_sta_firmware_ver < 802 ) {
1731109323Ssam				/* firm ver < 0.8 variant 2 */
1732109323Ssam				wi_write_val(sc, WI_RID_PROMISC, 1);
1733109323Ssam			}
1734109323Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE,
1735178354Ssam			    vap->iv_bss->ni_authmode);
1736178354Ssam			val |= PRIVACY_INVOKED;
1737109323Ssam		} else {
1738178354Ssam			wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN);
1739109323Ssam		}
1740109323Ssam		error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
1741109323Ssam		if (error)
1742109323Ssam			break;
1743150798Savatar		sc->sc_encryption = val;
1744138988Smdodd		if ((val & PRIVACY_INVOKED) == 0)
1745138988Smdodd			break;
1746178354Ssam		error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, vap->iv_def_txkey);
1747109323Ssam		break;
1748109323Ssam	}
1749109323Ssam	return error;
175046492Swpaul}
175146492Swpaul
1752192492Simpstatic int
1753109323Ssamwi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
175446492Swpaul{
1755178354Ssam	int i, s = 0;
1756178354Ssam
1757123098Simp	if (sc->wi_gone)
1758123098Simp		return (ENODEV);
1759123098Simp
1760109323Ssam	/* wait for the busy bit to clear */
1761123339Simp	for (i = sc->wi_cmd_count; i > 0; i--) {	/* 500ms */
1762116206Simp		if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY))
1763109323Ssam			break;
1764123098Simp		DELAY(1*1000);	/* 1ms */
1765109323Ssam	}
1766109323Ssam	if (i == 0) {
1767178354Ssam		device_printf(sc->sc_dev, "%s: busy bit won't clear, cmd 0x%x\n",
1768178354Ssam		   __func__, cmd);
1769123098Simp		sc->wi_gone = 1;
1770109323Ssam		return(ETIMEDOUT);
1771109323Ssam	}
177290580Sbrooks
1773109323Ssam	CSR_WRITE_2(sc, WI_PARAM0, val0);
1774109323Ssam	CSR_WRITE_2(sc, WI_PARAM1, val1);
1775109323Ssam	CSR_WRITE_2(sc, WI_PARAM2, val2);
1776109323Ssam	CSR_WRITE_2(sc, WI_COMMAND, cmd);
177790580Sbrooks
1778109323Ssam	if (cmd == WI_CMD_INI) {
1779109323Ssam		/* XXX: should sleep here. */
1780116206Simp		DELAY(100*1000);		/* 100ms delay for init */
1781109323Ssam	}
1782109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
1783109323Ssam		/*
1784109323Ssam		 * Wait for 'command complete' bit to be
1785109323Ssam		 * set in the event status register.
1786109323Ssam		 */
1787109323Ssam		s = CSR_READ_2(sc, WI_EVENT_STAT);
1788109323Ssam		if (s & WI_EV_CMD) {
1789109323Ssam			/* Ack the event and read result code. */
1790109323Ssam			s = CSR_READ_2(sc, WI_STATUS);
1791109323Ssam			CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
1792109323Ssam			if (s & WI_STAT_CMD_RESULT) {
1793109323Ssam				return(EIO);
1794109323Ssam			}
1795109323Ssam			break;
179690580Sbrooks		}
1797109323Ssam		DELAY(WI_DELAY);
1798109323Ssam	}
179990580Sbrooks
1800109323Ssam	if (i == WI_TIMEOUT) {
1801178354Ssam		device_printf(sc->sc_dev, "%s: timeout on cmd 0x%04x; "
1802178354Ssam		    "event status 0x%04x\n", __func__, cmd, s);
1803123098Simp		if (s == 0xffff)
1804123098Simp			sc->wi_gone = 1;
1805109323Ssam		return(ETIMEDOUT);
180653702Swpaul	}
1807109323Ssam	return (0);
1808109323Ssam}
180953702Swpaul
1810109323Ssamstatic int
1811109323Ssamwi_seek_bap(struct wi_softc *sc, int id, int off)
1812109323Ssam{
1813109323Ssam	int i, status;
181490580Sbrooks
1815109323Ssam	CSR_WRITE_2(sc, WI_SEL0, id);
1816109323Ssam	CSR_WRITE_2(sc, WI_OFF0, off);
181790580Sbrooks
1818109323Ssam	for (i = 0; ; i++) {
1819109323Ssam		status = CSR_READ_2(sc, WI_OFF0);
1820109323Ssam		if ((status & WI_OFF_BUSY) == 0)
1821109323Ssam			break;
1822109323Ssam		if (i == WI_TIMEOUT) {
1823178354Ssam			device_printf(sc->sc_dev, "%s: timeout, id %x off %x\n",
1824178354Ssam			    __func__, id, off);
1825109323Ssam			sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
1826123098Simp			if (status == 0xffff)
1827123098Simp				sc->wi_gone = 1;
1828109323Ssam			return ETIMEDOUT;
1829109323Ssam		}
1830109323Ssam		DELAY(1);
183153702Swpaul	}
1832109323Ssam	if (status & WI_OFF_ERR) {
1833178354Ssam		device_printf(sc->sc_dev, "%s: error, id %x off %x\n",
1834178354Ssam		    __func__, id, off);
1835109323Ssam		sc->sc_bap_off = WI_OFF_ERR;	/* invalidate */
1836109323Ssam		return EIO;
1837109323Ssam	}
1838109323Ssam	sc->sc_bap_id = id;
1839109323Ssam	sc->sc_bap_off = off;
1840109323Ssam	return 0;
184153702Swpaul}
184253702Swpaul
1843109323Ssamstatic int
1844109323Ssamwi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
184553702Swpaul{
1846253756Sjhibbits	int error, cnt;
184753702Swpaul
1848109323Ssam	if (buflen == 0)
1849109323Ssam		return 0;
1850109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
1851109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
1852109323Ssam			return error;
185375219Salfred	}
1854109323Ssam	cnt = (buflen + 1) / 2;
1855253756Sjhibbits	CSR_READ_MULTI_STREAM_2(sc, WI_DATA0, (u_int16_t *)buf, cnt);
1856109323Ssam	sc->sc_bap_off += cnt * 2;
1857109323Ssam	return 0;
185853702Swpaul}
185953702Swpaul
1860109323Ssamstatic int
1861287197Sglebiuswi_write_bap(struct wi_softc *sc, int id, int off, const void *buf, int buflen)
186253702Swpaul{
1863253756Sjhibbits	int error, cnt;
186446492Swpaul
1865109323Ssam	if (buflen == 0)
1866109323Ssam		return 0;
186746492Swpaul
1868109323Ssam	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
1869109323Ssam		if ((error = wi_seek_bap(sc, id, off)) != 0)
1870109323Ssam			return error;
1871109323Ssam	}
1872109323Ssam	cnt = (buflen + 1) / 2;
1873287197Sglebius	CSR_WRITE_MULTI_STREAM_2(sc, WI_DATA0, (const uint16_t *)buf, cnt);
1874109323Ssam	sc->sc_bap_off += cnt * 2;
1875109323Ssam
1876109323Ssam	return 0;
187746492Swpaul}
187853702Swpaul
1879109323Ssamstatic int
1880109323Ssamwi_mwrite_bap(struct wi_softc *sc, int id, int off, struct mbuf *m0, int totlen)
1881109323Ssam{
1882109323Ssam	int error, len;
1883109323Ssam	struct mbuf *m;
188453702Swpaul
1885109323Ssam	for (m = m0; m != NULL && totlen > 0; m = m->m_next) {
1886109323Ssam		if (m->m_len == 0)
1887109323Ssam			continue;
188853702Swpaul
1889109323Ssam		len = min(m->m_len, totlen);
189053702Swpaul
1891109323Ssam		if (((u_long)m->m_data) % 2 != 0 || len % 2 != 0) {
1892109323Ssam			m_copydata(m, 0, totlen, (caddr_t)&sc->sc_txbuf);
1893109323Ssam			return wi_write_bap(sc, id, off, (caddr_t)&sc->sc_txbuf,
1894109323Ssam			    totlen);
1895109323Ssam		}
189653702Swpaul
1897109323Ssam		if ((error = wi_write_bap(sc, id, off, m->m_data, len)) != 0)
1898109323Ssam			return error;
189953702Swpaul
1900109323Ssam		off += m->m_len;
1901109323Ssam		totlen -= len;
1902109323Ssam	}
1903109323Ssam	return 0;
1904109323Ssam}
190553702Swpaul
1906109323Ssamstatic int
1907109323Ssamwi_alloc_fid(struct wi_softc *sc, int len, int *idp)
190853702Swpaul{
190953702Swpaul	int i;
191053702Swpaul
1911109323Ssam	if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
1912178354Ssam		device_printf(sc->sc_dev, "%s: failed to allocate %d bytes on NIC\n",
1913178354Ssam		    __func__, len);
1914109323Ssam		return ENOMEM;
191553702Swpaul	}
191653702Swpaul
1917109323Ssam	for (i = 0; i < WI_TIMEOUT; i++) {
1918109323Ssam		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
1919109323Ssam			break;
1920109323Ssam		DELAY(1);
192153702Swpaul	}
1922144167Ssam	if (i == WI_TIMEOUT) {
1923178354Ssam		device_printf(sc->sc_dev, "%s: timeout in alloc\n", __func__);
1924144167Ssam		return ETIMEDOUT;
1925144167Ssam	}
1926109323Ssam	*idp = CSR_READ_2(sc, WI_ALLOC_FID);
1927109323Ssam	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
1928109323Ssam	return 0;
1929109323Ssam}
193053702Swpaul
1931109323Ssamstatic int
1932109323Ssamwi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp)
1933109323Ssam{
1934109323Ssam	int error, len;
1935109323Ssam	u_int16_t ltbuf[2];
193653702Swpaul
1937109323Ssam	/* Tell the NIC to enter record read mode. */
1938109323Ssam	error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0);
1939109323Ssam	if (error)
1940109323Ssam		return error;
194153702Swpaul
1942109323Ssam	error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
1943109323Ssam	if (error)
1944109323Ssam		return error;
194553702Swpaul
1946109323Ssam	if (le16toh(ltbuf[1]) != rid) {
1947109323Ssam		device_printf(sc->sc_dev, "record read mismatch, rid=%x, got=%x\n",
1948109323Ssam		    rid, le16toh(ltbuf[1]));
1949109323Ssam		return EIO;
195053702Swpaul	}
1951109323Ssam	len = (le16toh(ltbuf[0]) - 1) * 2;	 /* already got rid */
1952109323Ssam	if (*buflenp < len) {
1953109323Ssam		device_printf(sc->sc_dev, "record buffer is too small, "
1954109323Ssam		    "rid=%x, size=%d, len=%d\n",
1955109323Ssam		    rid, *buflenp, len);
1956109323Ssam		return ENOSPC;
195753702Swpaul	}
1958109323Ssam	*buflenp = len;
1959109323Ssam	return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len);
1960109323Ssam}
196153702Swpaul
1962109323Ssamstatic int
1963287197Sglebiuswi_write_rid(struct wi_softc *sc, int rid, const void *buf, int buflen)
1964109323Ssam{
1965109323Ssam	int error;
1966109323Ssam	u_int16_t ltbuf[2];
196753702Swpaul
1968109323Ssam	ltbuf[0] = htole16((buflen + 1) / 2 + 1);	 /* includes rid */
1969109323Ssam	ltbuf[1] = htole16(rid);
197053702Swpaul
1971109323Ssam	error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
1972178354Ssam	if (error) {
1973178354Ssam		device_printf(sc->sc_dev, "%s: bap0 write failure, rid 0x%x\n",
1974178354Ssam		    __func__, rid);
1975109323Ssam		return error;
1976178354Ssam	}
1977109323Ssam	error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen);
1978178354Ssam	if (error) {
1979178354Ssam		device_printf(sc->sc_dev, "%s: bap1 write failure, rid 0x%x\n",
1980178354Ssam		    __func__, rid);
1981109323Ssam		return error;
1982178354Ssam	}
1983102206Simp
1984109323Ssam	return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0);
198553702Swpaul}
198677217Sphk
198788546Salfredstatic int
1988178354Ssamwi_write_appie(struct wi_softc *sc, int rid, const struct ieee80211_appie *ie)
198977217Sphk{
1990178354Ssam	/* NB: 42 bytes is probably ok to have on the stack */
1991178354Ssam	char buf[sizeof(uint16_t) + 40];
199277217Sphk
1993178354Ssam	if (ie->ie_len > 40)
1994178354Ssam		return EINVAL;
1995178354Ssam	/* NB: firmware requires 16-bit ie length before ie data */
1996178354Ssam	*(uint16_t *) buf = htole16(ie->ie_len);
1997178354Ssam	memcpy(buf + sizeof(uint16_t), ie->ie_data, ie->ie_len);
1998178354Ssam	return wi_write_rid(sc, rid, buf, ie->ie_len + sizeof(uint16_t));
199977217Sphk}
200077217Sphk
2001300238Savosstatic u_int16_t
2002300238Savoswi_read_chanmask(struct wi_softc *sc)
2003300238Savos{
2004300238Savos	u_int16_t val;
2005300238Savos	int buflen;
2006300238Savos
2007300238Savos	buflen = sizeof(val);
2008300238Savos	if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0)
2009300238Savos		val = htole16(0x1fff);	/* assume 1-13 */
2010300238Savos	KASSERT(val != 0, ("%s: no available channels listed!", __func__));
2011300238Savos
2012300238Savos	val <<= 1;			/* shift for base 1 indices */
2013300238Savos
2014300238Savos	return (val);
2015300238Savos}
2016300238Savos
2017109323Ssamint
2018109323Ssamwi_alloc(device_t dev, int rid)
2019109323Ssam{
2020109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
2021109323Ssam
2022109323Ssam	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
2023109323Ssam		sc->iobase_rid = rid;
2024296137Sjhibbits		sc->iobase = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT,
2025296137Sjhibbits		    &sc->iobase_rid, (1 << 6),
2026109323Ssam		    rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
2027178354Ssam		if (sc->iobase == NULL) {
2028109323Ssam			device_printf(dev, "No I/O space?!\n");
2029178354Ssam			return ENXIO;
203098440Simp		}
2031109323Ssam
2032109323Ssam		sc->wi_io_addr = rman_get_start(sc->iobase);
2033109323Ssam		sc->wi_btag = rman_get_bustag(sc->iobase);
2034109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->iobase);
2035109323Ssam	} else {
2036109323Ssam		sc->mem_rid = rid;
2037127135Snjl		sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
2038127135Snjl		    &sc->mem_rid, RF_ACTIVE);
2039178354Ssam		if (sc->mem == NULL) {
2040109323Ssam			device_printf(dev, "No Mem space on prism2.5?\n");
2041178354Ssam			return ENXIO;
204277217Sphk		}
2043109323Ssam
2044109323Ssam		sc->wi_btag = rman_get_bustag(sc->mem);
2045109323Ssam		sc->wi_bhandle = rman_get_bushandle(sc->mem);
204677217Sphk	}
204777217Sphk
2048109323Ssam	sc->irq_rid = 0;
2049127135Snjl	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
2050127135Snjl	    RF_ACTIVE |
2051109323Ssam	    ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
2052178354Ssam	if (sc->irq == NULL) {
2053109323Ssam		wi_free(dev);
2054109323Ssam		device_printf(dev, "No irq?!\n");
2055178354Ssam		return ENXIO;
205677217Sphk	}
2057109323Ssam
2058109323Ssam	sc->sc_dev = dev;
2059109323Ssam	sc->sc_unit = device_get_unit(dev);
2060178354Ssam	return 0;
206177217Sphk}
206293359Simp
2063109323Ssamvoid
2064109323Ssamwi_free(device_t dev)
2065109323Ssam{
2066109323Ssam	struct wi_softc	*sc = device_get_softc(dev);
2067109323Ssam
2068109323Ssam	if (sc->iobase != NULL) {
2069109323Ssam		bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
2070109323Ssam		sc->iobase = NULL;
2071109323Ssam	}
2072109323Ssam	if (sc->irq != NULL) {
2073109323Ssam		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
2074109323Ssam		sc->irq = NULL;
2075109323Ssam	}
2076109323Ssam	if (sc->mem != NULL) {
2077109323Ssam		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
2078109323Ssam		sc->mem = NULL;
2079109323Ssam	}
2080109323Ssam}
2081