From owner-freebsd-net@FreeBSD.ORG Wed Mar 1 23:43:53 2006 Return-Path: X-Original-To: freebsd-net@freebsd.org Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 17AF916A420; Wed, 1 Mar 2006 23:43:53 +0000 (GMT) (envelope-from rizzo@icir.org) Received: from xorpc.icir.org (xorpc.icir.org [192.150.187.68]) by mx1.FreeBSD.org (Postfix) with ESMTP id 0E22A43D68; Wed, 1 Mar 2006 23:43:44 +0000 (GMT) (envelope-from rizzo@icir.org) Received: from xorpc.icir.org (localhost [127.0.0.1]) by xorpc.icir.org (8.12.11/8.12.11) with ESMTP id k21NhgSa053319; Wed, 1 Mar 2006 15:43:42 -0800 (PST) (envelope-from rizzo@xorpc.icir.org) Received: (from rizzo@localhost) by xorpc.icir.org (8.12.11/8.12.3/Submit) id k21Nhg5m053318; Wed, 1 Mar 2006 15:43:42 -0800 (PST) (envelope-from rizzo) Date: Wed, 1 Mar 2006 15:43:42 -0800 From: Luigi Rizzo To: Max Laier Message-ID: <20060301154342.A53213@xorpc.icir.org> References: <20060301193342.GA11086@totem.fix.no> <20060301115655.A50285@xorpc.icir.org> <200603012150.44445.max@love2party.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="5vNYLRcllDrimb99" Content-Disposition: inline User-Agent: Mutt/1.2.5.1i In-Reply-To: <200603012150.44445.max@love2party.net>; from max@love2party.net on Wed, Mar 01, 2006 at 09:50:36PM +0100 Cc: freebsd-net@freebsd.org, Anders Nordby , damien@freebsd.org, flz@freebsd.org Subject: Re: iwi stability when doing large file transfers/backups X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 01 Mar 2006 23:43:53 -0000 --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 #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -75,6 +83,7 @@ __FBSDID("$FreeBSD: src/sys/dev/iwi/if_i #include #include +#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<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--