Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

iwi stability when doing large file transfers/backups

0 views
Skip to first unread message

Anders Nordby

unread,
Mar 1, 2006, 2:38:14 PM3/1/06
to
Hi,

I'm having great difficulties taking backup of my laptop computer. It's
a Fujitsu Siemens Lifebook E8020 system with a Intel PRO/Wireless
2915ABG NIC. I'm connected to a FreeBSD hostap, using WPA2 (CCMP/AES).

Updating to 6-STABLE (6.1-PRERELEASE) seems to have improved the
stability of the iwi driver quite a bit, but when doing large file
transfers (in this case rsyncing all the data from the laptop through
SSH), I always and oftenly get:

Disconnecting: Corrupted MAC on input.

The messages comes from SSH, so it seems to be getting corrupted data.
Is anyone looking into that? Is there any fixes or settings that should
be tried? How are people's experience with iwi? Am I the only one to
have problems with it?

Cheers,

--
Anders.
_______________________________________________
freeb...@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net...@freebsd.org"

Luigi Rizzo

unread,
Mar 1, 2006, 3:02:18 PM3/1/06
to
On Wed, Mar 01, 2006 at 08:33:42PM +0100, Anders Nordby wrote:
> Hi,
>
> I'm having great difficulties taking backup of my laptop computer. It's
> a Fujitsu Siemens Lifebook E8020 system with a Intel PRO/Wireless
> 2915ABG NIC. I'm connected to a FreeBSD hostap, using WPA2 (CCMP/AES).
>
> Updating to 6-STABLE (6.1-PRERELEASE) seems to have improved the
> stability of the iwi driver quite a bit, but when doing large file
> transfers (in this case rsyncing all the data from the laptop through
> SSH), I always and oftenly get:
>
> Disconnecting: Corrupted MAC on input.
>
> The messages comes from SSH, so it seems to be getting corrupted data.
> Is anyone looking into that? Is there any fixes or settings that should
> be tried? How are people's experience with iwi? Am I the only one to
> have problems with it?

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.

curiously interactive ssh is always ok, so i suspect that packet size
has an influence on the problem.
I had no time to instrument the relevant part of the ssl library
to try and figure out if there is a pattern in the error (e.g.
zeroed bytes, or a constant packet size, etc.)

but yes, overall the native iwi driver does have issues and i
am unsure on what is the source (i had firmware 2.3 before feb,
and 2.4 now).

cheers
luigi

Max Laier

unread,
Mar 1, 2006, 3:55:24 PM3/1/06
to
--nextPart2009766.qCQR9IS4dk
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

ahrg, still didn't get to putting out the RELENG_6 version of the driver. =
=20
Luigi, can you share your version. Should be easy now since firmware has=20
been MFCed. I'd really like to see people testing this version and let us=
=20
know what works and what not. I hope to find some time to do something abo=
ut=20
that then.

> curiously interactive ssh is always ok, so i suspect that packet size
> has an influence on the problem.
> I had no time to instrument the relevant part of the ssl library
> to try and figure out if there is a pattern in the error (e.g.
> zeroed bytes, or a constant packet size, etc.)
>
> but yes, overall the native iwi driver does have issues and i
> am unsure on what is the source (i had firmware 2.3 before feb,
> and 2.4 now).

=2D-=20
/"\ Best regards, | mla...@freebsd.org
\ / Max Laier | ICQ #67774661
X http://pf4freebsd.love2party.net/ | mlaier@EFnet
/ \ ASCII Ribbon Campaign | Against HTML Mail and News

--nextPart2009766.qCQR9IS4dk
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (FreeBSD)

iD8DBQBEBgkkXyyEoT62BG0RAnx2AJ9EUpjQpP7BWR4fk/XRDaIZ2WRZ1QCfYz5N
iWhkkgwdWz4OIKnAusk/5jY=
=8i64
-----END PGP SIGNATURE-----

--nextPart2009766.qCQR9IS4dk--

Luigi Rizzo

unread,
Mar 1, 2006, 6:47:03 PM3/1/06
to

--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
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
freeb...@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net...@freebsd.org"

--5vNYLRcllDrimb99--

damien.b...@free.fr

unread,
Mar 2, 2006, 9:06:18 AM3/2/06
to
The one who wrote that diff, whoever he is, obviously don't
understand C code.

Otherwise, why would he write such nonsense:

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;
}

when you could just write:

r &= ~(IWI_RST_STANDBY | IWI_RST_GATE_ODMA |
IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA);

but he sure knows how to steel code from others and
violate the GPL by the way:

>From Linux ipw2200-1.1.0 (GPL):

static u32 ipw_register_toggle(u32 reg)
{
reg &= ~IPW_START_STANDBY;
if (reg & IPW_GATE_ODMA)
reg &= ~IPW_GATE_ODMA;
if (reg & IPW_GATE_IDMA)
reg &= ~IPW_GATE_IDMA;
if (reg & IPW_GATE_ADMA)
reg &= ~IPW_GATE_ADMA;
return reg;
}

...mmm very similar right?

Damien

Sam Leffler

unread,
Mar 2, 2006, 11:58:34 AM3/2/06
to

I'm glad you see ways to improve the code. As to concerns about GPL
contamination you are way off base. Oh and of course I wrote this
code--as you knew all along.

Sam

0 new messages