Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 1 Mar 2006 15:43:42 -0800
From:      Luigi Rizzo <rizzo@icir.org>
To:        Max Laier <max@love2party.net>
Cc:        freebsd-net@freebsd.org, Anders Nordby <anders@freebsd.org>, damien@freebsd.org, flz@freebsd.org
Subject:   Re: iwi stability when doing large file transfers/backups
Message-ID:  <20060301154342.A53213@xorpc.icir.org>
In-Reply-To: <200603012150.44445.max@love2party.net>; from max@love2party.net on Wed, Mar 01, 2006 at 09:50:36PM %2B0100
References:  <20060301193342.GA11086@totem.fix.no> <20060301115655.A50285@xorpc.icir.org> <200603012150.44445.max@love2party.net>

next in thread | previous in thread | raw e-mail | index | archive | help

--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Wed, Mar 01, 2006 at 09:50:36PM +0100, Max Laier wrote:
> On Wednesday 01 March 2006 20:56, Luigi Rizzo wrote:
...
> > I am using RELENG_6 on my dell latitude x1 and
> > i have been getting ssh's "corrupted data" messages with 'iwi' since
> > i started using the native driver back in september.
> > After the update at the end of january the driver would consistently
> > crash my laptop right after loading the firmware (i suspect some kind
> > of race condition) so i had a hard time for a couple of weeks until
> > i replaced it with the code that Max Laier and Sam Leffler gave me
> > (using the 'firmware' driver from RELENG_7).
> > Since then the driver is solid as a rock, but the "corrupt data"
> > thing keeps happening, especially with bulk data transfers such as
> > scp, although occasionally i get them with http accesses too.
> 
> ahrg, still didn't get to putting out the RELENG_6 version of the driver.  
> Luigi, can you share your version.  Should be easy now since firmware has 

attached is a patch against RELENG_6 (despite the filename, it's the driver
Max and Sam sent me, i did not write anything).
It's missing the iwi_fw directory because i am not sure how the
license allows redistribution, maybe you or Sam know better ?
In any case it basically contains the 2.4 firmware split into
various directories, plus some trivial makefiles.

Also i think in the MFC of 'firmware' you forgot to patch
src/sys/modules/Makefile to attach the module to the build - see
the last chunk of the patch ?

	cheers
	luigi

--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="iwi.luigi.diff"

Index: dev/iwi/if_iwi.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/iwi/if_iwi.c,v
retrieving revision 1.8.2.6
diff -u -p -r1.8.2.6 if_iwi.c
--- dev/iwi/if_iwi.c	23 Feb 2006 02:06:46 -0000	1.8.2.6
+++ dev/iwi/if_iwi.c	1 Mar 2006 23:23:28 -0000
@@ -46,6 +46,14 @@ __FBSDID("$FreeBSD: src/sys/dev/iwi/if_i
 #include <sys/module.h>
 #include <sys/bus.h>
 #include <sys/endian.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/linker.h>
+#include <sys/firmware.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -75,6 +83,7 @@ __FBSDID("$FreeBSD: src/sys/dev/iwi/if_i
 #include <dev/iwi/if_iwireg.h>
 #include <dev/iwi/if_iwivar.h>
 
+#define IWI_DEBUG
 #ifdef IWI_DEBUG
 #define DPRINTF(x)	do { if (iwi_debug > 0) printf x; } while (0)
 #define DPRINTFN(n, x)	do { if (iwi_debug >= (n)) printf x; } while (0)
@@ -87,6 +96,13 @@ SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLA
 
 MODULE_DEPEND(iwi, pci,  1, 1, 1);
 MODULE_DEPEND(iwi, wlan, 1, 1, 1);
+MODULE_DEPEND(iwi, firmware, 1, 1, 1);
+
+enum {
+	IWI_LED_TX,
+	IWI_LED_RX,
+	IWI_LED_POLL,
+};
 
 struct iwi_ident {
 	uint16_t	vendor;
@@ -121,16 +137,17 @@ static void	iwi_node_free(struct ieee802
 static int	iwi_media_change(struct ifnet *);
 static void	iwi_media_status(struct ifnet *, struct ifmediareq *);
 static int	iwi_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void	iwi_wme_init(struct iwi_softc *);
+static void	iwi_wme_setparams(void *, int);
 static int	iwi_wme_update(struct ieee80211com *);
 static uint16_t	iwi_read_prom_word(struct iwi_softc *, uint8_t);
-static void	iwi_fix_channel(struct ieee80211com *, struct mbuf *);
 static void	iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
 		    struct iwi_frame *);
 static void	iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
 static void	iwi_rx_intr(struct iwi_softc *);
 static void	iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
 static void	iwi_intr(void *);
-static int	iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int);
+static int	iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t);
 static void	iwi_write_ibssnode(struct iwi_softc *, const struct iwi_node *);
 static int	iwi_tx_start(struct ifnet *, struct mbuf *,
 		    struct ieee80211_node *, int);
@@ -139,18 +156,28 @@ static void	iwi_watchdog(struct ifnet *)
 static int	iwi_ioctl(struct ifnet *, u_long, caddr_t);
 static void	iwi_stop_master(struct iwi_softc *);
 static int	iwi_reset(struct iwi_softc *);
-static int	iwi_load_ucode(struct iwi_softc *, void *, int);
-static int	iwi_load_firmware(struct iwi_softc *, void *, int);
-static int	iwi_cache_firmware(struct iwi_softc *, void *);
-static void	iwi_free_firmware(struct iwi_softc *);
+static int	iwi_load_ucode(struct iwi_softc *, struct firmware *);
+static int	iwi_load_firmware(struct iwi_softc *, struct firmware *);
 static int	iwi_config(struct iwi_softc *);
-static int	iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
-static int	iwi_scan(struct iwi_softc *);
+static int	iwi_get_firmware(struct iwi_softc *);
+static void	iwi_put_firmware(struct iwi_softc *);
+static void	iwi_scanabort(void *, int);
+static void	iwi_scandone(void *, int);
+static void	iwi_scanstart(void *, int);
+static void	iwi_scanchan(void *, int);
 static int	iwi_auth_and_assoc(struct iwi_softc *);
+static int	iwi_disassociate(struct iwi_softc *, int quiet);
+static void	iwi_down(void *, int);
 static void	iwi_init(void *);
+static void	iwi_init_locked(void *);
 static void	iwi_stop(void *);
-static int	iwi_sysctl_stats(SYSCTL_HANDLER_ARGS);
-static int	iwi_sysctl_radio(SYSCTL_HANDLER_ARGS);
+static void	iwi_restart(void *, int);
+static int	iwi_getrfkill(struct iwi_softc *);
+static void	iwi_radio_on(void *, int);
+static void	iwi_radio_off(void *, int);
+static void	iwi_sysctlattach(struct iwi_softc *);
+static void	iwi_led_event(struct iwi_softc *, int);
+static void	iwi_ledattach(struct iwi_softc *);
 
 static int iwi_probe(device_t);
 static int iwi_attach(device_t);
@@ -193,6 +220,20 @@ static const struct ieee80211_rateset iw
 static const struct ieee80211_rateset iwi_rateset_11g =
 	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
 
+static __inline uint8_t
+MEM_READ_1(struct iwi_softc *sc, uint32_t addr)
+{
+	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
+	return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA);
+}
+
+static __inline uint32_t
+MEM_READ_4(struct iwi_softc *sc, uint32_t addr)
+{
+	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
+	return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA);
+}
+
 static int
 iwi_probe(device_t dev)
 {
@@ -227,6 +268,20 @@ iwi_attach(device_t dev)
 
 	sc->sc_unr = new_unrhdr(0, IWI_MAX_IBSSNODE, &sc->sc_mtx);
 
+	sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT,
+		taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc);
+	kthread_create(taskqueue_thread_loop, &sc->sc_tq, &sc->sc_tqproc,
+		0, 0, "%s taskq", device_get_nameunit(dev));
+	TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc);
+	TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc);
+	TASK_INIT(&sc->sc_scanstarttask, 0, iwi_scanstart, sc);
+	TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc);
+	TASK_INIT(&sc->sc_scandonetask, 0, iwi_scandone, sc);
+	TASK_INIT(&sc->sc_scantask, 0, iwi_scanchan, sc);
+	TASK_INIT(&sc->sc_setwmetask, 0, iwi_wme_setparams, sc);
+	TASK_INIT(&sc->sc_downtask, 0, iwi_down, sc);
+	TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc);
+
 	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
 		device_printf(dev, "chip is in D%d power mode "
 		    "-- setting to D0\n", pci_get_powerstate(dev));
@@ -303,6 +358,8 @@ iwi_attach(device_t dev)
 		goto fail;
 	}
 
+	iwi_wme_init(sc);
+
 	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
 	if (ifp == NULL) {
 		device_printf(dev, "can not if_alloc()\n");
@@ -329,7 +386,7 @@ iwi_attach(device_t dev)
 	ic->ic_caps =
 	    IEEE80211_C_IBSS |		/* IBSS mode supported */
 	    IEEE80211_C_MONITOR |	/* monitor mode supported */
-	    IEEE80211_C_TXPMGT |	/* tx power management */
+	    IEEE80211_C_PMGT |		/* power save supported */
 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
 	    IEEE80211_C_WPA |		/* 802.11i */
 	    IEEE80211_C_WME;		/* 802.11e */
@@ -386,45 +443,19 @@ iwi_attach(device_t dev)
 	ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
 
 	bpfattach2(ifp, DLT_IEEE802_11_RADIO,
-	    sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+	    sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
+	    &sc->sc_drvbpf);
 
-	sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+	sc->sc_rxtap_len = sizeof sc->sc_rxtap;
 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
 	sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT);
 
-	sc->sc_txtap_len = sizeof sc->sc_txtapu;
+	sc->sc_txtap_len = sizeof sc->sc_txtap;
 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
 	sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT);
 
-	/*
-	 * Add a few sysctl knobs.
-	 */
-	sc->dwelltime = 100;
-	sc->bluetooth = 1;
-	sc->antenna = 0;
-
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
-	    CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I",
-	    "radio transmitter switch state (0=off, 1=on)");
-
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats",
-	    CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S",
-	    "statistics");
-
-	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
-	    CTLFLAG_RW, &sc->dwelltime, 0,
-	    "channel dwell time (ms) for AP/station scanning");
-
-	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "bluetooth",
-	    CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence");
-
-	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "antenna",
-	    CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)");
+	iwi_sysctlattach(sc);
+	iwi_ledattach(sc);
 
 	/*
 	 * Hook our interrupt after all initialization is complete.
@@ -453,8 +484,7 @@ iwi_detach(device_t dev)
 	struct ifnet *ifp = ic->ic_ifp;
 
 	iwi_stop(sc);
-
-	iwi_free_firmware(sc);
+	iwi_put_firmware(sc);
 
 	if (ifp != NULL) {
 		bpfdetach(ifp);
@@ -479,6 +509,8 @@ iwi_detach(device_t dev)
 	if (ifp != NULL)
 		if_free(ifp);
 
+	taskqueue_free(sc->sc_tq);
+
 	if (sc->sc_unr != NULL)
 		delete_unrhdr(sc->sc_unr);
 
@@ -789,6 +821,7 @@ iwi_shutdown(device_t dev)
 	struct iwi_softc *sc = device_get_softc(dev);
 
 	iwi_stop(sc);
+	iwi_put_firmware(sc);		/* ??? XXX */
 
 	return 0;
 }
@@ -866,7 +899,7 @@ iwi_media_change(struct ifnet *ifp)
 	}
 
 	if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
-		iwi_init(sc);
+		iwi_init_locked(sc);
 
 	IWI_UNLOCK(sc);
 
@@ -941,20 +974,25 @@ iwi_newstate(struct ieee80211com *ic, en
 {
 	struct ifnet *ifp = ic->ic_ifp;
 	struct iwi_softc *sc = ifp->if_softc;
-	enum ieee80211_state ostate;
-	uint32_t tmp;
 
-	ostate = ic->ic_state;
+	DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
+		ieee80211_state_name[ic->ic_state],
+		ieee80211_state_name[nstate], sc->flags));
 
 	switch (nstate) {
 	case IEEE80211_S_SCAN:
-		if (sc->flags & IWI_FLAG_SCANNING)
+		if (ic->ic_state == IEEE80211_S_RUN) {
+			/*
+			 * Beacon miss, send disassoc and wait for a reply
+			 * from the card; we'll start a scan then.
+			 */
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask);
 			break;
-
-		ieee80211_node_table_reset(&ic->ic_scan);
-		ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
-		sc->flags |= IWI_FLAG_SCANNING;
-		iwi_scan(sc);
+		}
+		if ((sc->flags & IWI_FLAG_SCANNING) == 0) {
+			sc->flags |= IWI_FLAG_SCANNING;
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_scanstarttask);
+		}
 		break;
 
 	case IEEE80211_S_AUTH:
@@ -963,13 +1001,9 @@ iwi_newstate(struct ieee80211com *ic, en
 
 	case IEEE80211_S_RUN:
 		if (ic->ic_opmode == IEEE80211_M_IBSS)
-			iwi_auth_and_assoc(sc);
+			ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
 		else if (ic->ic_opmode == IEEE80211_M_MONITOR)
-			iwi_set_chan(sc, ic->ic_ibss_chan);
-
-		/* assoc led on */
-		tmp = MEM_READ_4(sc, IWI_MEM_EVENT_CTL) & IWI_LED_MASK;
-		MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, tmp | IWI_LED_ASSOC);
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_scantask);
 
 		return sc->sc_newstate(ic, nstate,
 		    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
@@ -978,19 +1012,17 @@ iwi_newstate(struct ieee80211com *ic, en
 		break;
 
 	case IEEE80211_S_INIT:
-		sc->flags &= ~IWI_FLAG_SCANNING;
-
-		if (ostate != IEEE80211_S_RUN)
-			break;
-
-		/* assoc led off */
-		tmp = MEM_READ_4(sc, IWI_MEM_EVENT_CTL) & IWI_LED_MASK;
-		MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, tmp & ~IWI_LED_ASSOC);
+		/*
+		 * NB: don't try to do this if iwi_stop_master has
+		 *     shutdown the firmware and disabled interrupts.
+		 */
+		if (ic->ic_state == IEEE80211_S_RUN &&
+		    (sc->flags & IWI_FLAG_FW_INITED))
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask);
 		break;
 	}
 
 	ic->ic_state = nstate;
-
 	return 0;
 }
 
@@ -1012,54 +1044,94 @@ static const struct wmeParams iwi_wme_of
 	{ 0, 2, 3,  4,  94 },	/* WME_AC_VI */
 	{ 0, 2, 2,  3,  47 }	/* WME_AC_VO */
 };
-
-static int
-iwi_wme_update(struct ieee80211com *ic)
-{
 #define IWI_EXP2(v)	htole16((1 << (v)) - 1)
 #define IWI_USEC(v)	htole16(IEEE80211_TXOP_TO_US(v))
-	struct iwi_softc *sc = ic->ic_ifp->if_softc;
-	struct iwi_wme_params wme[3];
+
+static void
+iwi_wme_init(struct iwi_softc *sc)
+{
 	const struct wmeParams *wmep;
 	int ac;
 
-	/*
-	 * We shall not override firmware default WME values if WME is not
-	 * actually enabled.
-	 */
-	if (!(ic->ic_flags & IEEE80211_F_WME))
-		return 0;
-
+	memset(sc->wme, 0, sizeof sc->wme);
 	for (ac = 0; ac < WME_NUM_AC; ac++) {
-		/* set WME values for current operating mode */
-		wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
-		wme[0].aifsn[ac] = wmep->wmep_aifsn;
-		wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
-		wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
-		wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
-		wme[0].acm[ac]   = wmep->wmep_acm;
-
 		/* set WME values for CCK modulation */
 		wmep = &iwi_wme_cck_params[ac];
-		wme[1].aifsn[ac] = wmep->wmep_aifsn;
-		wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
-		wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
-		wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
-		wme[1].acm[ac]   = wmep->wmep_acm;
+		sc->wme[1].aifsn[ac] = wmep->wmep_aifsn;
+		sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
+		sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
+		sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
+		sc->wme[1].acm[ac]   = wmep->wmep_acm;
 
 		/* set WME values for OFDM modulation */
 		wmep = &iwi_wme_ofdm_params[ac];
-		wme[2].aifsn[ac] = wmep->wmep_aifsn;
-		wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
-		wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
-		wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
-		wme[2].acm[ac]   = wmep->wmep_acm;
+		sc->wme[2].aifsn[ac] = wmep->wmep_aifsn;
+		sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
+		sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
+		sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
+		sc->wme[2].acm[ac]   = wmep->wmep_acm;
+	}
+}
+
+static int
+iwi_wme_setparams_locked(struct iwi_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	const struct wmeParams *wmep;
+	int ac;
+
+	for (ac = 0; ac < WME_NUM_AC; ac++) {
+		/* set WME values for current operating mode */
+		wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
+		sc->wme[0].aifsn[ac] = wmep->wmep_aifsn;
+		sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
+		sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
+		sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
+		sc->wme[0].acm[ac]   = wmep->wmep_acm;
 	}
 
 	DPRINTF(("Setting WME parameters\n"));
-	return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, wme, sizeof wme, 1);
+	return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme);
+}
+
+static void
+iwi_wme_setparams(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+
+	IWI_LOCK(sc);
+	(void) iwi_wme_setparams_locked(sc);
+	IWI_UNLOCK(sc);
+}
 #undef IWI_USEC
 #undef IWI_EXP2
+
+static int
+iwi_wme_update(struct ieee80211com *ic)
+{
+	struct iwi_softc *sc = ic->ic_ifp->if_softc;
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_setwmetask);
+	return 0;
+}
+
+static int
+iwi_wme_setie(struct iwi_softc *sc)
+{
+	struct ieee80211_wme_info wme;
+
+	memset(&wme, 0, sizeof wme);
+	wme.wme_id = IEEE80211_ELEMID_VENDOR;
+	wme.wme_len = sizeof (struct ieee80211_wme_info) - 2;
+	wme.wme_oui[0] = 0x00;
+	wme.wme_oui[1] = 0x50;
+	wme.wme_oui[2] = 0xf2;
+	wme.wme_type = WME_OUI_TYPE;
+	wme.wme_subtype = WME_INFO_OUI_SUBTYPE;
+	wme.wme_version = WME_VERSION;
+	wme.wme_info = 0;
+
+	DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len));
+	return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme);
 }
 
 /*
@@ -1117,41 +1189,18 @@ iwi_read_prom_word(struct iwi_softc *sc,
 	return val;
 }
 
-/*
- * XXX: Hack to set the current channel to the value advertised in beacons or
- * probe responses. Only used during AP detection.
- */
 static void
-iwi_fix_channel(struct ieee80211com *ic, struct mbuf *m)
+iwi_setcurchan(struct iwi_softc *sc, int chan)
 {
-	struct ieee80211_frame *wh;
-	uint8_t subtype;
-	uint8_t *frm, *efrm;
-
-	wh = mtod(m, struct ieee80211_frame *);
-
-	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
-		return;
-
-	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
-	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
-	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-		return;
-
-	frm = (uint8_t *)(wh + 1);
-	efrm = mtod(m, uint8_t *) + m->m_len;
+	struct ieee80211com *ic = &sc->sc_ic;
 
-	frm += 12;	/* skip tstamp, bintval and capinfo fields */
-	while (frm < efrm) {
-		if (*frm == IEEE80211_ELEMID_DSPARMS)
-#if IEEE80211_CHAN_MAX < 255
-		if (frm[2] <= IEEE80211_CHAN_MAX)
-#endif
-			ic->ic_curchan = &ic->ic_channels[frm[2]];
+	ic->ic_curchan = &ic->ic_channels[chan];
+	sc->curchan = chan;
 
-		frm += frm[1] + 2;
-	}
+	sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
+		htole16(ic->ic_curchan->ic_freq);
+	sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags =
+		htole16(ic->ic_curchan->ic_flags);
 }
 
 static void
@@ -1161,16 +1210,18 @@ iwi_frame_intr(struct iwi_softc *sc, str
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = ic->ic_ifp;
 	struct mbuf *mnew, *m;
-	struct ieee80211_frame *wh;
 	struct ieee80211_node *ni;
-	int error;
+	int type, error;
 
-	DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u\n",
-	    le16toh(frame->len), frame->chan, frame->rssi_dbm));
+	DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n",
+	    le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm));
 
 	if (le16toh(frame->len) < sizeof (struct ieee80211_frame))
 		return;
 
+	if (frame->chan != sc->curchan)
+		iwi_setcurchan(sc, frame->chan);
+
 	/*
 	 * Try to allocate a new mbuf for this ring element and load it before
 	 * processing the current mbuf. If the ring element cannot be loaded,
@@ -1219,32 +1270,114 @@ iwi_frame_intr(struct iwi_softc *sc, str
 
 	m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
 
-	if (ic->ic_state == IEEE80211_S_SCAN)
-		iwi_fix_channel(ic, m);
-
 	if (sc->sc_drvbpf != NULL) {
 		struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
 
 		tap->wr_flags = 0;
-		tap->wr_rate = frame->rate;
-		tap->wr_chan_freq =
-		    htole16(ic->ic_channels[frame->chan].ic_freq);
-		tap->wr_chan_flags =
-		    htole16(ic->ic_channels[frame->chan].ic_flags);
+		tap->wr_rate = frame->rate;	/* XXX map to ieee rate */
 		tap->wr_antsignal = frame->signal;
 		tap->wr_antenna = frame->antenna;
 
 		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
 	}
 
-	wh = mtod(m, struct ieee80211_frame *);
-	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
+	ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
 
 	/* send the frame to the 802.11 layer */
-	ieee80211_input(ic, m, ni, frame->rssi_dbm, 0);
+	type = ieee80211_input(ic, m, ni, frame->rssi_dbm, 0);
 
 	/* node is no longer needed */
 	ieee80211_free_node(ni);
+
+	if (sc->sc_softled) {
+		/*
+		 * Blink for any data frame.  Otherwise do a
+		 * heartbeat-style blink when idle.  The latter
+		 * is mainly for station mode where we depend on
+		 * periodic beacon frames to trigger the poll event.
+		 */
+		if (type == IEEE80211_FC0_TYPE_DATA) {
+			sc->sc_rxrate = frame->rate;
+			iwi_led_event(sc, IWI_LED_RX);
+		} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
+			iwi_led_event(sc, IWI_LED_POLL);
+	}
+}
+
+/* unaligned little endian access */     
+#define LE_READ_2(p)					\
+	((u_int16_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)					\
+	((u_int32_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8) |		\
+	  (((const u_int8_t *)(p))[2] << 16) |		\
+	  (((const u_int8_t *)(p))[3] << 24)))
+
+#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
+	if ((_len) < (_minlen)) {					\
+		return;							\
+	}								\
+} while (0)
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+/*
+ * Check for an association response frame to see if QoS
+ * has been negotiated.  We parse just enough to figure
+ * out if we're supposed to use QoS.  The proper solution
+ * is to pass the frame up so ieee80211_input can do the
+ * work but that's made hard by how things currently are
+ * done in the driver.
+ */
+static void
+iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
+{
+#define	SUBTYPE(wh)	((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+	const uint8_t *frm, *efrm, *wme;
+	struct ieee80211_node *ni;
+
+	/* NB: +8 for capinfo, status, associd, and first ie */
+	if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) ||
+	    SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP)
+		return;
+	/*
+	 * asresp frame format
+	 *	[2] capability information
+	 *	[2] status
+	 *	[2] association ID
+	 *	[tlv] supported rates
+	 *	[tlv] extended supported rates
+	 *	[tlv] WME
+	 */
+	frm = (const uint8_t *)&wh[1];
+	efrm = ((const uint8_t *) wh) + len;
+	frm += 6;
+
+	wme = NULL;
+	while (frm < efrm) {
+		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+		switch (*frm) {
+		case IEEE80211_ELEMID_VENDOR:
+			if (iswmeoui(frm))
+				wme = frm;
+			break;
+		}
+		frm += frm[1] + 2;
+	}
+
+	ni = sc->sc_ic.ic_bss;
+	if (wme != NULL)
+		ni->ni_flags |= IEEE80211_NODE_QOS;
+	else
+		ni->ni_flags &= ~IEEE80211_NODE_QOS;
+#undef SUBTYPE
 }
 
 static void
@@ -1255,12 +1388,28 @@ iwi_notification_intr(struct iwi_softc *
 	struct iwi_notif_scan_complete *scan;
 	struct iwi_notif_authentication *auth;
 	struct iwi_notif_association *assoc;
+	struct iwi_notif_beacon_state *beacon;
 
 	switch (notif->type) {
 	case IWI_NOTIF_TYPE_SCAN_CHANNEL:
 		chan = (struct iwi_notif_scan_channel *)(notif + 1);
 
-		DPRINTFN(2, ("Scanning channel (%u)\n", chan->nchan));
+		DPRINTFN(3, ("Scan of channel %u complete (%u)\n",
+		    ic->ic_channels[chan->nchan].ic_freq, chan->nchan));
+
+		/*
+		 * Monitor mode works by doing a passive scan to set
+		 * the channel and enable rx.  Because we don't want
+		 * to abort a scan lest the firmware crash we scan
+		 * for a short period of time and automatically restart
+		 * the scan when notified the sweep has completed.
+		 * Note that it is unreliable to wait for the scan
+		 * complete notification; this seems to frequently be
+		 * lost so instead use the per-channel notification to
+		 * kick off a new scan.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_MONITOR)
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_scantask);
 		break;
 
 	case IWI_NOTIF_TYPE_SCAN_COMPLETE:
@@ -1269,26 +1418,33 @@ iwi_notification_intr(struct iwi_softc *
 		DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan,
 		    scan->status));
 
-		/* monitor mode uses scan to set the channel ... */
+		sc->sc_scan_timer = 0;
+
+		/* see above for monitor mode */
 		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
 			sc->flags &= ~IWI_FLAG_SCANNING;
-			ieee80211_end_scan(ic);
-		} else
-			iwi_set_chan(sc, ic->ic_ibss_chan);
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_scandonetask);
+		}
 		break;
 
 	case IWI_NOTIF_TYPE_AUTHENTICATION:
 		auth = (struct iwi_notif_authentication *)(notif + 1);
 
-		DPRINTFN(2, ("Authentication (%u)\n", auth->state));
-
 		switch (auth->state) {
-		case IWI_AUTHENTICATED:
+		case IWI_AUTH_SUCCESS:
+			DPRINTFN(2, ("Authentication succeeeded\n"));
 			ieee80211_node_authorize(ic->ic_bss);
 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
 			break;
 
-		case IWI_DEAUTHENTICATED:
+		case IWI_AUTH_FAIL:
+			DPRINTFN(2, ("Authentication failed\n"));
+			sc->flags &= ~IWI_FLAG_ASSOCIATED;
+			/* XXX */
+			break;
+
+		case IWI_AUTH_SENT_1:
+		case IWI_AUTH_RECV_2:
 			break;
 
 		default:
@@ -1300,20 +1456,24 @@ iwi_notification_intr(struct iwi_softc *
 	case IWI_NOTIF_TYPE_ASSOCIATION:
 		assoc = (struct iwi_notif_association *)(notif + 1);
 
-		DPRINTFN(2, ("Association (%u, %u)\n", assoc->state,
-		    assoc->status));
-
 		switch (assoc->state) {
-		case IWI_AUTHENTICATED:
+		case IWI_AUTH_SUCCESS:
 			/* re-association, do nothing */
 			break;
 
-		case IWI_ASSOCIATED:
+		case IWI_ASSOC_SUCCESS:
+			DPRINTFN(2, ("Association succeeded\n"));
+			sc->flags |= IWI_FLAG_ASSOCIATED;
+			iwi_checkforqos(sc,
+			    (const struct ieee80211_frame *)(assoc+1),
+			    le16toh(notif->len) - sizeof(*assoc));
 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
 			break;
 
-		case IWI_DEASSOCIATED:
-			ieee80211_begin_scan(ic, 1);
+		case IWI_ASSOC_FAIL:
+			DPRINTFN(2, ("Association failed\n"));
+			sc->flags &= ~IWI_FLAG_ASSOCIATED;
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
 			break;
 
 		default:
@@ -1322,8 +1482,49 @@ iwi_notification_intr(struct iwi_softc *
 		}
 		break;
 
-	default:
+	case IWI_NOTIF_TYPE_BEACON:
+		/* XXX check struct length */
+		beacon = (struct iwi_notif_beacon_state *)(notif + 1);
+
+		DPRINTFN(5, ("Beacon state (%u, %u)\n",
+		    beacon->state, le32toh(beacon->number)));
+
+		if (beacon->state == IWI_BEACON_MISS) {
+#if 0
+			if (sc->flags & IWI_FLAG_SCANNING) {
+				/* XXX terminate scan, linux driver
+				  says fw can get stuck */
+				/* XXX should be handled in iwi_newstate */
+				taskqueue_enqueue(sc->sc_tq,
+					&sc->sc_scanaborttask);
+			}
+#endif
+			/*
+			 * The firmware notifies us of every beacon miss
+			 * so we need to track the count against the
+			 * configured threshold before notifying the
+			 * 802.11 layer.
+			 * XXX try to roam, drop assoc only on much higher count
+			 */
+			if (le32toh(beacon->number) >= ic->ic_bmissthreshold) {
+				DPRINTF(("Beacon miss: %u >= %u\n",
+				    le32toh(beacon->number),
+				    ic->ic_bmissthreshold));
+				ieee80211_beacon_miss(ic);
+			}
+		}
+		break;
+
+	case IWI_NOTIF_TYPE_CALIBRATION:
+	case IWI_NOTIF_TYPE_NOISE:
+	case IWI_NOTIF_TYPE_LINK_QUALITY:
 		DPRINTFN(5, ("Notification (%u)\n", notif->type));
+		break;
+
+	default:
+		device_printf(sc->sc_dev,
+		    "unknown notification type %u flags 0x%x len %u\n",
+		    notif->type, notif->flags, le16toh(notif->len));
 	}
 }
 
@@ -1401,6 +1602,10 @@ iwi_tx_intr(struct iwi_softc *sc, struct
 
 	sc->sc_tx_timer = 0;
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+	if (sc->sc_softled)
+		iwi_led_event(sc, IWI_LED_TX);
+
 	iwi_start(ifp);
 }
 
@@ -1417,13 +1622,12 @@ iwi_intr(void *arg)
 		return;
 	}
 
-	/* disable interrupts */
-	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
+	/* acknowledge interrupts */
+	CSR_WRITE_4(sc, IWI_CSR_INTR, r);
 
-	if (r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)) {
-		device_printf(sc->sc_dev, "fatal error\n");
-		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
-		iwi_stop(sc);
+	if (r & IWI_INTR_FATAL_ERROR) {
+		device_printf(sc->sc_dev, "firmware error\n");
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask);
 	}
 
 	if (r & IWI_INTR_FW_INITED) {
@@ -1431,14 +1635,13 @@ iwi_intr(void *arg)
 			wakeup(sc);
 	}
 
-	if (r & IWI_INTR_RADIO_OFF) {
-		DPRINTF(("radio transmitter turned off\n"));
-		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
-		iwi_stop(sc);
-	}
+	if (r & IWI_INTR_RADIO_OFF)
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask);
 
-	if (r & IWI_INTR_CMD_DONE)
+	if (r & IWI_INTR_CMD_DONE) {
+		sc->flags &= ~IWI_FLAG_BUSY;
 		wakeup(sc);
+	}
 
 	if (r & IWI_INTR_TX1_DONE)
 		iwi_tx_intr(sc, &sc->txq[0]);
@@ -1455,20 +1658,26 @@ iwi_intr(void *arg)
 	if (r & IWI_INTR_RX_DONE)
 		iwi_rx_intr(sc);
 
-	/* acknowledge interrupts */
-	CSR_WRITE_4(sc, IWI_CSR_INTR, r);
-
-	/* re-enable interrupts */
-	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
+	if (r & IWI_INTR_PARITY_ERROR) {
+		/* XXX rate-limit */
+		device_printf(sc->sc_dev, "parity error\n");
+	}
 
 	IWI_UNLOCK(sc);
 }
 
 static int
-iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len, int async)
+iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len)
 {
 	struct iwi_cmd_desc *desc;
 
+	if (sc->flags & IWI_FLAG_BUSY) {
+		device_printf(sc->sc_dev, "%s: cmd 0x%x not sent, busy\n",
+			__func__, type);
+		return EAGAIN;
+	}
+	sc->flags |= IWI_FLAG_BUSY;
+
 	desc = &sc->cmdq.desc[sc->cmdq.cur];
 
 	desc->hdr.type = IWI_HDR_TYPE_COMMAND;
@@ -1486,7 +1695,7 @@ iwi_cmd(struct iwi_softc *sc, uint8_t ty
 	sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT;
 	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
 
-	return async ? 0 : msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz);
+	return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz);
 }
 
 static void
@@ -1510,7 +1719,7 @@ iwi_tx_start(struct ifnet *ifp, struct m
 	struct iwi_softc *sc = ifp->if_softc;
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct iwi_node *in = (struct iwi_node *)ni;
-	struct ieee80211_frame *wh;
+	const struct ieee80211_frame *wh;
 	struct ieee80211_key *k;
 	const struct chanAccParams *cap;
 	struct iwi_tx_ring *txq = &sc->txq[ac];
@@ -1518,16 +1727,24 @@ iwi_tx_start(struct ifnet *ifp, struct m
 	struct iwi_tx_desc *desc;
 	struct mbuf *mnew;
 	bus_dma_segment_t segs[IWI_MAX_NSEG];
-	int error, nsegs, hdrlen, i, noack = 0;
+	int error, nsegs, hdrlen, i;
+	int flags, xflags;
 
-	wh = mtod(m0, struct ieee80211_frame *);
+	wh = mtod(m0, const struct ieee80211_frame *);
+	/* NB: only data frames use this path */
+	hdrlen = ieee80211_hdrsize(wh);
+	flags = xflags = 0;
 
-	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
-		hdrlen = sizeof (struct ieee80211_qosframe);
+	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+		flags |= IWI_DATA_FLAG_NEED_ACK;
+	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+		flags |= IWI_DATA_FLAG_SHPREAMBLE;
+	if (IEEE80211_QOS_HAS_SEQ(wh)) {
+		xflags |= IWI_DATA_XFLAG_QOS;
 		cap = &ic->ic_wme.wme_chanParams;
-		noack = cap->cap_wmeParams[ac].wmep_noackPolicy;
-	} else
-		hdrlen = sizeof (struct ieee80211_frame);
+		if (!cap->cap_wmeParams[ac].wmep_noackPolicy)
+			flags &= ~IWI_DATA_FLAG_NEED_ACK;
+	}
 
 	/*
 	 * This is only used in IBSS mode where the firmware expect an index
@@ -1559,8 +1776,6 @@ iwi_tx_start(struct ifnet *ifp, struct m
 		struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
 
 		tap->wt_flags = 0;
-		tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
-		tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
 
 		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
 	}
@@ -1609,26 +1824,16 @@ iwi_tx_start(struct ifnet *ifp, struct m
 	    (ic->ic_opmode == IEEE80211_M_IBSS) ? in->in_station : 0;
 	desc->cmd = IWI_DATA_CMD_TX;
 	desc->len = htole16(m0->m_pkthdr.len);
-	desc->flags = 0;
-	desc->xflags = 0;
-
-	if (!noack && !IEEE80211_IS_MULTICAST(desc->wh.i_addr1))
-		desc->flags |= IWI_DATA_FLAG_NEED_ACK;
+	desc->flags = flags;
+	desc->xflags = xflags;
 
 #if 0
-	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
-		desc->wh.i_fc[1] |= IEEE80211_FC1_WEP;
-		desc->weptxkey = ic->ic_crypto.cs_def_txkey;
-	} else
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
+	else
 #endif
 		desc->flags |= IWI_DATA_FLAG_NO_WEP;
 
-	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
-		desc->flags |= IWI_DATA_FLAG_SHPREAMBLE;
-
-	if (desc->wh.i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
-		desc->xflags |= IWI_DATA_XFLAG_QOS;
-
 	desc->nseg = htole32(nsegs);
 	for (i = 0; i < nsegs; i++) {
 		desc->seg_addr[i] = htole32(segs[i].ds_addr);
@@ -1666,48 +1871,59 @@ iwi_start(struct ifnet *ifp)
 	}
 
 	for (;;) {
-		IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
-		if (m0 == NULL)
-			break;
-
-		if (m0->m_len < sizeof (struct ether_header) &&
-		    (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) {
-			ifp->if_oerrors++;
-			continue;
-		}
-		eh = mtod(m0, struct ether_header *);
-		ni = ieee80211_find_txnode(ic, eh->ether_dhost);
-		if (ni == NULL) {
-			m_freem(m0);
-			ifp->if_oerrors++;
-			continue;
-		}
+		IF_DEQUEUE(&ic->ic_mgtq, m0);
+		if (m0 == NULL) {
+			IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
+			if (m0 == NULL)
+				break;
+
+			if (m0->m_len < sizeof (struct ether_header) &&
+			    (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) {
+				ifp->if_oerrors++;
+				continue;
+			}
+			eh = mtod(m0, struct ether_header *);
+			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+			if (ni == NULL) {
+				m_freem(m0);
+				ifp->if_oerrors++;
+				continue;
+			}
 
-		/* classify mbuf so we can find which tx ring to use */
-		if (ieee80211_classify(ic, m0, ni) != 0) {
-			m_freem(m0);
-			ieee80211_free_node(ni);
-			ifp->if_oerrors++;
-			continue;
-		}
+			/* classify mbuf so we can find which tx ring to use */
+			if (ieee80211_classify(ic, m0, ni) != 0) {
+				m_freem(m0);
+				ieee80211_free_node(ni);
+				ifp->if_oerrors++;
+				continue;
+			}
 
-		/* no QoS encapsulation for EAPOL frames */
-		ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
-		    M_WME_GETAC(m0) : WME_AC_BE;
-
-		if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
-			/* there is no place left in this ring */
-			IFQ_DRV_PREPEND(&ifp->if_snd, m0);
-			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
-			break;
-		}
+			/* XXX does not belong here */
+			/* no QoS encapsulation for EAPOL frames */
+			ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
+			    M_WME_GETAC(m0) : WME_AC_BE;
+
+			if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
+				/* there is no place left in this ring */
+				IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+				ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+				break;
+			}
 
-		BPF_MTAP(ifp, m0);
+			BPF_MTAP(ifp, m0);
 
-		m0 = ieee80211_encap(ic, m0, ni);
-		if (m0 == NULL) {
+			m0 = ieee80211_encap(ic, m0, ni);
+			if (m0 == NULL) {
+				ieee80211_free_node(ni);
+				ifp->if_oerrors++;
+				continue;
+			}
+		} else {
+			ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
+			m0->m_pkthdr.rcvif = NULL;
+			/* XXX no way to send mgt frames (yet), discard */
+			m_freem(m0);
 			ieee80211_free_node(ni);
-			ifp->if_oerrors++;
 			continue;
 		}
 
@@ -1735,19 +1951,38 @@ iwi_watchdog(struct ifnet *ifp)
 
 	IWI_LOCK(sc);
 
-	ifp->if_timer = 0;
-
 	if (sc->sc_tx_timer > 0) {
 		if (--sc->sc_tx_timer == 0) {
 			if_printf(ifp, "device timeout\n");
 			ifp->if_oerrors++;
-			ifp->if_flags &= ~IFF_UP;
-			iwi_stop(sc);
-			IWI_UNLOCK(sc);
-			return;
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask);
+		}
+	}
+	if (sc->sc_rfkill_timer > 0) {
+		if (--sc->sc_rfkill_timer == 0) {
+			/*
+			 * Check for a change in rfkill state.  We get an
+			 * interrupt when a radio is disabled but not when
+			 * it is enabled so we must poll for the latter.
+			 */
+			if (!iwi_getrfkill(sc))
+				taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
+			else
+				sc->sc_rfkill_timer = 2;
+		}
+	}
+	if (sc->sc_scan_timer > 0) {
+		if (--sc->sc_scan_timer == 0) {
+			if (sc->flags & IWI_FLAG_SCANNING) {
+				if_printf(ifp, "scan stuck\n");
+				taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask);
+			}
 		}
-		ifp->if_timer = 1;
 	}
+	if (sc->sc_tx_timer || sc->sc_rfkill_timer || sc->sc_scan_timer)
+		ifp->if_timer = 1;
+	else
+		ifp->if_timer = 0;
 
 	ieee80211_watchdog(ic);
 
@@ -1759,7 +1994,6 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd,
 {
 	struct iwi_softc *sc = ifp->if_softc;
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifreq *ifr;
 	int error = 0;
 
 	IWI_LOCK(sc);
@@ -1768,32 +2002,22 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd,
 	case SIOCSIFFLAGS:
 		if (ifp->if_flags & IFF_UP) {
 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
-				iwi_init(sc);
+				iwi_init_locked(sc);
 		} else {
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 				iwi_stop(sc);
+			else {
+				/*
+				 * If device was stopped due to rfkill then
+				 * marked down we'll have the polling thread
+				 * running; stop it explicitly.
+				 */
+				sc->sc_rfkill_timer = 0;
+			}
+			iwi_put_firmware(sc);
 		}
 		break;
 
-	case SIOCSLOADFW:
-		/* only super-user can do that! */
-		if ((error = suser(curthread)) != 0)
-			break;
-
-		ifr = (struct ifreq *)data;
-		error = iwi_cache_firmware(sc, ifr->ifr_data);
-		break;
-
-	case SIOCSKILLFW:
-		/* only super-user can do that! */
-		if ((error = suser(curthread)) != 0)
-			break;
-
-		ifp->if_flags &= ~IFF_UP;
-		iwi_stop(sc);
-		iwi_free_firmware(sc);
-		break;
-
 	default:
 		error = ieee80211_ioctl(ic, cmd, data);
 	}
@@ -1802,7 +2026,7 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd,
 		if ((ifp->if_flags & IFF_UP) &&
 		    (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
 		    (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
-			iwi_init(sc);
+			iwi_init_locked(sc);
 		error = 0;
 	}
 
@@ -1876,45 +2100,176 @@ iwi_reset(struct iwi_softc *sc)
 	return 0;
 }
 
+/*
+ * Get the required firmware images if not already loaded.
+ * Note that we hold firmware images so long as the device
+ * is marked up in case we need to reload them on device init.
+ * This is necessary because we re-init the device sometimes
+ * from a context where we cannot read from the filesystem
+ * (e.g. from the taskqueue thread when rfkill is re-enabled).
+ *
+ * NB: the order of get'ing and put'ing images here is
+ * intentional to support handling firmware images bundled
+ * by operating mode and/or all together in one file with
+ * the boot firmware as "master".
+ */
 static int
-iwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
+iwi_get_firmware(struct iwi_softc *sc)
 {
-	uint32_t tmp;
-	uint16_t *w;
-	int ntries, i;
+	struct ieee80211com *ic = &sc->sc_ic;
 
-	CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
-	    IWI_RST_STOP_MASTER);
-	for (ntries = 0; ntries < 5; ntries++) {
-		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
-			break;
-		DELAY(10);
-	}
-	if (ntries == 5) {
-		device_printf(sc->sc_dev, "timeout waiting for master\n");
-		return EIO;
-	}
+	/* invalidate cached firmware on mode change */
+	if (sc->fw_mode != ic->ic_opmode)
+		iwi_put_firmware(sc);
 
-	MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
-	DELAY(5000);
+	if (sc->fw_boot == NULL)
+		sc->fw_boot = firmware_get("iwi_boot");
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_STA:
+		if (sc->fw_fw == NULL)
+			sc->fw_fw = firmware_get("iwi_bss");
+		if (sc->fw_uc == NULL)
+			sc->fw_uc = firmware_get("iwi_ucode_bss");
+		break;
 
-	tmp = CSR_READ_4(sc, IWI_CSR_RST);
-	tmp &= ~IWI_RST_PRINCETON_RESET;
-	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
+	case IEEE80211_M_IBSS:
+		if (sc->fw_fw == NULL)
+			sc->fw_fw = firmware_get("iwi_ibss");
+		if (sc->fw_uc == NULL)
+			sc->fw_uc = firmware_get("iwi_ucode_ibss");
+		break;
 
-	DELAY(5000);
-	MEM_WRITE_4(sc, 0x3000e0, 0);
-	DELAY(1000);
-	MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, 1);
-	DELAY(1000);
-	MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, 0);
-	DELAY(1000);
-	MEM_WRITE_1(sc, 0x200000, 0x00);
-	MEM_WRITE_1(sc, 0x200000, 0x40);
-	DELAY(1000);
+	case IEEE80211_M_MONITOR:
+		if (sc->fw_fw == NULL)
+			sc->fw_fw = firmware_get("iwi_monitor");
+		if (sc->fw_uc == NULL)
+			sc->fw_uc = firmware_get("iwi_ucode_monitor");
+		break;
+
+	default:
+		break;
+	}
+	if (sc->fw_boot == NULL) {
+		device_printf(sc->sc_dev, "could not load boot firmware\n");
+		return 0;
+	}
+	if (sc->fw_fw == NULL) {
+		device_printf(sc->sc_dev, "could not load firmware\n");
+		return 0;
+	}
+	if (sc->fw_uc == NULL) {
+		device_printf(sc->sc_dev, "could not load ucode\n");
+		return 0;
+	}
+	if (sc->fw_boot->version != 240) {
+		device_printf(sc->sc_dev,
+		    "firmware version for '%s' is %d, but 240 is required\n",
+		    sc->fw_boot->name, sc->fw_boot->version);
+		return 0;
+	}
+	if (sc->fw_fw->version != 240) {
+		device_printf(sc->sc_dev,
+		    "firmware version for '%s' is %d, but 240 is required\n",
+		    sc->fw_fw->name, sc->fw_boot->version);
+		return 0;
+	}
+	if (sc->fw_uc->version != 240) {
+		device_printf(sc->sc_dev,
+		    "firmware version for '%s' is %d, but 240 is required\n",
+		    sc->fw_uc->name, sc->fw_boot->version);
+		return 0;
+	}
+
+	sc->fw_mode = ic->ic_opmode;
+	return 1;
+}
+
+/*
+ * Release any cached firmware images.
+ */
+static void
+iwi_put_firmware(struct iwi_softc *sc)
+{
+	if (sc->fw_uc != NULL) {
+		firmware_put(sc->fw_uc, FIRMWARE_UNLOAD);
+		sc->fw_uc = NULL;
+	}
+	if (sc->fw_fw != NULL) {
+		firmware_put(sc->fw_fw, FIRMWARE_UNLOAD);
+		sc->fw_fw = NULL;
+	}
+	if (sc->fw_boot != NULL) {
+		firmware_put(sc->fw_boot, FIRMWARE_UNLOAD);
+		sc->fw_boot = NULL;
+	}
+}
+
+static int
+iwi_load_ucode(struct iwi_softc *sc, struct firmware *fp)
+{
+	const struct iwi_firmware_hdr *hdr;
+	uint32_t tmp;
+	const uint16_t *w;
+	const char *uc;
+	size_t size;
+	int i, ntries, error = 0;
+
+	if (fp->datasize < sizeof (struct iwi_firmware_hdr)) {
+		error = EINVAL;
+		goto fail;
+	}
+	hdr = (const struct iwi_firmware_hdr *)fp->data;
+	if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) ||
+	    (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) {
+		device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n",
+		    fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)),
+		    IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR,
+		    IWI_FW_REQ_MINOR);
+		error = EINVAL;
+		goto fail;
+	}
+	if (le32toh(hdr->mode) != IWI_FW_MODE_UCODE) {
+		device_printf(sc->sc_dev, "%s is no ucode image\n", fp->name);
+		error = EINVAL;
+		goto fail;
+	}
+
+	uc = ((const char *) fp->data) + sizeof (struct iwi_firmware_hdr);
+	size = fp->datasize - sizeof (struct iwi_firmware_hdr);
+
+	CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
+	    IWI_RST_STOP_MASTER);
+	for (ntries = 0; ntries < 5; ntries++) {
+		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
+			break;
+		DELAY(10);
+	}
+	if (ntries == 5) {
+		device_printf(sc->sc_dev, "timeout waiting for master\n");
+		error = EIO;
+		goto fail;
+	}
+
+	MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
+	DELAY(5000);
+
+	tmp = CSR_READ_4(sc, IWI_CSR_RST);
+	tmp &= ~IWI_RST_PRINCETON_RESET;
+	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
+
+	DELAY(5000);
+	MEM_WRITE_4(sc, 0x3000e0, 0);
+	DELAY(1000);
+	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1);
+	DELAY(1000);
+	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0);
+	DELAY(1000);
+	MEM_WRITE_1(sc, 0x200000, 0x00);
+	MEM_WRITE_1(sc, 0x200000, 0x40);
+	DELAY(1000);
 
 	/* write microcode into adapter memory */
-	for (w = uc; size > 0; w++, size -= 2)
+	for (w = (const uint16_t *)uc; size > 0; w++, size -= 2)
 		MEM_WRITE_2(sc, 0x200010, htole16(*w));
 
 	MEM_WRITE_1(sc, 0x200000, 0x00);
@@ -1929,7 +2284,8 @@ iwi_load_ucode(struct iwi_softc *sc, voi
 	if (ntries == 100) {
 		device_printf(sc->sc_dev,
 		    "timeout waiting for ucode to initialize\n");
-		return EIO;
+		error = EIO;
+		goto fail;
 	}
 
 	/* read the answer or the firmware will not initialize properly */
@@ -1938,51 +2294,46 @@ iwi_load_ucode(struct iwi_softc *sc, voi
 
 	MEM_WRITE_1(sc, 0x200000, 0x00);
 
-	return 0;
+fail:
+	return error;
 }
 
 /* macro to handle unaligned little endian data in firmware image */
 #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
 
 static int
-iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
+iwi_load_firmware(struct iwi_softc *sc, struct firmware *fp)
 {
-	bus_dma_tag_t dmat;
-	bus_dmamap_t map;
-	bus_addr_t physaddr;
-	void *virtaddr;
+	const struct iwi_firmware_hdr *hdr;
+	const char *fw;
 	u_char *p, *end;
 	uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
-	int ntries, error = 0;
-
-	/* allocate DMA memory for mapping firmware image */
-	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
-	    BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &dmat);
-	if (error != 0) {
-		device_printf(sc->sc_dev,
-		    "could not create firmware DMA tag\n");
-		goto fail1;
-	}
+	size_t size;
+	int ntries, error;
 
-	error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_NOWAIT, &map);
-	if (error != 0) {
-		device_printf(sc->sc_dev,
-		    "could not allocate firmware DMA memory\n");
-		goto fail2;
+	if (fp->datasize < sizeof (struct iwi_firmware_hdr)) {
+		error = EINVAL;
+		goto fail5;
+	}
+	hdr = (const struct iwi_firmware_hdr *)fp->data;
+	if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) ||
+	    (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) {
+		device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n",
+		    fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)),
+		    IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR,
+		    IWI_FW_REQ_MINOR);
+		error = EINVAL;
+		goto fail5;
 	}
 
-	error = bus_dmamap_load(dmat, map, virtaddr, size, iwi_dma_map_addr,
-	    &physaddr, 0);
-	if (error != 0) {
-		device_printf(sc->sc_dev, "could not load firmware DMA map\n");
-		goto fail3;
-	}
+	fw = ((const char *) fp->data) + sizeof (struct iwi_firmware_hdr);
+	size = fp->datasize - sizeof (struct iwi_firmware_hdr);
 
 	/* copy firmware image to DMA memory */
-	memcpy(virtaddr, fw, size);
+	memcpy(sc->fw_virtaddr, fw, size);
 
 	/* make sure the adapter will get up-to-date values */
-	bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE);
 
 	/* tell the adapter where the command blocks are stored */
 	MEM_WRITE_4(sc, 0x3000a0, 0x27000);
@@ -1992,8 +2343,8 @@ iwi_load_firmware(struct iwi_softc *sc, 
 	 * indirections. The adapter will read the firmware image through DMA
 	 * using information stored in command blocks.
 	 */
-	src = physaddr;
-	p = virtaddr;
+	src = sc->fw_physaddr;
+	p = sc->fw_virtaddr;
 	end = p + size;
 	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000);
 
@@ -2041,7 +2392,7 @@ iwi_load_firmware(struct iwi_softc *sc, 
 		device_printf(sc->sc_dev,
 		    "timeout processing command blocks\n");
 		error = EIO;
-		goto fail4;
+		goto fail5;
 	}
 
 	/* we're done with command blocks processing */
@@ -2060,94 +2411,26 @@ iwi_load_firmware(struct iwi_softc *sc, 
 	if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) {
 		device_printf(sc->sc_dev, "timeout waiting for firmware "
 		    "initialization to complete\n");
-		goto fail4;
 	}
 
-fail4:	bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE);
-	bus_dmamap_unload(dmat, map);
-fail3:	bus_dmamem_free(dmat, virtaddr, map);
-fail2:	bus_dma_tag_destroy(dmat);
-fail1:
+fail5:
 	return error;
 }
 
-/*
- * Store firmware into kernel memory so we can download it when we need to,
- * e.g when the adapter wakes up from suspend mode.
- */
 static int
-iwi_cache_firmware(struct iwi_softc *sc, void *data)
+iwi_setpowermode(struct iwi_softc *sc)
 {
-	struct iwi_firmware *kfw = &sc->fw;
-	struct iwi_firmware ufw;
-	int error;
-
-	iwi_free_firmware(sc);
-
-	IWI_UNLOCK(sc);
-
-	if ((error = copyin(data, &ufw, sizeof ufw)) != 0)
-		goto fail1;
-
-	kfw->boot_size  = ufw.boot_size;
-	kfw->ucode_size = ufw.ucode_size;
-	kfw->main_size  = ufw.main_size;
-
-	kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_NOWAIT);
-	if (kfw->boot == NULL) {
-		error = ENOMEM;
-		goto fail1;
-	}
-
-	kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_NOWAIT);
-	if (kfw->ucode == NULL) {
-		error = ENOMEM;
-		goto fail2;
-	}
-
-	kfw->main = malloc(kfw->main_size, M_DEVBUF, M_NOWAIT);
-	if (kfw->main == NULL) {
-		error = ENOMEM;
-		goto fail3;
-	}
-
-	if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0)
-		goto fail4;
-
-	if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0)
-		goto fail4;
-
-	if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0)
-		goto fail4;
-
-	DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n",
-	    kfw->boot_size, kfw->ucode_size, kfw->main_size));
-
-	IWI_LOCK(sc);
-
-	sc->flags |= IWI_FLAG_FW_CACHED;
-
-	return 0;
-
-fail4:	free(kfw->boot, M_DEVBUF);
-fail3:	free(kfw->ucode, M_DEVBUF);
-fail2:	free(kfw->main, M_DEVBUF);
-fail1:	IWI_LOCK(sc);
-
-	return error;
-}
-
-static void
-iwi_free_firmware(struct iwi_softc *sc)
-{
-	if (!(sc->flags & IWI_FLAG_FW_CACHED))
-		return;
+	struct ieee80211com *ic = &sc->sc_ic;
+	uint32_t data;
 
-	free(sc->fw.boot, M_DEVBUF);
-	free(sc->fw.ucode, M_DEVBUF);
-	free(sc->fw.main, M_DEVBUF);
+	if (ic->ic_flags & IEEE80211_F_PMGTON) {
+		/* XXX set more fine-grained operation */
+		data = htole32(IWI_POWER_MODE_MAX);
+	} else
+		data = htole32(IWI_POWER_MODE_CAM);
 
-	sc->flags &= ~IWI_FLAG_FW_CACHED;
+	DPRINTF(("Setting power mode to %u\n", le32toh(data)));
+	return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data);
 }
 
 static int
@@ -2166,7 +2449,7 @@ iwi_config(struct iwi_softc *sc)
 	IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
 	DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
 	error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
-	    IEEE80211_ADDR_LEN, 0);
+	    IEEE80211_ADDR_LEN);
 	if (error != 0)
 		return error;
 
@@ -2178,25 +2461,23 @@ iwi_config(struct iwi_softc *sc)
 	config.disable_unicast_decryption = 1;
 	config.disable_multicast_decryption = 1;
 	DPRINTF(("Configuring adapter\n"));
-	error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
 	if (error != 0)
 		return error;
 
-	data = htole32(IWI_POWER_MODE_CAM);
-	DPRINTF(("Setting power mode to %u\n", le32toh(data)));
-	error = iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data, 0);
+	error = iwi_setpowermode(sc);
 	if (error != 0)
 		return error;
 
 	data = htole32(ic->ic_rtsthreshold);
 	DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
-	error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
 	if (error != 0)
 		return error;
 
 	data = htole32(ic->ic_fragthreshold);
 	DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
-	error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
 	if (error != 0)
 		return error;
 
@@ -2208,15 +2489,13 @@ iwi_config(struct iwi_softc *sc)
 			power.chan[i].power = IWI_TXPOWER_MAX;
 		}
 		DPRINTF(("Setting .11b channels tx power\n"));
-		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
-		    0);
+		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power);
 		if (error != 0)
 			return error;
 
 		power.mode = IWI_MODE_11G;
 		DPRINTF(("Setting .11g channels tx power\n"));
-		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
-		    0);
+		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power);
 		if (error != 0)
 			return error;
 	}
@@ -2227,7 +2506,7 @@ iwi_config(struct iwi_softc *sc)
 	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates,
 	    rs.nrates);
 	DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates));
-	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
 	if (error != 0)
 		return error;
 
@@ -2237,7 +2516,7 @@ iwi_config(struct iwi_softc *sc)
 	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates,
 	    rs.nrates);
 	DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates));
-	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
 	if (error != 0)
 		return error;
 
@@ -2252,14 +2531,14 @@ iwi_config(struct iwi_softc *sc)
 		}
 #endif
 		error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid,
-		    ic->ic_des_esslen, 0);
+		    ic->ic_des_esslen);
 		if (error != 0)
 			return error;
 	}
 
 	data = htole32(arc4random());
 	DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
-	error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0);
+	error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data);
 	if (error != 0)
 		return error;
 
@@ -2274,75 +2553,200 @@ iwi_config(struct iwi_softc *sc)
 		DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx,
 		    wepkey.len));
 		error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
-		    sizeof wepkey, 0);
+		    sizeof wepkey);
 		if (error != 0)
 			return error;
 	}
 
 	/* enable adapter */
 	DPRINTF(("Enabling adapter\n"));
-	return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0);
+	return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0);
 }
 
-static int
-iwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan)
+static __inline void
+set_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct iwi_scan scan;
-
-	memset(&scan, 0, sizeof scan);
-	memset(scan.type, IWI_SCAN_TYPE_PASSIVE, sizeof scan.type);
-	scan.passive = htole16(2000);
-	scan.channels[0] = 1 |
-	    (IEEE80211_IS_CHAN_5GHZ(chan) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ);
-	scan.channels[1] = ieee80211_chan2ieee(ic, chan);
-
-	DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan)));
-	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
+	uint8_t *st = &scan->scan_type[ix / 2];
+	if (ix % 2)
+		*st = (*st & 0xf0) | ((scan_type & 0xf) << 0);
+	else
+		*st = (*st & 0x0f) | ((scan_type & 0xf) << 4);
 }
 
 static int
 iwi_scan(struct iwi_softc *sc)
 {
+#define	IEEE80211_MODE_5GHZ	(1<<IEEE80211_MODE_11A)
+#define	IEEE80211_MODE_2GHZ	((1<<IEEE80211_MODE_11B)|1<<IEEE80211_MODE_11G)
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct iwi_scan scan;
-	uint8_t *p;
-	int i, count;
+	const struct ieee80211_channel *c;
+	struct iwi_scan_ext scan;
+	int i, ix, start, scan_type;
 
 	memset(&scan, 0, sizeof scan);
 
-	if (ic->ic_des_esslen != 0) {
-		scan.bdirected = htole16(sc->dwelltime);
-		memset(scan.type, IWI_SCAN_TYPE_BDIRECTED, sizeof scan.type);
-	} else {
-		scan.broadcast = htole16(sc->dwelltime);
-		memset(scan.type, IWI_SCAN_TYPE_BROADCAST, sizeof scan.type);
-	}
-
-	p = scan.channels;
-	count = 0;
-	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
-		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) &&
-		    isset(ic->ic_chan_active, i)) {
-			*++p = i;
-			count++;
-		}
-	}
-	*(p - count) = IWI_CHAN_5GHZ | count;
-
-	p = (count > 0) ? p + 1 : scan.channels;
-	count = 0;
-	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
-		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) &&
-		    isset(ic->ic_chan_active, i)) {
-			*++p = i;
-			count++;
+	/* XXX different dwell times for different scan types */
+	scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(sc->dwelltime);
+	scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(sc->dwelltime);
+	scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(sc->dwelltime);
+
+	scan.full_scan_index = htole32(ic->ic_scan.nt_scangen);
+
+	scan_type = (ic->ic_des_esslen != 0) ? IWI_SCAN_TYPE_BDIRECTED :
+	    IWI_SCAN_TYPE_BROADCAST;
+
+	ix = 0;
+	if (ic->ic_modecaps & IEEE80211_MODE_5GHZ) {
+		start = ix;
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+			c = &ic->ic_channels[i];
+			if (!IEEE80211_IS_CHAN_5GHZ(c) ||
+			    !isset(ic->ic_chan_scan, i))
+				continue;
+			ix++;
+			scan.channels[ix] = i;
+			if (c->ic_flags & IEEE80211_CHAN_PASSIVE)
+				set_scan_type(&scan, ix, IWI_SCAN_TYPE_PASSIVE);
+			else
+				set_scan_type(&scan, ix, scan_type);
+		}
+		if (start != ix) {
+			scan.channels[start] = IWI_CHAN_5GHZ | (ix - start);
+			ix++;
+		}
+	}
+	if (ic->ic_modecaps & IEEE80211_MODE_2GHZ) {
+		start = ix;
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+			c = &ic->ic_channels[i];
+			if (!IEEE80211_IS_CHAN_2GHZ(c) ||
+			    !isset(ic->ic_chan_scan, i))
+				continue;
+			ix++;
+			scan.channels[ix] = i;
+			if (c->ic_flags & IEEE80211_CHAN_PASSIVE)
+				set_scan_type(&scan, ix, IWI_SCAN_TYPE_PASSIVE);
+			else
+				set_scan_type(&scan, ix, scan_type);
 		}
+		if (start != ix)
+			scan.channels[start] = IWI_CHAN_2GHZ | (ix - start);
 	}
-	*(p - count) = IWI_CHAN_2GHZ | count;
 
 	DPRINTF(("Start scanning\n"));
-	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
+	/*
+	 * With 100ms/channel dwell time and a max of ~20 channels
+	 * 5 seconds may be too tight; leave a bit more slack.
+	 */
+	sc->sc_scan_timer = 7;			/* seconds to complete */
+	sc->sc_ifp->if_timer = 1;
+	sc->flags |= IWI_FLAG_SCANNING;
+	return iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan);
+#undef IEEE80211_MODE_5GHZ
+#undef IEEE80211_MODE_2GHZ
+}
+
+static void
+iwi_scanabort(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+
+	IWI_LOCK(sc);
+	/* NB: make sure we're still scanning */
+	if (sc->flags & IWI_FLAG_SCANNING)
+		iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0);
+	IWI_UNLOCK(sc);
+}
+
+static void
+iwi_scanstart(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	IWI_LOCK(sc);
+	/*
+	 * Tell the card to kick off a scan.  We guard this
+	 * by checking IWI_FLAG_SCANNING as otherwise we'll
+	 * do this twice because ieee80211_begin_scan will
+	 * immediately call us back to scan the first channel
+	 * in the list.
+	 */
+	if (sc->flags & IWI_FLAG_SCANNING) {
+		ieee80211_begin_scan(ic, 1);
+		if (iwi_scan(sc) != 0) {
+			/* XXX should not happen */
+			sc->flags &= ~IWI_FLAG_SCANNING;
+			ieee80211_new_state(ic, IEEE80211_S_INIT, 0);
+		}
+	}
+	IWI_UNLOCK(sc);
+}
+
+static void
+iwi_scandone(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	IWI_LOCK(sc);
+	if (sc->flags & IWI_FLAG_ASSOCIATED)
+		iwi_disassociate(sc, 0);
+	ieee80211_end_scan(ic);
+	IWI_UNLOCK(sc);
+}
+
+/*
+ * Set the current channel by doing a passive scan.  Note this
+ * is explicitly for monitor mode operation; do not use it for
+ * anything else (sigh).
+ */
+static void
+iwi_scanchan(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+	struct ieee80211com *ic;
+	struct ieee80211_channel *chan;
+	struct iwi_scan_ext scan;
+
+	IWI_LOCK(sc);
+	ic = &sc->sc_ic;
+	KASSERT(ic->ic_opmode == IEEE80211_M_MONITOR,
+		("opmode %u", ic->ic_opmode));
+	chan = ic->ic_ibss_chan;
+
+	memset(&scan, 0, sizeof scan);
+	/*
+	 * Set the dwell time to a fairly small value.  The firmware
+	 * is prone to crash when aborting a scan so it's better to
+	 * let a scan complete before changing channels--such as when
+	 * channel hopping in monitor mode.
+	 */
+	scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(2000);
+	scan.full_scan_index = htole32(ic->ic_scan.nt_scangen);
+	if (IEEE80211_IS_CHAN_5GHZ(chan))
+		scan.channels[0] = 1 | IWI_CHAN_5GHZ;
+	else
+		scan.channels[0] = 1 | IWI_CHAN_2GHZ;
+	scan.channels[1] = ieee80211_chan2ieee(ic, chan);
+	set_scan_type(&scan, 1, IWI_SCAN_TYPE_PASSIVE);
+
+	DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan)));
+	sc->flags |= IWI_FLAG_SCANNING;
+	(void) iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan);
+	IWI_UNLOCK(sc);
+}
+
+static int
+iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm)
+{
+	struct iwi_sensitivity sens;
+
+	DPRINTF(("Setting sensitivity to %d\n", rssi_dbm));
+
+	memset(&sens, 0, sizeof sens);
+	sens.rssi = htole16(rssi_dbm);
+	return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens);
 }
 
 static int
@@ -2351,12 +2755,10 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_node *ni = ic->ic_bss;
-	struct ieee80211_wme_info wme;
 	struct iwi_configuration config;
-	struct iwi_associate assoc;
+	struct iwi_associate *assoc = &sc->assoc;
 	struct iwi_rateset rs;
 	uint16_t capinfo;
-	uint32_t data;
 	int error;
 
 	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
@@ -2370,8 +2772,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
 		config.disable_unicast_decryption = 1;
 		config.disable_multicast_decryption = 1;
 		DPRINTF(("Configuring adapter\n"));
-		error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config,
-		    1);
+		error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
 		if (error != 0)
 			return error;
 	}
@@ -2383,7 +2784,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
 		printf("\n");
 	}
 #endif
-	error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen, 1);
+	error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen);
 	if (error != 0)
 		return error;
 
@@ -2393,53 +2794,46 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
 	rs.type = IWI_RATESET_TYPE_NEGOTIATED;
 	rs.nrates = ni->ni_rates.rs_nrates;
 	memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates);
-	DPRINTF(("Setting negociated rates (%u)\n", rs.nrates));
-	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1);
+	DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates));
+	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
 	if (error != 0)
 		return error;
 
-	if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
-		wme.wme_id = IEEE80211_ELEMID_VENDOR;
-		wme.wme_len = sizeof (struct ieee80211_wme_info) - 2;
-		wme.wme_oui[0] = 0x00;
-		wme.wme_oui[1] = 0x50;
-		wme.wme_oui[2] = 0xf2;
-		wme.wme_type = WME_OUI_TYPE;
-		wme.wme_subtype = WME_INFO_OUI_SUBTYPE;
-		wme.wme_version = WME_VERSION;
-		wme.wme_info = 0;
+	memset(assoc, 0, sizeof *assoc);
 
-		DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len));
-		error = iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme, 1);
-		if (error != 0)
-			return error;
+	if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
+		/* NB: don't treat WME setup as failure */
+		if (iwi_wme_setparams_locked(sc) == 0 && iwi_wme_setie(sc) == 0)
+			assoc->policy |= htole16(IWI_POLICY_WME);
+		/* XXX complain on failure? */
 	}
 
 	if (ic->ic_opt_ie != NULL) {
 		DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
 		error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
-		    ic->ic_opt_ie_len, 1);
+		    ic->ic_opt_ie_len);
 		if (error != 0)
 			return error;
 	}
 
-	data = htole32(ni->ni_rssi);
-	DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi));
-	error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1);
+	error = iwi_set_sensitivity(sc, ni->ni_rssi);
 	if (error != 0)
 		return error;
 
-	memset(&assoc, 0, sizeof assoc);
-	assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
-	    IWI_MODE_11G;
-	assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+	if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+		assoc->mode = IWI_MODE_11A;
+	else if (IEEE80211_IS_CHAN_G(ni->ni_chan))
+		assoc->mode = IWI_MODE_11G;
+	else if (IEEE80211_IS_CHAN_B(ni->ni_chan))
+		assoc->mode = IWI_MODE_11B;
+	/* XXX else error */
+	assoc->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
 	if (ni->ni_authmode == IEEE80211_AUTH_SHARED)
-		assoc.auth = ic->ic_crypto.cs_def_txkey << 4 | IWI_AUTH_SHARED;
-	if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
-		assoc.policy |= htole16(IWI_POLICY_WME);
+		assoc->auth = ic->ic_crypto.cs_def_txkey << 4 | IWI_AUTH_SHARED;
 	if (ic->ic_flags & IEEE80211_F_WPA)
-		assoc.policy |= htole16(IWI_POLICY_WPA);
-	memcpy(assoc.tstamp, ni->ni_tstamp.data, 8);
+		assoc->policy |= htole16(IWI_POLICY_WPA);
+	assoc->type = IWI_HC_ASSOC;
+	memcpy(assoc->tstamp, ni->ni_tstamp.data, 8);
 
 	if (ic->ic_opmode == IEEE80211_M_IBSS)
 		capinfo = IEEE80211_CAPINFO_IBSS;
@@ -2450,41 +2844,72 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+	if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
 		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
-	assoc.capinfo = htole16(capinfo);
+	assoc->capinfo = htole16(capinfo);
 
-	assoc.lintval = htole16(ic->ic_lintval);
-	assoc.intval = htole16(ni->ni_intval);
-	IEEE80211_ADDR_COPY(assoc.bssid, ni->ni_bssid);
+	assoc->lintval = htole16(ic->ic_lintval);
+	assoc->intval = htole16(ni->ni_intval);
+	IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid);
 	if (ic->ic_opmode == IEEE80211_M_IBSS)
-		IEEE80211_ADDR_COPY(assoc.dst, ifp->if_broadcastaddr);
+		IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr);
+	else
+		IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid);
+
+	DPRINTF(("Trying to associate to %6D channel %u policy 0x%x auth %u "
+	    "capinfo 0x%x lintval %u bintval %u\n",
+	    assoc->bssid, ":", assoc->chan, le16toh(assoc->policy), assoc->auth,
+	    le16toh(assoc->capinfo), le16toh(assoc->lintval),
+	    le16toh(assoc->intval)));
+	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
+}
+
+static int
+iwi_disassociate(struct iwi_softc *sc, int quiet)
+{
+	struct iwi_associate *assoc = &sc->assoc;
+
+	if (quiet)
+		assoc->type = IWI_HC_DISASSOC_QUIET;
 	else
-		IEEE80211_ADDR_COPY(assoc.dst, ni->ni_bssid);
+		assoc->type = IWI_HC_DISASSOC;
 
-	DPRINTF(("Trying to associate to %6D channel %u auth %u\n",
-	    assoc.bssid, ":", assoc.chan, assoc.auth));
-	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &assoc, sizeof assoc, 1);
+	DPRINTF(("Trying to disassociate from %6D channel %u\n",
+	    assoc->bssid, ":", assoc->chan));
+	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
+}
+
+static void
+iwi_down(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+
+	IWI_LOCK(sc);
+	iwi_disassociate(sc, 0);
+	IWI_UNLOCK(sc);
 }
 
 static void
 iwi_init(void *priv)
 {
 	struct iwi_softc *sc = priv;
+
+	IWI_LOCK(sc);
+	iwi_init_locked(sc);
+	IWI_UNLOCK(sc);
+}
+
+static void
+iwi_init_locked(void *priv)
+{
+	struct iwi_softc *sc = priv;
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = ic->ic_ifp;
-	struct iwi_firmware *fw = &sc->fw;
 	struct iwi_rx_data *data;
-	int i;
+	int i, lock_depth = 0;
 
-	/* exit immediately if firmware has not been ioctl'd */
-	if (!(sc->flags & IWI_FLAG_FW_CACHED)) {
-		if (!(sc->flags & IWI_FLAG_FW_WARNED))
-			device_printf(sc->sc_dev, "Please load firmware\n");
-		sc->flags |= IWI_FLAG_FW_WARNED;
-		ifp->if_flags &= ~IFF_UP;
-		return;
-	}
+	if (sc->flags & IWI_FLAG_FW_LOADING)
+		return;		/* XXX: condvar? */
 
 	iwi_stop(sc);
 
@@ -2493,15 +2918,57 @@ iwi_init(void *priv)
 		goto fail;
 	}
 
-	if (iwi_load_firmware(sc, fw->boot, fw->boot_size) != 0) {
-		device_printf(sc->sc_dev, "could not load boot firmware\n");
+	sc->flags |= IWI_FLAG_FW_LOADING;
+
+	IWI_DROP(sc, lock_depth);
+	if (!iwi_get_firmware(sc)) {
+		IWI_PICKUP(sc, lock_depth);
 		goto fail;
 	}
 
-	if (iwi_load_ucode(sc, fw->ucode, fw->ucode_size) != 0) {
-		device_printf(sc->sc_dev, "could not load microcode\n");
+	/* allocate DMA memory for mapping firmware image */
+	if (sc->fw_boot->datasize > sc->fw_dma_size)
+		sc->fw_dma_size = sc->fw_boot->datasize;
+	if (sc->fw_fw->datasize > sc->fw_dma_size)
+		sc->fw_dma_size = sc->fw_fw->datasize;
+	if (sc->fw_uc->datasize > sc->fw_dma_size)
+		sc->fw_dma_size = sc->fw_uc->datasize;
+	sc->fw_dma_size -= sizeof (struct iwi_firmware_hdr);
+
+	if (bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
+	    BUS_SPACE_MAXADDR, NULL, NULL, sc->fw_dma_size, 1, sc->fw_dma_size,
+	    0, NULL, NULL, &sc->fw_dmat) != 0) {
+		device_printf(sc->sc_dev,
+		    "could not create firmware DMA tag\n");
+		IWI_PICKUP(sc, lock_depth);
 		goto fail;
 	}
+	if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0,
+	    &sc->fw_map) != 0) {
+		device_printf(sc->sc_dev,
+		    "could not allocate firmware DMA memory\n");
+		IWI_PICKUP(sc, lock_depth);
+		goto fail2;
+	}
+	if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr,
+	    sc->fw_dma_size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) {
+		device_printf(sc->sc_dev, "could not load firmware DMA map\n");
+		IWI_PICKUP(sc, lock_depth);
+		goto fail3;
+	}
+	IWI_PICKUP(sc, lock_depth);
+
+	if (iwi_load_firmware(sc, sc->fw_boot) != 0) {
+		device_printf(sc->sc_dev,
+			"could not load boot firmware %s\n", sc->fw_boot->name);
+		goto fail4;
+	}
+
+	if (iwi_load_ucode(sc, sc->fw_uc) != 0) {
+		device_printf(sc->sc_dev,
+			"could not load microcode %s\n", sc->fw_uc->name);
+		goto fail4;
+	}
 
 	iwi_stop_master(sc);
 
@@ -2532,13 +2999,18 @@ iwi_init(void *priv)
 
 	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1);
 
-	if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) {
-		device_printf(sc->sc_dev, "could not load main firmware\n");
-		goto fail;
+	if (iwi_load_firmware(sc, sc->fw_fw) != 0) {
+		device_printf(sc->sc_dev,
+			"could not load main firmware %s\n", sc->fw_fw->name);
+		goto fail4;
 	}
-
 	sc->flags |= IWI_FLAG_FW_INITED;
 
+	bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
+	bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
+	bus_dma_tag_destroy(sc->fw_dmat);
+
 	if (iwi_config(sc) != 0) {
 		device_printf(sc->sc_dev, "device configuration failed\n");
 		goto fail;
@@ -2553,10 +3025,17 @@ iwi_init(void *priv)
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
 
+	sc->flags &= ~IWI_FLAG_FW_LOADING;
 	return;
 
+fail4:	bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
+fail3:	bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
+fail2:	bus_dma_tag_destroy(sc->fw_dmat);
 fail:	ifp->if_flags &= ~IFF_UP;
+	sc->flags &= ~IWI_FLAG_FW_LOADING;
 	iwi_stop(sc);
+	iwi_put_firmware(sc);
 }
 
 static void
@@ -2566,7 +3045,10 @@ iwi_stop(void *priv)
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = ic->ic_ifp;
 
-	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+	if (sc->sc_softled) {
+		callout_stop(&sc->sc_ledtimer);
+		sc->sc_blinking = 0;
+	}
 
 	iwi_stop_master(sc);
 
@@ -2580,9 +3062,56 @@ iwi_stop(void *priv)
 	iwi_reset_tx_ring(sc, &sc->txq[3]);
 	iwi_reset_rx_ring(sc, &sc->rxq);
 
-	sc->sc_tx_timer = 0;
 	ifp->if_timer = 0;
 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+	sc->sc_tx_timer = 0;
+	sc->sc_rfkill_timer = 0;
+	sc->sc_scan_timer = 0;
+	sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_SCANNING | IWI_FLAG_ASSOCIATED);
+
+	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+}
+
+static void
+iwi_restart(void *arg, int npending)
+{
+	struct iwi_softc *sc = arg;
+
+	IWI_LOCK(sc);
+	iwi_stop(sc);
+	iwi_init_locked(sc);
+	IWI_UNLOCK(sc);
+}
+
+/*
+ * Return whether or not the radio is enabled in hardware
+ * (i.e. the rfkill switch is "off").
+ */
+static int
+iwi_getrfkill(struct iwi_softc *sc)
+{
+	return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0;
+}
+
+static void
+iwi_radio_on(void *arg, int pending)
+{
+	struct iwi_softc *sc = arg;
+
+	device_printf(sc->sc_dev, "radio turned on\n");
+	iwi_init(sc);
+}
+
+static void
+iwi_radio_off(void *arg, int pending)
+{
+	struct iwi_softc *sc = arg;
+
+	device_printf(sc->sc_dev, "radio turned off\n");
+	iwi_stop(sc);
+	sc->sc_rfkill_timer = 2;
+	sc->sc_ifp->if_timer = 1;
 }
 
 static int
@@ -2606,9 +3135,234 @@ static int
 iwi_sysctl_radio(SYSCTL_HANDLER_ARGS)
 {
 	struct iwi_softc *sc = arg1;
-	int val;
-
-	val = (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) ? 1 : 0;
+	int val = !iwi_getrfkill(sc);
 
 	return SYSCTL_OUT(req, &val, sizeof val);
 }
+
+/*
+ * Add sysctl knobs.
+ */
+static void
+iwi_sysctlattach(struct iwi_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio",
+	    CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I",
+	    "radio transmitter switch state (0=off, 1=on)");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats",
+	    CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S",
+	    "statistics");
+
+	sc->dwelltime = 100;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "dwell",
+	    CTLFLAG_RW, &sc->dwelltime, 0,
+	    "channel dwell time (ms) for AP/station scanning");
+
+	sc->bluetooth = 0;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth",
+	    CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence");
+
+	sc->antenna = 0;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna",
+	    CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)");
+}
+
+/*
+ * LED support.
+ *
+ * Different cards have different capabilities.  Some have three
+ * led's while others have only one.  The linux ipw driver defines
+ * led's for link state (associated or not), band (11a, 11g, 11b),
+ * and for link activity.  We use one led and vary the blink rate
+ * according to the tx/rx traffic a la the ath driver.
+ */
+
+static uint32_t
+iwi_toggle_event(uint32_t r)
+{
+	r &= ~IWI_RST_STANDBY;
+	if (r & IWI_RST_GATE_ODMA)
+		r &= ~IWI_RST_GATE_ODMA;
+	if (r & IWI_RST_GATE_IDMA)
+		r &= ~IWI_RST_GATE_IDMA;
+	if (r & IWI_RST_GATE_ADMA)
+		r &= ~IWI_RST_GATE_ADMA;
+	return r;
+}
+
+static uint32_t
+iwi_read_event(struct iwi_softc *sc)
+{
+	return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT);
+}
+
+static void
+iwi_write_event(struct iwi_softc *sc, uint32_t v)
+{
+	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v);
+}
+
+static void
+iwi_led_done(void *arg)
+{
+	struct iwi_softc *sc = arg;
+
+	sc->sc_blinking = 0;
+}
+
+/*
+ * Turn the activity LED off: flip the pin and then set a timer so no
+ * update will happen for the specified duration.
+ */
+static void
+iwi_led_off(void *arg)
+{
+	struct iwi_softc *sc = arg;
+	uint32_t v;
+
+	v = iwi_read_event(sc);
+	v &= ~sc->sc_ledpin;
+	iwi_write_event(sc, iwi_toggle_event(v));
+	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc);
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+iwi_led_blink(struct iwi_softc *sc, int on, int off)
+{
+	uint32_t v;
+
+	v = iwi_read_event(sc);
+	v |= sc->sc_ledpin;
+	iwi_write_event(sc, iwi_toggle_event(v));
+	sc->sc_blinking = 1;
+	sc->sc_ledoff = off;
+	callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc);
+}
+
+static void
+iwi_led_event(struct iwi_softc *sc, int event)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	/* NB: on/off times from the Atheros NDIS driver, w/ permission */
+	static const struct {
+		u_int		rate;		/* tx/rx iwi rate */
+		u_int16_t	timeOn;		/* LED on time (ms) */
+		u_int16_t	timeOff;	/* LED off time (ms) */
+	} blinkrates[] = {
+		{ IWI_RATE_OFDM54, 40,  10 },
+		{ IWI_RATE_OFDM48, 44,  11 },
+		{ IWI_RATE_OFDM36, 50,  13 },
+		{ IWI_RATE_OFDM24, 57,  14 },
+		{ IWI_RATE_OFDM18, 67,  16 },
+		{ IWI_RATE_OFDM12, 80,  20 },
+		{ IWI_RATE_DS11,  100,  25 },
+		{ IWI_RATE_OFDM9, 133,  34 },
+		{ IWI_RATE_OFDM6, 160,  40 },
+		{ IWI_RATE_DS5,   200,  50 },
+		{            6,   240,  58 },	/* XXX 3Mb/s if it existed */
+		{ IWI_RATE_DS2,   267,  66 },
+		{ IWI_RATE_DS1,   400, 100 },
+		{            0,   500, 130 },	/* unknown rate/polling */
+	};
+	uint32_t txrate;
+	int j = 0;			/* XXX silence compiler */
+
+	sc->sc_ledevent = ticks;	/* time of last event */
+	if (sc->sc_blinking)		/* don't interrupt active blink */
+		return;
+	switch (event) {
+	case IWI_LED_POLL:
+		j = N(blinkrates)-1;
+		break;
+	case IWI_LED_TX:
+		/* read current transmission rate from adapter */
+		txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE);
+		if (blinkrates[sc->sc_txrix].rate != txrate) {
+			for (j = 0; j < N(blinkrates)-1; j++)
+				if (blinkrates[j].rate == txrate)
+					break;
+			sc->sc_txrix = j;
+		} else
+			j = sc->sc_txrix;
+		break;
+	case IWI_LED_RX:
+		if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) {
+			for (j = 0; j < N(blinkrates)-1; j++)
+				if (blinkrates[j].rate == sc->sc_rxrate)
+					break;
+			sc->sc_rxrix = j;
+		} else
+			j = sc->sc_rxrix;
+		break;
+	}
+	/* XXX beware of overflow */
+	iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000,
+		(blinkrates[j].timeOff * hz) / 1000);
+#undef N
+}
+
+static int
+iwi_sysctl_softled(SYSCTL_HANDLER_ARGS)
+{
+	struct iwi_softc *sc = arg1;
+	int softled = sc->sc_softled;
+	int error;
+
+	error = sysctl_handle_int(oidp, &softled, 0, req);
+	if (error || !req->newptr)
+		return error;
+	softled = (softled != 0);
+	if (softled != sc->sc_softled) {
+		if (softled) {
+			uint32_t v = iwi_read_event(sc);
+			v &= ~sc->sc_ledpin;
+			iwi_write_event(sc, iwi_toggle_event(v));
+		}
+		sc->sc_softled = softled;
+	}
+	return 0;
+}
+
+static void
+iwi_ledattach(struct iwi_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+	sc->sc_blinking = 0;
+	sc->sc_ledstate = 1;
+	sc->sc_ledidle = (2700*hz)/1000;	/* 2.7sec */
+	callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0);
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		iwi_sysctl_softled, "I", "enable/disable software LED support");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
+		"pin setting to turn activity LED on");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
+		"idle time for inactivity LED (ticks)");
+	/* XXX for debugging */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"nictype", CTLFLAG_RD, &sc->sc_nictype, 0,
+		"NIC type from EEPROM");
+
+	sc->sc_ledpin = IWI_RST_LED_ACTIVITY;
+	sc->sc_softled = 1;
+
+	sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff;
+	if (sc->sc_nictype == 1) {
+		/*
+		 * NB: led's are reversed.
+		 */
+		sc->sc_ledpin = IWI_RST_LED_ASSOCIATED;
+	}
+}
Index: dev/iwi/if_iwireg.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/iwi/if_iwireg.h,v
retrieving revision 1.2.2.2
diff -u -p -r1.2.2.2 if_iwireg.h
--- dev/iwi/if_iwireg.h	29 Jan 2006 13:54:19 -0000	1.2.2.2
+++ dev/iwi/if_iwireg.h	19 Feb 2006 20:47:20 -0000
@@ -94,9 +94,16 @@
 
 /* flags for IWI_CSR_RST */
 #define IWI_RST_PRINCETON_RESET	0x00000001
+#define	IWI_RST_STANDBY		0x00000004
+#define	IWI_RST_LED_ACTIVITY	0x00000010	/* tx/rx traffic led */
+#define	IWI_RST_LED_ASSOCIATED	0x00000020	/* station associated led */
+#define	IWI_RST_LED_OFDM	0x00000040	/* ofdm/cck led */
 #define IWI_RST_SOFT_RESET	0x00000080
 #define IWI_RST_MASTER_DISABLED	0x00000100
 #define IWI_RST_STOP_MASTER	0x00000200
+#define IWI_RST_GATE_ODMA	0x02000000
+#define IWI_RST_GATE_IDMA	0x04000000
+#define IWI_RST_GATE_ADMA	0x20000000
 
 /* flags for IWI_CSR_CTL */
 #define IWI_CTL_CLOCK_READY	0x00000001
@@ -127,6 +134,22 @@
 #define IWI_RATE_OFDM48	1
 #define IWI_RATE_OFDM54	3
 
+/* firmware binary image header, fields in little endian */
+struct iwi_firmware_hdr {
+	uint32_t	version;
+	uint32_t	mode;
+} __packed;
+#define	IWI_FW_REQ_MAJOR	2
+#define	IWI_FW_REQ_MINOR	4
+#define	IWI_FW_GET_MAJOR(ver)	((ver) & 0xff)
+#define	IWI_FW_GET_MINOR(ver)	(((ver) & 0xff00) >> 8)
+
+#define	IWI_FW_MODE_UCODE	0
+#define	IWI_FW_MODE_BOOT	0
+#define	IWI_FW_MODE_BSS		0
+#define	IWI_FW_MODE_IBSS	1
+#define	IWI_FW_MODE_MONITOR	2
+
 struct iwi_hdr {
 	uint8_t	type;
 #define IWI_HDR_TYPE_DATA	0
@@ -144,11 +167,16 @@ struct iwi_hdr {
 struct iwi_notif {
 	uint32_t	reserved[2];
 	uint8_t		type;
+#define IWI_NOTIF_TYPE_SUCCESS		0
+#define IWI_NOTIF_TYPE_UNSPECIFIED	1	/* unspecified failure */
 #define IWI_NOTIF_TYPE_ASSOCIATION	10
 #define IWI_NOTIF_TYPE_AUTHENTICATION	11
 #define IWI_NOTIF_TYPE_SCAN_CHANNEL	12
 #define IWI_NOTIF_TYPE_SCAN_COMPLETE	13
-#define IWI_NOTIF_TYPE_BEACON		17
+#define IWI_NOTIF_TYPE_FRAG_LENGTH	14
+#define IWI_NOTIF_TYPE_LINK_QUALITY	15	/* "link deterioration" */
+#define IWI_NOTIF_TYPE_BEACON		17	/* beacon state, e.g. miss */
+#define	IWI_NOTIF_TYPE_TGI_TX_KEY	18	/* WPA transmit key */
 #define IWI_NOTIF_TYPE_CALIBRATION	20
 #define IWI_NOTIF_TYPE_NOISE		25
 
@@ -159,20 +187,18 @@ struct iwi_notif {
 /* structure for notification IWI_NOTIF_TYPE_AUTHENTICATION */
 struct iwi_notif_authentication {
 	uint8_t	state;
-#define IWI_DEAUTHENTICATED	0
-#define IWI_AUTHENTICATED	9
+#define IWI_AUTH_FAIL		0
+#define	IWI_AUTH_SENT_1		1		/* tx first frame */
+#define	IWI_AUTH_RECV_2		2		/* rx second frame */
+#define IWI_AUTH_SUCCESS	9
 } __packed;
 
 /* structure for notification IWI_NOTIF_TYPE_ASSOCIATION */
 struct iwi_notif_association {
 	uint8_t			state;
-#define IWI_DEASSOCIATED	0
-#define IWI_ASSOCIATED		12
-
-	struct ieee80211_frame	frame;
-	uint16_t		capinfo;
-	uint16_t		status;
-	uint16_t		associd;
+#define IWI_ASSOC_FAIL		0
+#define IWI_ASSOC_SUCCESS	12
+	uint8_t			pad[11];
 } __packed;
 
 /* structure for notification IWI_NOTIF_TYPE_SCAN_CHANNEL */
@@ -189,6 +215,13 @@ struct iwi_notif_scan_complete {
 	uint8_t	reserved;
 } __packed;
 
+/* structure for notification IWI_NOTIF_TYPE_BEACON */
+struct iwi_notif_beacon_state {
+	uint32_t state;
+#define IWI_BEACON_MISS		1
+	uint32_t number;
+} __packed;
+
 /* received frame header */
 struct iwi_frame {
 	uint32_t	reserved1[2];
@@ -226,7 +259,7 @@ struct iwi_tx_desc {
 	uint8_t		xflags;
 #define IWI_DATA_XFLAG_QOS	0x10
 
-	uint8_t		weptxkey;
+	uint8_t		wep_txkey;
 	uint8_t		wepkey[IEEE80211_KEYBUF_SIZE];
 	uint8_t		rate;
 	uint8_t		antenna;
@@ -253,11 +286,12 @@ struct iwi_cmd_desc {
 #define IWI_CMD_SET_FRAG_THRESHOLD		16
 #define IWI_CMD_SET_POWER_MODE			17
 #define IWI_CMD_SET_WEP_KEY			18
+#define IWI_CMD_SCAN				20
 #define IWI_CMD_ASSOCIATE			21
 #define IWI_CMD_SET_RATES			22
 #define IWI_CMD_ABORT_SCAN			23
 #define IWI_CMD_SET_WME_PARAMS			25
-#define IWI_CMD_SCAN				26
+#define IWI_CMD_SCAN_EXT			26
 #define IWI_CMD_SET_OPTIE			31
 #define IWI_CMD_DISABLE				33
 #define IWI_CMD_SET_IV				34
@@ -282,7 +316,9 @@ struct iwi_ibssnode {
 #define IWI_MODE_11G	2
 
 /* possible values for command IWI_CMD_SET_POWER_MODE */
-#define IWI_POWER_MODE_CAM	0
+#define IWI_POWER_MODE_CAM	0	/* no power save */
+#define IWI_POWER_MODE_PSP	3
+#define IWI_POWER_MODE_MAX	5	/* max power save operation */
 
 /* structure for command IWI_CMD_SET_RATES */
 struct iwi_rateset {
@@ -310,72 +346,89 @@ struct iwi_txpower {
 
 /* structure for command IWI_CMD_ASSOCIATE */
 struct iwi_associate {
-	uint8_t		chan;
-	uint8_t		auth;
+	uint8_t		chan;		/* channel # */
+	uint8_t		auth;		/* type and key */
 #define IWI_AUTH_OPEN	0
 #define IWI_AUTH_SHARED	1
 #define IWI_AUTH_NONE	3
 
-	uint8_t		type;
-	uint8_t		reserved1;
+	uint8_t		type;		/* request */
+#define	IWI_HC_ASSOC		0
+#define	IWI_HC_REASSOC		1
+#define	IWI_HC_DISASSOC		2
+#define	IWI_HC_IBSS_START	3
+#define	IWI_HC_IBSS_RECONF	4
+#define	IWI_HC_DISASSOC_QUIET	5
+	uint8_t		reserved;
 	uint16_t	policy;
 #define IWI_POLICY_WME	1
 #define IWI_POLICY_WPA	2
 
-	uint8_t		plen;
-	uint8_t		mode;
+	uint8_t		plen;		/* preamble length */
+	uint8_t		mode;		/* 11a, 11b, or 11g */
 	uint8_t		bssid[IEEE80211_ADDR_LEN];
-	uint8_t		tstamp[8];
+	uint8_t		tstamp[8];	/* tsf for beacon sync */
 	uint16_t	capinfo;
-	uint16_t	lintval;
-	uint16_t	intval;
+	uint16_t	lintval;	/* listen interval */
+	uint16_t	intval;		/* beacon interval */
 	uint8_t		dst[IEEE80211_ADDR_LEN];
-	uint32_t	reserved3;
-	uint16_t	reserved4;
+	uint16_t	atim_window;
+	uint8_t		smr;
+	uint8_t		reserved1;
+	uint16_t	reserved2;
 } __packed;
 
+#define	IWI_SCAN_CHANNELS	54
+
 /* structure for command IWI_CMD_SCAN */
 struct iwi_scan {
-	uint32_t	index;
-	uint8_t		channels[54];
+	uint8_t		type;
+	uint16_t	dwelltime;	/* channel dwell time (ms) */
+	uint8_t		channels[IWI_SCAN_CHANNELS];
 #define IWI_CHAN_5GHZ	(0 << 6)
 #define IWI_CHAN_2GHZ	(1 << 6)
 
-	uint8_t		type[27];
-#define IWI_SCAN_TYPE_PASSIVE	0x11
-#define IWI_SCAN_TYPE_DIRECTED	0x22
-#define IWI_SCAN_TYPE_BROADCAST	0x33
-#define IWI_SCAN_TYPE_BDIRECTED	0x44
+	uint8_t		reserved[3];
+} __packed;
 
-	uint8_t		reserved1;
-	uint16_t	reserved2;
-	uint16_t	passive;	/* dwell time */
-	uint16_t	directed;	/* dwell time */
-	uint16_t	broadcast;	/* dwell time */
-	uint16_t	bdirected;	/* dwell time */
+/* scan type codes */
+#define IWI_SCAN_TYPE_PASSIVE_STOP	0 /* passive, stop on first beacon */
+#define IWI_SCAN_TYPE_PASSIVE		1 /* passive, full dwell on channel */
+#define IWI_SCAN_TYPE_DIRECTED		2 /* active, directed probe req */
+#define IWI_SCAN_TYPE_BROADCAST		3 /* active, bcast probe req */
+#define IWI_SCAN_TYPE_BDIRECTED		4 /* active, directed+bcast probe */
+#define IWI_SCAN_TYPES			5
+
+/* structure for command IWI_CMD_SCAN_EXT */
+struct iwi_scan_ext {
+	uint32_t	full_scan_index;
+	uint8_t		channels[IWI_SCAN_CHANNELS];
+	uint8_t		scan_type[IWI_SCAN_CHANNELS / 2];
+	uint8_t		reserved;
+	uint16_t	dwell_time[IWI_SCAN_TYPES];
 } __packed;
 
 /* structure for command IWI_CMD_SET_CONFIG */
 struct iwi_configuration {
 	uint8_t	bluetooth_coexistence;
 	uint8_t	reserved1;
-	uint8_t	answer_pbreq;
-	uint8_t	allow_invalid_frames;
-	uint8_t	multicast_enabled;
+	uint8_t	answer_pbreq;		/* answer bcast ssid probe req frames */
+	uint8_t	allow_invalid_frames;	/* accept data frames w/ errors */
+	uint8_t	multicast_enabled;	/* accept frames w/ any bssid */
 	uint8_t	drop_unicast_unencrypted;
 	uint8_t	disable_unicast_decryption;
 	uint8_t	drop_multicast_unencrypted;
 	uint8_t	disable_multicast_decryption;
-	uint8_t	antenna;
-	uint8_t	reserved2;
-	uint8_t	use_protection;
-	uint8_t	protection_ctsonly;
+	uint8_t	antenna;		/* antenna diversity */
+	uint8_t	include_crc;		/* include crc in rx'd frames */
+	uint8_t	use_protection;		/* auto-detect 11g operation */
+	uint8_t	protection_ctsonly;	/* use CTS-to-self protection */
 	uint8_t	enable_multicast_filtering;
-	uint8_t	bluetooth_threshold;
+	uint8_t	bluetooth_threshold;	/* collision threshold */
 	uint8_t	reserved4;
-	uint8_t	allow_beacon_and_probe_resp;
-	uint8_t	allow_mgt;
-	uint8_t	noise_reported;
+	uint8_t	allow_beacon_and_probe_resp;/* accept frames w/ any bssid */
+	uint8_t	allow_mgt;		/* accept frames w/ any bssid */
+	uint8_t	noise_reported;		/* report noise stats to host */
 	uint8_t	reserved5;
 } __packed;
 
@@ -399,14 +452,19 @@ struct iwi_wme_params {
 	uint16_t	burst[WME_NUM_AC];
 } __packed;
 
-#define IWI_MEM_EVENT_CTL	0x00300004
-#define IWI_MEM_EEPROM_CTL	0x00300040
+/* structure for command IWI_CMD_SET_SENSITIVTY */
+struct iwi_sensitivity {
+	uint16_t rssi;			/* beacon rssi in dBm */
+#define	IWI_RSSI_TO_DBM		112
+	uint16_t reserved;
+} __packed;
 
-/* possible flags for register IWI_MEM_EVENT */
-#define IWI_LED_ASSOC	(1 << 5)
-#define IWI_LED_MASK	0xd9fffffb
+#define IWI_MEM_EEPROM_EVENT	0x00300004
+#define IWI_MEM_EEPROM_CTL	0x00300040
 
 #define IWI_EEPROM_MAC	0x21
+#define IWI_EEPROM_NIC	0x25		/* nic type (lsb) */
+#define IWI_EEPROM_SKU	0x25		/* nic type (msb) */
 
 #define IWI_EEPROM_DELAY	1	/* minimum hold time (microsecond) */
 
@@ -450,14 +508,6 @@ struct iwi_wme_params {
 /*
  * indirect memory space access macros
  */
-#define MEM_READ_1(sc, addr)						\
-	(CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)),		\
-	 CSR_READ_1((sc), IWI_CSR_INDIRECT_DATA))
-
-#define MEM_READ_4(sc, addr)						\
-	(CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)),		\
-	 CSR_READ_4((sc), IWI_CSR_INDIRECT_DATA))
-
 #define MEM_WRITE_1(sc, addr, val) do {					\
 	CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr));		\
 	CSR_WRITE_1((sc), IWI_CSR_INDIRECT_DATA, (val));		\
Index: dev/iwi/if_iwivar.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/iwi/if_iwivar.h,v
retrieving revision 1.4.2.1
diff -u -p -r1.4.2.1 if_iwivar.h
--- dev/iwi/if_iwivar.h	26 Sep 2005 17:31:36 -0000	1.4.2.1
+++ dev/iwi/if_iwivar.h	19 Feb 2006 20:47:20 -0000
@@ -27,15 +27,6 @@
  * SUCH DAMAGE.
  */
 
-struct iwi_firmware {
-	void	*boot;
-	int	boot_size;
-	void	*ucode;
-	int	ucode_size;
-	void	*main;
-	int	main_size;
-};
-
 struct iwi_rx_radiotap_header {
 	struct ieee80211_radiotap_header wr_ihdr;
 	uint8_t		wr_flags;
@@ -126,13 +117,15 @@ struct iwi_softc {
 
 	struct mtx		sc_mtx;
 	struct unrhdr		*sc_unr;
+	struct taskqueue	*sc_tq;		/* private task queue */
+	struct proc		*sc_tqproc;
 
-	struct iwi_firmware	fw;
 	uint32_t		flags;
-#define IWI_FLAG_FW_CACHED	(1 << 0)
-#define IWI_FLAG_FW_INITED	(1 << 1)
-#define IWI_FLAG_FW_WARNED	(1 << 2)
-#define IWI_FLAG_SCANNING	(1 << 3)
+#define IWI_FLAG_FW_INITED	(1 << 0)
+#define IWI_FLAG_SCANNING	(1 << 1)
+#define	IWI_FLAG_FW_LOADING	(1 << 2)
+#define	IWI_FLAG_BUSY		(1 << 3)	/* busy sending a command */
+#define	IWI_FLAG_ASSOCIATED	(1 << 4)	/* current associated  */
 
 	struct iwi_cmd_ring	cmdq;
 	struct iwi_tx_ring	txq[WME_NUM_AC];
@@ -146,11 +139,50 @@ struct iwi_softc {
 	int			mem_rid;
 	int			irq_rid;
 
+	int			fw_dma_size;
+	bus_dma_tag_t		fw_dmat;
+	bus_dmamap_t		fw_map;
+	bus_addr_t		fw_physaddr;
+	void			*fw_virtaddr;
+	enum ieee80211_opmode	fw_mode;	/* mode of current firmware */
+	struct firmware		*fw_fw;		/* operating mode image */
+	struct firmware		*fw_uc;		/* microcode image */
+	struct firmware		*fw_boot;	/* boot firmware image */
+
+	int			curchan;	/* current h/w channel # */
 	int			antenna;
 	int			dwelltime;
 	int			bluetooth;
+	struct iwi_associate	assoc;
+	struct iwi_wme_params	wme[3];
+
+	struct task		sc_radiontask;	/* radio on processing */
+	struct task		sc_radiofftask;	/* radio off processing */
+	struct task		sc_scanstarttask;/* scan start processing */
+	struct task		sc_scanaborttask;/* scan abort processing */
+	struct task		sc_scandonetask;/* scan completed processing */
+	struct task		sc_scantask;	/* scan channel processing */
+	struct task		sc_setwmetask;	/* set wme params processing */
+	struct task		sc_downtask;	/* disassociate processing */
+	struct task		sc_restarttask;	/* restart adapter processing */
+
+	unsigned int		sc_softled : 1,	/* enable LED gpio status */
+				sc_ledstate: 1,	/* LED on/off state */
+				sc_blinking: 1;	/* LED blink operation active */
+	u_int			sc_nictype;	/* NIC type from EEPROM */
+	u_int			sc_ledpin;	/* mask for activity LED */
+	u_int			sc_ledidle;	/* idle polling interval */
+	int			sc_ledevent;	/* time of last LED event */
+	u_int8_t		sc_rxrate;	/* current rx rate for LED */
+	u_int8_t		sc_rxrix;
+	u_int8_t		sc_txrate;	/* current tx rate for LED */
+	u_int8_t		sc_txrix;
+	u_int16_t		sc_ledoff;	/* off time for current blink */
+	struct callout		sc_ledtimer;	/* led off timer */
 
 	int			sc_tx_timer;
+	int			sc_rfkill_timer;/* poll for rfkill change */
+	int			sc_scan_timer;	/* scan request timeout */
 
 	struct bpf_if		*sc_drvbpf;
 
@@ -169,8 +201,15 @@ struct iwi_softc {
 	int			sc_txtap_len;
 };
 
-#define SIOCSLOADFW	 _IOW('i', 137, struct ifreq)
-#define SIOCSKILLFW	 _IOW('i', 138, struct ifreq)
-
 #define IWI_LOCK(sc)	mtx_lock(&(sc)->sc_mtx)
 #define IWI_UNLOCK(sc)	mtx_unlock(&(sc)->sc_mtx)
+#define	IWI_DROP(sc, cnt)	do {		\
+	while (mtx_owned(&(sc)->sc_mtx)) {	\
+		(cnt)++;			\
+		mtx_unlock(&(sc)->sc_mtx);	\
+	}					\
+} while (0)
+#define	IWI_PICKUP(sc, cnt)	do {		\
+	while ((cnt)--)				\
+		mtx_lock(&(sc)->sc_mtx);	\
+} while (0)
Index: modules/Makefile
===================================================================
RCS file: /home/ncvs/src/sys/modules/Makefile,v
retrieving revision 1.450.2.9
diff -u -p -r1.450.2.9 Makefile
--- modules/Makefile	13 Feb 2006 11:39:01 -0000	1.450.2.9
+++ modules/Makefile	20 Feb 2006 08:58:54 -0000
@@ -82,6 +82,7 @@ SUBDIR=	${_3dfx} \
 	fdescfs \
 	${_fe} \
 	firewire \
+	firmware \
 	fxp \
 	${_gem} \
 	geom \
@@ -123,6 +124,7 @@ SUBDIR=	${_3dfx} \
 	isp \
 	ispfw \
 	iwi \
+	iwi_fw \
 	joy \
 	kbdmux \
 	kue \

--5vNYLRcllDrimb99--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060301154342.A53213>