diff options
Diffstat (limited to 'sys')
69 files changed, 13028 insertions, 4853 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 454543b51053..08ea41d6300d 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -786,6 +786,8 @@ device wlan_tkip #802.11 TKIP support device wlan_xauth #802.11 external authenticator support device wlan_acl #802.11 MAC ACL support device wlan_amrr #AMRR transmit rate control algorithm +device wlan_scan_ap #802.11 AP mode scanning +device wlan_scan_sta #802.11 STA mode scanning device token #Generic TokenRing device fddi #Generic FDDI device arcnet #Generic Arcnet diff --git a/sys/conf/files b/sys/conf/files index fde91d22a1b6..c08878a11d97 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1613,11 +1613,17 @@ net80211/ieee80211_crypto_none.c optional wlan net80211/ieee80211_crypto_tkip.c optional wlan_tkip net80211/ieee80211_crypto_wep.c optional wlan_wep net80211/ieee80211_freebsd.c optional wlan +net80211/ieee80211_ht.c optional wlan net80211/ieee80211_input.c optional wlan net80211/ieee80211_ioctl.c optional wlan net80211/ieee80211_node.c optional wlan net80211/ieee80211_output.c optional wlan +net80211/ieee80211_power.c optional wlan net80211/ieee80211_proto.c optional wlan +net80211/ieee80211_regdomain.c optional wlan +net80211/ieee80211_scan.c optional wlan +net80211/ieee80211_scan_ap.c optional wlan_scan_ap +net80211/ieee80211_scan_sta.c optional wlan_scan_sta net80211/ieee80211_xauth.c optional wlan_xauth netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk diff --git a/sys/dev/ath/ath_rate/amrr/amrr.c b/sys/dev/ath/ath_rate/amrr/amrr.c index fc72abfb088d..da564a89d741 100644 --- a/sys/dev/ath/ath_rate/amrr/amrr.c +++ b/sys/dev/ath/ath_rate/amrr/amrr.c @@ -297,27 +297,27 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) /* NB: the rate set is assumed sorted */ for (; srate >= 0 && RATE(srate) > 72; srate--) ; - KASSERT(srate >= 0, ("bogus rate set")); } } else { /* - * A fixed rate is to be used; ic_fixed_rate is an - * index into the supported rate set. Convert this + * A fixed rate is to be used; ic_fixed_rate is the + * IEEE code for this rate (sans basic bit). Convert this * to the index into the negotiated rate set for * the node. We know the rate is there because the * rate set is checked when the station associates. */ - const struct ieee80211_rateset *rs = - &ic->ic_sup_rates[ic->ic_curmode]; - int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; /* NB: the rate set is assumed sorted */ srate = ni->ni_rates.rs_nrates - 1; - for (; srate >= 0 && RATE(srate) != r; srate--) + for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--) ; - KASSERT(srate >= 0, - ("fixed rate %d not in rate set", ic->ic_fixed_rate)); } - ath_rate_update(sc, ni, srate); + /* + * The selected rate may not be available due to races + * and mode settings. Also orphaned nodes created in + * adhoc mode may not have any rate set so this lookup + * can fail. This is not fatal. + */ + ath_rate_update(sc, ni, srate < 0 ? 0 : srate); #undef RATE } diff --git a/sys/dev/ath/ath_rate/onoe/onoe.c b/sys/dev/ath/ath_rate/onoe/onoe.c index b682a471f4dd..281e4f1de31b 100644 --- a/sys/dev/ath/ath_rate/onoe/onoe.c +++ b/sys/dev/ath/ath_rate/onoe/onoe.c @@ -274,27 +274,27 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) /* NB: the rate set is assumed sorted */ for (; srate >= 0 && RATE(srate) > 72; srate--) ; - KASSERT(srate >= 0, ("bogus rate set")); } } else { /* - * A fixed rate is to be used; ic_fixed_rate is an - * index into the supported rate set. Convert this + * A fixed rate is to be used; ic_fixed_rate is the + * IEEE code for this rate (sans basic bit). Convert this * to the index into the negotiated rate set for * the node. We know the rate is there because the * rate set is checked when the station associates. */ - const struct ieee80211_rateset *rs = - &ic->ic_sup_rates[ic->ic_curmode]; - int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; /* NB: the rate set is assumed sorted */ srate = ni->ni_rates.rs_nrates - 1; - for (; srate >= 0 && RATE(srate) != r; srate--) + for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--) ; - KASSERT(srate >= 0, - ("fixed rate %d not in rate set", ic->ic_fixed_rate)); } - ath_rate_update(sc, ni, srate); + /* + * The selected rate may not be available due to races + * and mode settings. Also orphaned nodes created in + * adhoc mode may not have any rate set so this lookup + * can fail. This is not fatal. + */ + ath_rate_update(sc, ni, srate < 0 ? 0 : srate); #undef RATE } diff --git a/sys/dev/ath/ath_rate/sample/sample.c b/sys/dev/ath/ath_rate/sample/sample.c index 6cf564e27357..180ef82f851b 100644 --- a/sys/dev/ath/ath_rate/sample/sample.c +++ b/sys/dev/ath/ath_rate/sample/sample.c @@ -32,6 +32,7 @@ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. + * */ #include <sys/cdefs.h> @@ -680,21 +681,23 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) sn->static_rate_ndx = -1; if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { /* - * A fixed rate is to be used; ic_fixed_rate is an - * index into the supported rate set. Convert this + * A fixed rate is to be used; ic_fixed_rate is the + * IEEE code for this rate (sans basic bit). Convert this * to the index into the negotiated rate set for * the node. */ - const struct ieee80211_rateset *rs = - &ic->ic_sup_rates[ic->ic_curmode]; - int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; /* NB: the rate set is assumed sorted */ srate = ni->ni_rates.rs_nrates - 1; - for (; srate >= 0 && RATE(srate) != r; srate--) + for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--) ; - KASSERT(srate >= 0, - ("fixed rate %d not in rate set", ic->ic_fixed_rate)); - sn->static_rate_ndx = srate; + /* + * The fixed rate may not be available due to races + * and mode settings. Also orphaned nodes created in + * adhoc mode may not have any rate set so this lookup + * can fail. + */ + if (srate >= 0) + sn->static_rate_ndx = srate; } DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s size 1600 rate/tt", diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index caaa939c984f..2841e8e473ae 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -135,11 +135,13 @@ static int ath_desc_alloc(struct ath_softc *); static void ath_desc_free(struct ath_softc *); static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *); static void ath_node_free(struct ieee80211_node *); -static u_int8_t ath_node_getrssi(const struct ieee80211_node *); +static int8_t ath_node_getrssi(const struct ieee80211_node *); +static void ath_node_getsignal(const struct ieee80211_node *, + int8_t *, int8_t *); static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); static void ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni, - int subtype, int rssi, u_int32_t rstamp); + int subtype, int rssi, int noise, u_int32_t rstamp); static void ath_setdefantenna(struct ath_softc *, u_int); static void ath_rx_proc(void *, int); static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); @@ -148,6 +150,7 @@ static int ath_tx_setup(struct ath_softc *, int, int); static int ath_wme_update(struct ieee80211com *); static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); static void ath_tx_cleanup(struct ath_softc *); +static void ath_freetx(struct mbuf *); static int ath_tx_start(struct ath_softc *, struct ieee80211_node *, struct ath_buf *, struct mbuf *); static void ath_tx_proc_q0(void *, int); @@ -158,7 +161,9 @@ static void ath_draintxq(struct ath_softc *); static void ath_stoprecv(struct ath_softc *); static int ath_startrecv(struct ath_softc *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); -static void ath_next_scan(void *); +static void ath_scan_start(struct ieee80211com *); +static void ath_scan_end(struct ieee80211com *); +static void ath_set_channel(struct ieee80211com *); static void ath_calibrate(void *); static int ath_newstate(struct ieee80211com *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); @@ -180,9 +185,6 @@ static void ath_announce(struct ath_softc *); SYSCTL_DECL(_hw_ath); /* XXX validate sysctl values */ -static int ath_dwelltime = 200; /* 5 channels/second */ -SYSCTL_INT(_hw_ath, OID_AUTO, dwell, CTLFLAG_RW, &ath_dwelltime, - 0, "channel dwell time (ms) for AP/station scanning"); static int ath_calinterval = 30; /* calibrate every 30 secs */ SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval, 0, "chip calibration interval (secs)"); @@ -345,7 +347,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) * like the phy mode. */ error = ath_getchannels(sc, ath_regdomain, ath_countrycode, - ath_xchanmode != 0, ath_outdoor != 0); + ath_outdoor != 0, ath_xchanmode != 0); if (error != 0) goto bad; @@ -357,6 +359,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ath_rate_setup(sc, IEEE80211_MODE_11G); ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); + ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); + ath_rate_setup(sc, IEEE80211_MODE_11NA); + ath_rate_setup(sc, IEEE80211_MODE_11NG); ath_rate_setup(sc, IEEE80211_MODE_HALF); ath_rate_setup(sc, IEEE80211_MODE_QUARTER); @@ -371,7 +376,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) if_printf(ifp, "failed to allocate descriptors: %d\n", error); goto bad; } - callout_init(&sc->sc_scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE); callout_init(&sc->sc_dfs_ch, CALLOUT_MPSAFE); @@ -419,7 +423,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { - /* + /* * Not enough hardware tx queues to properly do WME; * just punt and assign them all to the same h/w queue. * We could do a better job of this if, for example, @@ -435,7 +439,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; } - /* + /* * Special case certain configurations. Note the * CAB queue is handled by these specially so don't * include them when checking the txq setup mask. @@ -507,6 +511,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_TXFRAG /* handle tx frags */ ; /* * Query the hal to figure out h/w crypto support. @@ -571,6 +577,10 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) */ if (ath_hal_hasbursting(ah)) ic->ic_caps |= IEEE80211_C_BURST; + if (ath_hal_hasfastframes(ah)) + ic->ic_caps |= IEEE80211_C_FF; + if (ath_hal_getwirelessmodes(ah, ath_countrycode) & (HAL_MODE_108G|HAL_MODE_TURBO)) + ic->ic_caps |= IEEE80211_C_TURBOP; /* * Indicate we need the 802.11 header padded to a @@ -600,10 +610,14 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = ath_node_free; ic->ic_node_getrssi = ath_node_getrssi; + ic->ic_node_getsignal = ath_node_getsignal; sc->sc_recv_mgmt = ic->ic_recv_mgmt; ic->ic_recv_mgmt = ath_recv_mgmt; sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = ath_newstate; + ic->ic_scan_start = ath_scan_start; + ic->ic_scan_end = ath_scan_end; + ic->ic_set_channel = ath_set_channel; ic->ic_crypto.cs_max_keyix = sc->sc_keymax; ic->ic_crypto.cs_key_alloc = ath_key_alloc; ic->ic_crypto.cs_key_delete = ath_key_delete; @@ -736,8 +750,10 @@ ath_intr(void *arg) } if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ return; - if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & - IFF_DRV_RUNNING))) { + if ((ifp->if_flags & IFF_UP) == 0 || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + HAL_INT status; + DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n", __func__, ifp->if_flags); ath_hal_getisr(ah, &status); /* clear ISR */ @@ -817,6 +833,7 @@ ath_fatal_proc(void *arg, int pending) struct ifnet *ifp = sc->sc_ifp; u_int32_t *state; u_int32_t len; + void *sp; if_printf(ifp, "hardware error; resetting\n"); /* @@ -824,8 +841,9 @@ ath_fatal_proc(void *arg, int pending) * are caused by DMA errors. Collect h/w state from * the hal so we can diagnose what's going on. */ - if (ath_hal_getfatalstate(sc->sc_ah, &state, &len)) { + if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); + state = sp; if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", state[0], state[1] , state[2], state[3], state[4], state[5]); @@ -885,20 +903,22 @@ ath_bmiss_proc(void *arg, int pending) * the frequency possibly mapped for GSM channels. */ static void -ath_mapchan(struct ieee80211com *ic, HAL_CHANNEL *hc, - const struct ieee80211_channel *chan) +ath_mapchan(HAL_CHANNEL *hc, const struct ieee80211_channel *chan) { #define N(a) (sizeof(a) / sizeof(a[0])) - static const u_int modeflags[] = { + static const u_int modeflags[IEEE80211_MODE_MAX] = { 0, /* IEEE80211_MODE_AUTO */ CHANNEL_A, /* IEEE80211_MODE_11A */ CHANNEL_B, /* IEEE80211_MODE_11B */ CHANNEL_PUREG, /* IEEE80211_MODE_11G */ 0, /* IEEE80211_MODE_FH */ - CHANNEL_ST, /* IEEE80211_MODE_TURBO_A */ - CHANNEL_108G /* IEEE80211_MODE_TURBO_G */ + CHANNEL_108A, /* IEEE80211_MODE_TURBO_A */ + CHANNEL_108G, /* IEEE80211_MODE_TURBO_G */ + CHANNEL_ST, /* IEEE80211_MODE_STURBO_A */ + CHANNEL_A, /* IEEE80211_MODE_11NA */ + CHANNEL_PUREG, /* IEEE80211_MODE_11NG */ }; - enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan); + enum ieee80211_phymode mode = ieee80211_chan2mode(chan); KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode)); KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode)); @@ -907,6 +927,12 @@ ath_mapchan(struct ieee80211com *ic, HAL_CHANNEL *hc, hc->channelFlags |= CHANNEL_HALF; if (IEEE80211_IS_CHAN_QUARTER(chan)) hc->channelFlags |= CHANNEL_QUARTER; + if (IEEE80211_IS_CHAN_HT20(chan)) + hc->channelFlags |= CHANNEL_HT20; + if (IEEE80211_IS_CHAN_HT40D(chan)) + hc->channelFlags |= CHANNEL_HT40MINUS; + if (IEEE80211_IS_CHAN_HT40U(chan)) + hc->channelFlags |= CHANNEL_HT40PLUS; hc->channel = IEEE80211_IS_CHAN_GSM(chan) ? 2422 + (922 - chan->ic_freq) : chan->ic_freq; @@ -939,7 +965,7 @@ ath_init(void *arg) * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ - ath_mapchan(ic, &sc->sc_curchan, ic->ic_curchan); + ath_mapchan(&sc->sc_curchan, ic->ic_curchan); if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) { if_printf(ifp, "unable to reset hardware; hal status %u\n", status); @@ -1103,7 +1129,7 @@ ath_reset(struct ifnet *ifp) * Convert to a HAL channel description with the flags * constrained to reflect the current operating mode. */ - ath_mapchan(ic, &sc->sc_curchan, ic->ic_curchan); + ath_mapchan(&sc->sc_curchan, ic->ic_curchan); ath_hal_intrset(ah, 0); /* disable interrupts */ ath_draintxq(sc); /* stop xmit side */ @@ -1116,14 +1142,14 @@ ath_reset(struct ifnet *ifp) sc->sc_diversity = ath_hal_getdiversity(ah); sc->sc_calinterval = 1; sc->sc_caltries = 0; + if (ath_startrecv(sc) != 0) /* restart recv */ + if_printf(ifp, "%s: unable to start recv logic\n", __func__); /* * We may be doing a reset in response to an ioctl * that changes the channel so update any state that * might change as a result. */ ath_chan_change(sc, ic->ic_curchan); - if (ath_startrecv(sc) != 0) /* restart recv */ - if_printf(ifp, "%s: unable to start recv logic\n", __func__); if (ic->ic_state == IEEE80211_S_RUN) ath_beacon_config(sc); /* restart beacons */ ath_hal_intrset(ah, sc->sc_imask); @@ -1132,6 +1158,369 @@ ath_reset(struct ifnet *ifp) return 0; } +static int +ath_ff_always(struct ath_txq *txq, struct ath_buf *bf) +{ + return 0; +} + +#if 0 +static int +ath_ff_ageflushtestdone(struct ath_txq *txq, struct ath_buf *bf) +{ + return (txq->axq_curage - bf->bf_age) < ATH_FF_STAGEMAX; +} +#endif + +/* + * Flush FF staging queue. + */ +static void +ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq, + int (*ath_ff_flushdonetest)(struct ath_txq *txq, struct ath_buf *bf)) +{ + struct ath_buf *bf; + struct ieee80211_node *ni; + int pktlen, pri; + + for (;;) { + ATH_TXQ_LOCK(txq); + /* + * Go from the back (oldest) to front so we can + * stop early based on the age of the entry. + */ + bf = TAILQ_LAST(&txq->axq_stageq, axq_headtype); + if (bf == NULL || ath_ff_flushdonetest(txq, bf)) { + ATH_TXQ_UNLOCK(txq); + break; + } + + ni = bf->bf_node; + pri = M_WME_GETAC(bf->bf_m); + KASSERT(ATH_NODE(ni)->an_ff_buf[pri], + ("no bf on staging queue %p", bf)); + ATH_NODE(ni)->an_ff_buf[pri] = NULL; + TAILQ_REMOVE(&txq->axq_stageq, bf, bf_stagelist); + + ATH_TXQ_UNLOCK(txq); + + DPRINTF(sc, ATH_DEBUG_FF, "%s: flush frame, age %u\n", + __func__, bf->bf_age); + + sc->sc_stats.ast_ff_flush++; + + /* encap and xmit */ + bf->bf_m = ieee80211_encap(&sc->sc_ic, bf->bf_m, ni); + if (bf->bf_m == NULL) { + DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, + "%s: discard, encapsulation failure\n", + __func__); + sc->sc_stats.ast_tx_encap++; + goto bad; + } + pktlen = bf->bf_m->m_pkthdr.len; /* NB: don't reference below */ + if (ath_tx_start(sc, ni, bf, bf->bf_m) == 0) { +#if 0 /*XXX*/ + ifp->if_opackets++; +#endif + continue; + } + bad: + if (ni != NULL) + ieee80211_free_node(ni); + bf->bf_node = NULL; + if (bf->bf_m != NULL) { + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK(sc); + } +} + +static __inline u_int32_t +ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m) +{ + u_int32_t framelen; + struct ath_buf *bf; + + /* + * Approximate the frame length to be transmitted. A swag to add + * the following maximal values to the skb payload: + * - 32: 802.11 encap + CRC + * - 24: encryption overhead (if wep bit) + * - 4 + 6: fast-frame header and padding + * - 16: 2 LLC FF tunnel headers + * - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd) + */ + framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14; + if (sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY) + framelen += 24; + bf = an->an_ff_buf[M_WME_GETAC(m)]; + if (bf != NULL) + framelen += bf->bf_m->m_pkthdr.len; + return ath_hal_computetxtime(sc->sc_ah, sc->sc_currates, framelen, + sc->sc_lastdatarix, AH_FALSE); +} + +/* + * Determine if a data frame may be aggregated via ff tunnelling. + * Note the caller is responsible for checking if the destination + * supports fast frames. + * + * NB: allowing EAPOL frames to be aggregated with other unicast traffic. + * Do 802.1x EAPOL frames proceed in the clear? Then they couldn't + * be aggregated with other types of frames when encryption is on? + * + * NB: assumes lock on an_ff_buf effectively held by txq lock mechanism. + */ +static __inline int +ath_ff_can_aggregate(struct ath_softc *sc, + struct ath_node *an, struct mbuf *m, int *flushq) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_txq *txq; + u_int32_t txoplimit; + u_int pri; + + *flushq = 0; + + /* + * If there is no frame to combine with and the txq has + * fewer frames than the minimum required; then do not + * attempt to aggregate this frame. + */ + pri = M_WME_GETAC(m); + txq = sc->sc_ac2q[pri]; + if (an->an_ff_buf[pri] == NULL && txq->axq_depth < sc->sc_fftxqmin) + return 0; + /* + * When not in station mode never aggregate a multicast + * frame; this insures, for example, that a combined frame + * does not require multiple encryption keys when using + * 802.1x/WPA. + */ + if (ic->ic_opmode != IEEE80211_M_STA && + ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) + return 0; + /* + * Consult the max bursting interval to insure a combined + * frame fits within the TxOp window. + */ + txoplimit = IEEE80211_TXOP_TO_US( + ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit); + if (txoplimit != 0 && ath_ff_approx_txtime(sc, an, m) > txoplimit) { + DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, + "%s: FF TxOp violation\n", __func__); + if (an->an_ff_buf[pri] != NULL) + *flushq = 1; + return 0; + } + return 1; /* try to aggregate */ +} + +/* + * Check if the supplied frame can be partnered with an existing + * or pending frame. Return a reference to any frame that should be + * sent on return; otherwise return NULL. + */ +static struct mbuf * +ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ath_node *an = ATH_NODE(ni); + struct ath_buf *bfstaged; + int ff_flush, pri; + + /* + * Check if the supplied frame can be aggregated. + * + * NB: we use the txq lock to protect references to + * an->an_ff_txbuf in ath_ff_can_aggregate(). + */ + ATH_TXQ_LOCK(txq); + pri = M_WME_GETAC(m); + if (ath_ff_can_aggregate(sc, an, m, &ff_flush)) { + struct ath_buf *bfstaged = an->an_ff_buf[pri]; + if (bfstaged != NULL) { + /* + * A frame is available for partnering; remove + * it, chain it to this one, and encapsulate. + */ + an->an_ff_buf[pri] = NULL; + TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); + ATH_TXQ_UNLOCK(txq); + + /* + * Chain mbufs and add FF magic. + */ + DPRINTF(sc, ATH_DEBUG_FF, + "[%s] aggregate fast-frame, age %u\n", + ether_sprintf(ni->ni_macaddr), txq->axq_curage); + m->m_nextpkt = NULL; + bfstaged->bf_m->m_nextpkt = m; + m = bfstaged->bf_m; + bfstaged->bf_m = NULL; + m->m_flags |= M_FF; + /* + * Release the node reference held while + * the packet sat on an_ff_buf[] + */ + bfstaged->bf_node = NULL; + ieee80211_free_node(ni); + + /* + * Return bfstaged to the free list. + */ + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); + ATH_TXBUF_UNLOCK(sc); + + return m; /* ready to go */ + } else { + /* + * No frame available, queue this frame to wait + * for a partner. Note that we hold the buffer + * and a reference to the node; we need the + * buffer in particular so we're certain we + * can flush the frame at a later time. + */ + DPRINTF(sc, ATH_DEBUG_FF, + "[%s] stage fast-frame, age %u\n", + ether_sprintf(ni->ni_macaddr), txq->axq_curage); + + bf->bf_m = m; + bf->bf_node = ni; /* NB: held reference */ + bf->bf_age = txq->axq_curage; + an->an_ff_buf[pri] = bf; + TAILQ_INSERT_HEAD(&txq->axq_stageq, bf, bf_stagelist); + ATH_TXQ_UNLOCK(txq); + + return NULL; /* consumed */ + } + } + /* + * Frame could not be aggregated, it needs to be returned + * to the caller for immediate transmission. In addition + * we check if we should first flush a frame from the + * staging queue before sending this one. + * + * NB: ath_ff_can_aggregate only marks ff_flush if a frame + * is present to flush. + */ + if (ff_flush) { + int pktlen; + + bfstaged = an->an_ff_buf[pri]; + an->an_ff_buf[pri] = NULL; + TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist); + ATH_TXQ_UNLOCK(txq); + + DPRINTF(sc, ATH_DEBUG_FF, "[%s] flush staged frame\n", + ether_sprintf(an->an_node.ni_macaddr)); + + /* encap and xmit */ + bfstaged->bf_m = ieee80211_encap(ic, bfstaged->bf_m, ni); + if (bfstaged->bf_m == NULL) { + DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF, + "%s: discard, encap failure\n", __func__); + sc->sc_stats.ast_tx_encap++; + goto ff_flushbad; + } + pktlen = bfstaged->bf_m->m_pkthdr.len; + if (ath_tx_start(sc, ni, bfstaged, bfstaged->bf_m)) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: discard, xmit failure\n", __func__); + ff_flushbad: + /* + * Unable to transmit frame that was on the staging + * queue. Reclaim the node reference and other + * resources. + */ + if (ni != NULL) + ieee80211_free_node(ni); + bfstaged->bf_node = NULL; + if (bfstaged->bf_m != NULL) { + m_freem(bfstaged->bf_m); + bfstaged->bf_m = NULL; + } + + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); + ATH_TXBUF_UNLOCK(sc); + } else { +#if 0 + ifp->if_opackets++; +#endif + } + } else { + if (an->an_ff_buf[pri] != NULL) { + /* + * XXX: out-of-order condition only occurs for AP + * mode and multicast. There may be no valid way + * to get this condition. + */ + DPRINTF(sc, ATH_DEBUG_FF, "[%s] out-of-order frame\n", + ether_sprintf(an->an_node.ni_macaddr)); + /* XXX stat */ + } + ATH_TXQ_UNLOCK(txq); + } + return m; +} + +/* + * Cleanup driver resources when we run out of buffers + * while processing fragments; return the tx buffers + * allocated and drop node references. + */ +static void +ath_txfrag_cleanup(struct ath_softc *sc, + ath_bufhead *frags, struct ieee80211_node *ni) +{ + struct ath_buf *bf, *next; + + ATH_TXBUF_LOCK_ASSERT(sc); + + STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { + /* NB: bf assumed clean */ + STAILQ_REMOVE_HEAD(frags, bf_list); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ieee80211_node_decref(ni); + } +} + +/* + * Setup xmit of a fragmented frame. Allocate a buffer + * for each frag and bump the node reference count to + * reflect the held reference to be setup by ath_tx_start. + */ +static int +ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, + struct mbuf *m0, struct ieee80211_node *ni) +{ + struct mbuf *m; + struct ath_buf *bf; + + ATH_TXBUF_LOCK(sc); + for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { + bf = STAILQ_FIRST(&sc->sc_txbuf); + if (bf == NULL) { /* out of buffers, cleanup */ + ath_txfrag_cleanup(sc, frags, ni); + break; + } + STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + ieee80211_node_incref(ni); + STAILQ_INSERT_TAIL(frags, bf, bf_list); + } + ATH_TXBUF_UNLOCK(sc); + + return !STAILQ_EMPTY(frags); +} + static void ath_start(struct ifnet *ifp) { @@ -1140,9 +1529,12 @@ ath_start(struct ifnet *ifp) struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct ath_buf *bf; - struct mbuf *m; + struct mbuf *m, *next; struct ieee80211_frame *wh; struct ether_header *eh; + struct ath_txq *txq; + ath_bufhead frags; + int pri; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) return; @@ -1189,7 +1581,8 @@ ath_start(struct ifnet *ifp) ATH_TXBUF_UNLOCK(sc); break; } - /* + STAILQ_INIT(&frags); + /* * Find the node for the destination so we can do * things like power save and fast frames aggregation. */ @@ -1213,7 +1606,16 @@ ath_start(struct ifnet *ifp) * to the 802.11 layer and continue. We'll get * the frame back when the time is right. */ - ieee80211_pwrsave(ic, ni, m); + ieee80211_pwrsave(ni, m); + /* + * If we're in power save mode 'cuz of a bg + * scan cancel it so the traffic can flow. + * The packet we just queued will automatically + * get sent when we drop out of power save. + * XXX locking + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_scan(ic); goto reclaim; } /* calculate priority so we can find the tx queue */ @@ -1224,6 +1626,28 @@ ath_start(struct ifnet *ifp) m_freem(m); goto bad; } + pri = M_WME_GETAC(m); + txq = sc->sc_ac2q[pri]; + if (ni->ni_ath_flags & IEEE80211_NODE_FF) { + /* + * Check queue length; if too deep drop this + * frame (tail drop considered good). + */ + if (txq->axq_depth >= sc->sc_fftxqmax) { + DPRINTF(sc, ATH_DEBUG_FF, + "[%s] tail drop on q %u depth %u\n", + ether_sprintf(ni->ni_macaddr), + txq->axq_qnum, txq->axq_depth); + sc->sc_stats.ast_tx_qfull++; + m_freem(m); + goto reclaim; + } + m = ath_ff_check(sc, txq, bf, m, ni); + if (m == NULL) { + /* NB: ni ref & bf held on stageq */ + continue; + } + } ifp->if_opackets++; BPF_MTAP(ifp, m); /* @@ -1237,6 +1661,20 @@ ath_start(struct ifnet *ifp) sc->sc_stats.ast_tx_encap++; goto bad; } + /* + * Check for fragmentation. If this frame + * has been broken up verify we have enough + * buffers to send all the fragments so all + * go out or none... + */ + if ((m->m_flags & M_FRAG) && + !ath_txfrag_setup(sc, &frags, m, ni)) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: out of txfrag buffers\n", __func__); + ic->ic_stats.is_tx_nobuf++; /* XXX */ + ath_freetx(m); + goto bad; + } } else { /* * Hack! The referenced node pointer is in the @@ -1267,20 +1705,62 @@ ath_start(struct ifnet *ifp) sc->sc_stats.ast_tx_mgmt++; } + nextfrag: + /* + * Pass the frame to the h/w for transmission. + * Fragmented frames have each frag chained together + * with m_nextpkt. We know there are sufficient ath_buf's + * to send all the frags because of work done by + * ath_txfrag_setup. We leave m_nextpkt set while + * calling ath_tx_start so it can use it to extend the + * the tx duration to cover the subsequent frag and + * so it can reclaim all the mbufs in case of an error; + * ath_tx_start clears m_nextpkt once it commits to + * handing the frame to the hardware. + */ + next = m->m_nextpkt; if (ath_tx_start(sc, ni, bf, m)) { bad: ifp->if_oerrors++; reclaim: + bf->bf_m = NULL; + bf->bf_node = NULL; ATH_TXBUF_LOCK(sc); STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ath_txfrag_cleanup(sc, &frags, ni); ATH_TXBUF_UNLOCK(sc); if (ni != NULL) ieee80211_free_node(ni); continue; } + if (next != NULL) { + /* + * Beware of state changing between frags. + * XXX check sta power-save state? + */ + if (ic->ic_state != IEEE80211_S_RUN) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: flush fragmented packet, state %s\n", + __func__, + ieee80211_state_name[ic->ic_state]); + ath_freetx(next); + goto reclaim; + } + m = next; + bf = STAILQ_FIRST(&frags); + KASSERT(bf != NULL, ("no buf for txfrag")); + STAILQ_REMOVE_HEAD(&frags, bf_list); + goto nextfrag; + } - sc->sc_tx_timer = 5; - ifp->if_timer = 1; + ifp->if_timer = 5; +#if 0 + /* + * Flush stale frames from the fast-frame staging queue. + */ + if (ic->ic_opmode != IEEE80211_M_STA) + ath_ff_stageq_flush(sc, txq, ath_ff_ageflushtestdone); +#endif } } @@ -1306,7 +1786,7 @@ ath_media_change(struct ifnet *ifp) } else sc->sc_opmode = ic->ic_opmode; if (IS_UP(ifp)) - ath_init(ifp->if_softc); /* XXX lose error */ + ath_init(sc); /* XXX lose error */ error = 0; } return error; @@ -1770,7 +2250,7 @@ ath_key_update_end(struct ieee80211com *ic) * - when in monitor mode */ static u_int32_t -ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state) +ath_calcrxfilter(struct ath_softc *sc) { #define RX_FILTER_PRESERVE (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR) struct ieee80211com *ic = &sc->sc_ic; @@ -1787,7 +2267,7 @@ ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state) rfilt |= HAL_RX_FILTER_PROM; if (ic->ic_opmode == IEEE80211_M_STA || ic->ic_opmode == IEEE80211_M_IBSS || - state == IEEE80211_S_SCAN) + sc->sc_scanning) rfilt |= HAL_RX_FILTER_BEACON; if (ic->ic_opmode == IEEE80211_M_MONITOR) rfilt |= HAL_RX_FILTER_CONTROL; @@ -1806,7 +2286,7 @@ ath_mode_init(struct ath_softc *sc) struct ifmultiaddr *ifma; /* configure rx filter */ - rfilt = ath_calcrxfilter(sc, ic->ic_state); + rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(ah, rfilt); /* configure operational mode */ @@ -2381,9 +2861,7 @@ ath_beacon_config(struct ath_softc *sc) #endif /* * Calculate the number of consecutive beacons to miss - * before taking a BMISS interrupt. The configuration - * is specified in ms, so we need to convert that to - * TU's and then calculate based on the beacon interval. + * before taking a BMISS interrupt. * Note that we clamp the result to at most 10 beacons. */ bs.bs_bmissthreshold = ic->ic_bmissthreshold; @@ -2406,7 +2884,7 @@ ath_beacon_config(struct ath_softc *sc) if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); - DPRINTF(sc, ATH_DEBUG_BEACON, + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" , __func__ , tsf, tsftu @@ -2699,7 +3177,7 @@ ath_node_free(struct ieee80211_node *ni) sc->sc_node_free(ni); } -static u_int8_t +static int8_t ath_node_getrssi(const struct ieee80211_node *ni) { #define HAL_EP_RND(x, mul) \ @@ -2719,6 +3197,22 @@ ath_node_getrssi(const struct ieee80211_node *ni) #undef HAL_EP_RND } +static void +ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ath_softc *sc = ic->ic_ifp->if_softc; + struct ath_hal *ah = sc->sc_ah; + HAL_CHANNEL hchan; + + *rssi = ath_node_getrssi(ni); + if (ni->ni_chan != IEEE80211_CHAN_ANYC) { + ath_mapchan(&hchan, ni->ni_chan); + *noise = ath_hal_getchannoise(ah, &hchan); + } else + *noise = -95; /* nominally correct */ +} + static int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) { @@ -2811,7 +3305,7 @@ ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf) static void ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni, - int subtype, int rssi, u_int32_t rstamp) + int subtype, int rssi, int noise, u_int32_t rstamp) { struct ath_softc *sc = ic->ic_ifp->if_softc; @@ -2819,7 +3313,7 @@ ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, * Call up first so subsequent work can use information * potentially stored in the node (e.g. for ibss merge). */ - sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, rstamp); + sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, noise, rstamp); switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: /* update rssi statistics for use by the hal */ @@ -2880,6 +3374,7 @@ static int ath_rx_tap(struct ath_softc *sc, struct mbuf *m, const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) { +#define CHANNEL_HT (CHANNEL_HT20|CHANNEL_HT40PLUS|CHANNEL_HT40MINUS) u_int8_t rix; KASSERT(sc->sc_drvbpf != NULL, ("no tap")); @@ -2893,13 +3388,33 @@ ath_rx_tap(struct ath_softc *sc, struct mbuf *m, sc->sc_stats.ast_rx_tooshort++; return 0; } - sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(rs->rs_tstamp, tsf)); rix = rs->rs_rate; + sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; +#if HAL_ABI_VERSION >= 0x07050400 + if (sc->sc_curchan.channelFlags & CHANNEL_HT) { + /* + * For HT operation we must specify the channel + * attributes for each packet since they vary. + * We deduce this by from HT40 bit in the rx + * status and the MCS/legacy rate bit. + */ + sc->sc_rx_th.wr_chan_flags &= ~IEEE80211_CHAN_HT; + if (sc->sc_rx_th.wr_rate & 0x80) { /* HT rate */ + /* XXX 40U/40D */ + sc->sc_rx_th.wr_chan_flags |= + (rs->rs_flags & HAL_RX_2040) ? + IEEE80211_CHAN_HT40U : IEEE80211_CHAN_HT20; + if ((rs->rs_flags & HAL_RX_GI) == 0) + sc->sc_rx_th.wr_flags |= + IEEE80211_RADIOTAP_F_SHORTGI; + } + } +#endif + sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(rs->rs_tstamp, tsf)); if (rs->rs_status & HAL_RXERR_CRC) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX propagate other error flags from descriptor */ - sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; sc->sc_rx_th.wr_antsignal = rs->rs_rssi + nf; sc->sc_rx_th.wr_antnoise = nf; sc->sc_rx_th.wr_antenna = rs->rs_antenna; @@ -2907,6 +3422,7 @@ ath_rx_tap(struct ath_softc *sc, struct mbuf *m, bpf_mtap2(sc->sc_drvbpf, &sc->sc_rx_th, sc->sc_rx_th_len, m); return 1; +#undef CHANNEL_HT } static void @@ -2976,24 +3492,12 @@ ath_rx_proc(void *arg, int npending) bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RECV_DESC) - ath_printrxbuf(bf, 0, status == HAL_OK); + ath_printrxbuf(bf, 0, status == HAL_OK); #endif if (status == HAL_EINPROGRESS) break; STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); - if (rs->rs_more) { - /* - * Frame spans multiple descriptors; this - * cannot happen yet as we don't support - * jumbograms. If not in monitor mode, - * discard the frame. - */ - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - sc->sc_stats.ast_rx_toobig++; - goto rx_next; - } - /* fall thru for monitor mode handling... */ - } else if (rs->rs_status != 0) { + if (rs->rs_status != 0) { if (rs->rs_status & HAL_RXERR_CRC) sc->sc_stats.ast_rx_crcerr++; if (rs->rs_status & HAL_RXERR_FIFO) @@ -3002,7 +3506,7 @@ ath_rx_proc(void *arg, int npending) sc->sc_stats.ast_rx_phyerr++; phyerr = rs->rs_phyerr & 0x1f; sc->sc_stats.ast_rx_phy[phyerr]++; - goto rx_next; + goto rx_error; /* NB: don't count in ierrors */ } if (rs->rs_status & HAL_RXERR_DECRYPT) { /* @@ -3039,6 +3543,14 @@ ath_rx_proc(void *arg, int npending) } } ifp->if_ierrors++; +rx_error: + /* + * Cleanup any pending partial frame. + */ + if (sc->sc_rxpending != NULL) { + m_freem(sc->sc_rxpending); + sc->sc_rxpending = NULL; + } /* * When a tap is present pass error frames * that have been requested. By default we @@ -3070,9 +3582,42 @@ rx_accept: bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); bf->bf_m = NULL; - m->m_pkthdr.rcvif = ifp; len = rs->rs_datalen; - m->m_pkthdr.len = m->m_len = len; + m->m_len = len; + + if (rs->rs_more) { + /* + * Frame spans multiple descriptors; save + * it for the next completed descriptor, it + * will be used to construct a jumbogram. + */ + if (sc->sc_rxpending != NULL) { + /* NB: max frame size is currently 2 clusters */ + sc->sc_stats.ast_rx_toobig++; + m_freem(sc->sc_rxpending); + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + sc->sc_rxpending = m; + goto rx_next; + } else if (sc->sc_rxpending != NULL) { + /* + * This is the second part of a jumbogram, + * chain it to the first mbuf, adjust the + * frame length, and clear the rxpending state. + */ + sc->sc_rxpending->m_next = m; + sc->sc_rxpending->m_pkthdr.len += len; + m = sc->sc_rxpending; + sc->sc_rxpending = NULL; + } else { + /* + * Normal single-descriptor receive; setup + * the rcvif and packet length. + */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + } sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; @@ -3095,7 +3640,7 @@ rx_accept: } if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { - ieee80211_dump_pkt(mtod(m, caddr_t), len, + ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, sc->sc_hwmap[rs->rs_rate].ieeerate, rs->rs_rssi); } @@ -3120,7 +3665,8 @@ rx_accept: /* * Send frame up for processing. */ - type = ieee80211_input(ic, m, ni, rs->rs_rssi, rs->rs_tstamp); + type = ieee80211_input(ic, m, ni, + rs->rs_rssi, nf, rs->rs_tstamp); ieee80211_free_node(ni); if (sc->sc_diversity) { /* @@ -3182,6 +3728,8 @@ ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) txq->axq_link = NULL; STAILQ_INIT(&txq->axq_q); ATH_TXQ_LOCK_INIT(sc, txq); + TAILQ_INIT(&txq->axq_stageq); + txq->axq_curage = 0; } /* @@ -3282,7 +3830,7 @@ ath_txq_update(struct ath_softc *sc, int ac) ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); qi.tqi_aifs = wmep->wmep_aifsn; qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); - qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); + qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { @@ -3437,6 +3985,22 @@ ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate) return 0; /* NB: lowest rate */ } +/* + * Reclaim mbuf resources. For fragmented frames we + * need to claim each frag chained with m_nextpkt. + */ +static void +ath_freetx(struct mbuf *m) +{ + struct mbuf *next; + + do { + next = m->m_nextpkt; + m->m_nextpkt = NULL; + m_freem(m); + } while ((m = next) != NULL); +} + static int ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) { @@ -3455,7 +4019,7 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) bf->bf_nseg = ATH_TXDESC+1; } else if (error != 0) { sc->sc_stats.ast_tx_busdma++; - m_freem(m0); + ath_freetx(m0); return error; } /* @@ -3467,7 +4031,7 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) sc->sc_stats.ast_tx_linear++; m = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC); if (m == NULL) { - m_freem(m0); + ath_freetx(m0); sc->sc_stats.ast_tx_nombuf++; return ENOMEM; } @@ -3477,14 +4041,14 @@ ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0) BUS_DMA_NOWAIT); if (error != 0) { sc->sc_stats.ast_tx_busdma++; - m_freem(m0); + ath_freetx(m0); return error; } KASSERT(bf->bf_nseg <= ATH_TXDESC, ("too many segments after defrag; nseg %u", bf->bf_nseg)); } else if (bf->bf_nseg == 0) { /* null packet, discard */ sc->sc_stats.ast_tx_nodata++; - m_freem(m0); + ath_freetx(m0); return EIO; } DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", @@ -3565,7 +4129,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf struct ath_hal *ah = sc->sc_ah; struct ifnet *ifp = sc->sc_ifp; const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; - int error, iswep, ismcast, ismrr; + int error, iswep, ismcast, isfrag, ismrr; int keyix, hdrlen, pktlen, try0; u_int8_t rix, txrate, ctsrate; u_int8_t cix = 0xff; /* NB: silence compiler */ @@ -3582,6 +4146,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf wh = mtod(m0, struct ieee80211_frame *); iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + isfrag = m0->m_flags & M_FRAG; hdrlen = ieee80211_anyhdrsize(wh); /* * Packet length must not include any @@ -3606,21 +4171,22 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf * 802.11 layer counts failures and provides * debugging/diagnostics. */ - m_freem(m0); + ath_freetx(m0); return EIO; } /* * Adjust the packet + header lengths for the crypto * additions and calculate the h/w key index. When * a s/w mic is done the frame will have had any mic - * added to it prior to entry so m0->m_pkthdr.len above will + * added to it prior to entry so m0->m_pkthdr.len will * account for it. Otherwise we need to add it to the * packet length. */ cip = k->wk_cipher; hdrlen += cip->ic_header; pktlen += cip->ic_header + cip->ic_trailer; - if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0) + /* NB: frags always have any TKIP MIC done in s/w */ + if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag) pktlen += cip->ic_miclen; keyix = k->wk_keyix; @@ -3739,6 +4305,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf ath_rate_findrate(sc, an, shortPreamble, pktlen, &rix, &try0, &txrate); sc->sc_txrate = txrate; /* for LED blinking */ + sc->sc_lastdatarix = rix; /* for fast frames */ if (try0 != ATH_TXMAXTRY) ismrr = 1; } @@ -3750,7 +4317,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf if_printf(ifp, "bogus frame type 0x%x (%s)\n", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); /* XXX statistic */ - m_freem(m0); + ath_freetx(m0); return EIO; } txq = sc->sc_ac2q[pri]; @@ -3771,7 +4338,8 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf */ if (ismcast) { flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ - } else if (pktlen > ic->ic_rtsthreshold) { + } else if (pktlen > ic->ic_rtsthreshold && + (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) { flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ cix = rt->info[rix].controlRate; sc->sc_stats.ast_tx_rts++; @@ -3792,7 +4360,17 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf flags |= HAL_TXDESC_RTSENA; else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= HAL_TXDESC_CTSENA; - cix = rt->info[sc->sc_protrix].controlRate; + if (isfrag) { + /* + * For frags it would be desirable to use the + * highest CCK rate for RTS/CTS. But stations + * farther away may detect it at a lower CCK rate + * so use the configured protection rate instead + * (for now). + */ + cix = rt->info[sc->sc_protrix].controlRate; + } else + cix = rt->info[sc->sc_protrix].controlRate; sc->sc_stats.ast_tx_protect++; } @@ -3803,13 +4381,31 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf if ((flags & HAL_TXDESC_NOACK) == 0 && (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { u_int16_t dur; - /* - * XXX not right with fragmentation. - */ if (shortPreamble) dur = rt->info[rix].spAckDuration; else dur = rt->info[rix].lpAckDuration; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { + dur += dur; /* additional SIFS+ACK */ + KASSERT(m0->m_nextpkt != NULL, ("no fragment")); + /* + * Include the size of next fragment so NAV is + * updated properly. The last fragment uses only + * the ACK duration + */ + dur += ath_hal_computetxtime(ah, rt, + m0->m_nextpkt->m_pkthdr.len, + rix, shortPreamble); + } + if (isfrag) { + /* + * Force hardware to use computed duration for next + * fragment by disabling multi-rate retry which updates + * duration based on the multi-rate duration table. + */ + ismrr = 0; + try0 = ATH_TXMGTTRY; /* XXX? */ + } *(u_int16_t *)wh->i_dur = htole16(dur); } @@ -3859,8 +4455,15 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf } else ctsrate = 0; + /* + * At this point we are committed to sending the frame + * and we don't need to look at m_nextpkt; clear it in + * case this frame is part of frag chain. + */ + m0->m_nextpkt = NULL; + if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) - ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len, + ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, sc->sc_hwmap[txrate].ieeerate, -1); if (bpf_peers_present(ic->ic_rawbpf)) @@ -3872,6 +4475,8 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags; if (iswep) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; + if (isfrag) + sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG; sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate; sc->sc_tx_th.wt_txpower = ni->ni_txpower; sc->sc_tx_th.wt_antenna = sc->sc_txantenna; @@ -3880,7 +4485,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf &sc->sc_tx_th, sc->sc_tx_th_len, m0); } - /* + /* * Determine if a tx interrupt should be generated for * this descriptor. We take a tx interrupt to reap * descriptors when the h/w hits an EOL condition or @@ -4002,6 +4607,8 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) sc->sc_stats.ast_tx_fifoerr++; if (ts->ts_status & HAL_TXERR_FILT) sc->sc_stats.ast_tx_filtered++; + if (bf->bf_m->m_flags & M_FF) + sc->sc_stats.ast_ff_txerr++; } sr = ts->ts_shortretry; lr = ts->ts_longretry; @@ -4021,6 +4628,13 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) ath_rate_tx_complete(sc, an, bf); } /* + * Do any tx complete callback. Note this must + * be done before releasing the node reference. + */ + if (bf->bf_m->m_flags & M_TXCB) + ieee80211_process_callback(ni, bf->bf_m, + ts->ts_status); + /* * Reclaim reference to node. * * NB: the node may be reclaimed here if, for example @@ -4032,6 +4646,7 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + m_freem(bf->bf_m); bf->bf_m = NULL; bf->bf_node = NULL; @@ -4040,6 +4655,11 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); } + /* + * Flush fast-frame staging queue when traffic slows. + */ + if (txq->axq_depth <= 1) + ath_ff_stageq_flush(sc, txq, ath_ff_always); return nacked; } @@ -4066,7 +4686,7 @@ ath_tx_proc_q0(void *arg, int npending) if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum)) ath_tx_processq(sc, sc->sc_cabq); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->sc_tx_timer = 0; + ifp->if_timer = 0; if (sc->sc_softled) ath_led_event(sc, ATH_LED_TX); @@ -4103,7 +4723,7 @@ ath_tx_proc_q0123(void *arg, int npending) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->sc_tx_timer = 0; + ifp->if_timer = 0; if (sc->sc_softled) ath_led_event(sc, ATH_LED_TX); @@ -4132,7 +4752,7 @@ ath_tx_proc(void *arg, int npending) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->sc_tx_timer = 0; + ifp->if_timer = 0; if (sc->sc_softled) ath_led_event(sc, ATH_LED_TX); @@ -4169,13 +4789,11 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) ath_printtxbuf(bf, txq->axq_qnum, ix, ath_hal_txprocdesc(ah, bf->bf_desc, &bf->bf_status.ds_txstat) == HAL_OK); - ieee80211_dump_pkt(mtod(bf->bf_m, caddr_t), + ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t), bf->bf_m->m_len, 0, -1); } #endif /* ATH_DEBUG */ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - m_freem(bf->bf_m); - bf->bf_m = NULL; ni = bf->bf_node; bf->bf_node = NULL; if (ni != NULL) { @@ -4184,6 +4802,9 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) */ ieee80211_free_node(ni); } + m_freem(bf->bf_m); + bf->bf_m = NULL; + ATH_TXBUF_LOCK(sc); STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); @@ -4235,13 +4856,13 @@ ath_draintxq(struct ath_softc *sc) ath_printtxbuf(bf, sc->sc_bhalq, 0, ath_hal_txprocdesc(ah, bf->bf_desc, &bf->bf_status.ds_txstat) == HAL_OK); - ieee80211_dump_pkt(mtod(bf->bf_m, caddr_t), + ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t), bf->bf_m->m_len, 0, -1); } } #endif /* ATH_DEBUG */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->sc_tx_timer = 0; + ifp->if_timer = 0; } /* @@ -4278,6 +4899,10 @@ ath_stoprecv(struct ath_softc *sc) } } #endif + if (sc->sc_rxpending != NULL) { + m_freem(sc->sc_rxpending); + sc->sc_rxpending = NULL; + } sc->sc_rxlink = NULL; /* just in case */ #undef PA2DESC } @@ -4292,6 +4917,7 @@ ath_startrecv(struct ath_softc *sc) struct ath_buf *bf; sc->sc_rxlink = NULL; + sc->sc_rxpending = NULL; STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { int error = ath_rxbuf_init(sc, bf); if (error != 0) { @@ -4316,9 +4942,7 @@ ath_startrecv(struct ath_softc *sc) static void ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) { - struct ieee80211com *ic = &sc->sc_ic; enum ieee80211_phymode mode; - u_int16_t flags; /* * Change channels and update the h/w rate map @@ -4329,30 +4953,18 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) else if (IEEE80211_IS_CHAN_QUARTER(chan)) mode = IEEE80211_MODE_QUARTER; else - mode = ieee80211_chan2mode(ic, chan); + mode = ieee80211_chan2mode(chan); if (mode != sc->sc_curmode) ath_setcurmode(sc, mode); - /* - * Update BPF state. NB: ethereal et. al. don't handle - * merged flags well so pick a unique mode for their use. - */ - if (IEEE80211_IS_CHAN_A(chan)) - flags = IEEE80211_CHAN_A; - /* XXX 11g schizophrenia */ - else if (IEEE80211_IS_CHAN_ANYG(chan)) - flags = IEEE80211_CHAN_G; - else - flags = IEEE80211_CHAN_B; - if (IEEE80211_IS_CHAN_T(chan)) - flags |= IEEE80211_CHAN_TURBO; - if (IEEE80211_IS_CHAN_HALF(chan)) - flags |= IEEE80211_CHAN_HALF; - if (IEEE80211_IS_CHAN_QUARTER(chan)) - flags |= IEEE80211_CHAN_QUARTER; - sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = - htole16(chan->ic_freq); - sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = - htole16(flags); + + sc->sc_rx_th.wr_chan_flags = htole32(chan->ic_flags); + sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags; + sc->sc_rx_th.wr_chan_freq = htole16(chan->ic_freq); + sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq; + sc->sc_rx_th.wr_chan_ieee = chan->ic_ieee; + sc->sc_tx_th.wt_chan_ieee = sc->sc_rx_th.wr_chan_ieee; + sc->sc_rx_th.wr_chan_maxpow = chan->ic_maxregpower; + sc->sc_tx_th.wt_chan_maxpow = sc->sc_rx_th.wr_chan_maxpow; } /* @@ -4409,7 +5021,7 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) * the flags constrained to reflect the current * operating mode. */ - ath_mapchan(ic, &hchan, chan); + ath_mapchan(&hchan, chan); DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n", @@ -4458,7 +5070,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ - ic->ic_ibss_chan = chan; ath_chan_change(sc, chan); /* @@ -4488,16 +5099,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) return 0; } -static void -ath_next_scan(void *arg) -{ - struct ath_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_next_scan(ic); -} - /* * Periodically recalibrate the PHY to account * for temperature/environment changes. @@ -4533,7 +5134,7 @@ ath_calibrate(void *arg) ath_hal_process_noisefloor(ah); /* * Poll more frequently when the IQ calibration is in - * progress to speedup loading the final settings. + * progress to speedup loading the final settings. * We temper this aggressive polling with an exponential * back off after 4 tries up to ath_calinterval. */ @@ -4557,6 +5158,63 @@ ath_calibrate(void *arg) ath_calibrate, sc); } +static void +ath_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ath_softc *sc = ifp->if_softc; + struct ath_hal *ah = sc->sc_ah; + u_int32_t rfilt; + + /* XXX calibration timer? */ + + sc->sc_scanning = 1; + sc->sc_syncbeacon = 0; + rfilt = ath_calcrxfilter(sc); + ath_hal_setrxfilter(ah, rfilt); + ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0); + + DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", + __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr)); +} + +static void +ath_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ath_softc *sc = ifp->if_softc; + struct ath_hal *ah = sc->sc_ah; + u_int32_t rfilt; + + sc->sc_scanning = 0; + rfilt = ath_calcrxfilter(sc); + ath_hal_setrxfilter(ah, rfilt); + ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); + + ath_hal_process_noisefloor(ah); + + DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", + __func__, rfilt, ether_sprintf(sc->sc_curbssid), + sc->sc_curaid); +} + +static void +ath_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ath_softc *sc = ifp->if_softc; + + (void) ath_chan_set(sc, ic->ic_curchan); + /* + * If we are returning to our bss channel then mark state + * so the next recv'd beacon's tsf will be used to sync the + * beacon timers. Note that since we only hear beacons in + * sta/ibss mode this has no effect in other operating modes. + */ + if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) + sc->sc_syncbeacon = 1; +} + static int ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { @@ -4564,8 +5222,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) struct ath_softc *sc = ifp->if_softc; struct ath_hal *ah = sc->sc_ah; struct ieee80211_node *ni; - int i, error; - const u_int8_t *bssid; + int i, error, stamode; u_int32_t rfilt; static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ @@ -4579,7 +5236,6 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ieee80211_state_name[ic->ic_state], ieee80211_state_name[nstate]); - callout_stop(&sc->sc_scan_ch); callout_stop(&sc->sc_cal_ch); callout_stop(&sc->sc_dfs_ch); ath_hal_setledstate(ah, leds[nstate]); /* set LED */ @@ -4604,26 +5260,28 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) goto done; } ni = ic->ic_bss; - error = ath_chan_set(sc, ic->ic_curchan); - if (error != 0) - goto bad; - rfilt = ath_calcrxfilter(sc, nstate); - if (nstate == IEEE80211_S_SCAN) - bssid = ifp->if_broadcastaddr; - else - bssid = ni->ni_bssid; + + rfilt = ath_calcrxfilter(sc); + stamode = (sc->sc_opmode == HAL_M_STA || sc->sc_opmode == HAL_M_IBSS); + if (stamode && nstate == IEEE80211_S_RUN) { + sc->sc_curaid = ni->ni_associd; + IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); + } else + sc->sc_curaid = 0; + + DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", + __func__, rfilt, ether_sprintf(sc->sc_curbssid), + sc->sc_curaid); + ath_hal_setrxfilter(ah, rfilt); - DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s\n", - __func__, rfilt, ether_sprintf(bssid)); + if (stamode) + ath_hal_setassocid(ah, sc->sc_curbssid, ni->ni_associd); - if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) - ath_hal_setassocid(ah, bssid, ni->ni_associd); - else - ath_hal_setassocid(ah, bssid, 0); - if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (ic->ic_opmode != IEEE80211_M_STA && + (ic->ic_flags & IEEE80211_F_PRIVACY)) { for (i = 0; i < IEEE80211_WEP_NKID; i++) if (ath_hal_keyisvalid(ah, i)) - ath_hal_keysetmac(ah, i, bssid); + ath_hal_keysetmac(ah, i, ni->ni_bssid); } /* @@ -4632,9 +5290,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) */ ath_rate_newstate(sc, nstate); - if (ic->ic_opmode == IEEE80211_M_MONITOR) { - /* nothing to do */; - } else if (nstate == IEEE80211_S_RUN) { + if (nstate == IEEE80211_S_RUN) { DPRINTF(sc, ATH_DEBUG_STATE, "%s(RUN): ic_flags=0x%08x iv=%d bssid=%s " "capinfo=0x%04x chan=%d\n" @@ -4692,7 +5348,6 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) default: break; } - /* * Let the hal process statistics collected during a * scan so it can provide calibrated noise floor data. @@ -4706,7 +5361,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; } else { ath_hal_intrset(ah, - sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); + sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); } done: @@ -4721,10 +5376,6 @@ done: /* start periodic recalibration timer */ callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz, ath_calibrate, sc); - } else if (nstate == IEEE80211_S_SCAN) { - /* start ap/neighbor scan timer */ - callout_reset(&sc->sc_scan_ch, (ath_dwelltime * hz) / 1000, - ath_next_scan, sc); } bad: return error; @@ -4786,16 +5437,11 @@ static int ath_getchannels(struct ath_softc *sc, HAL_REG_DOMAIN rd, HAL_CTRY_CODE cc, HAL_BOOL outdoor, HAL_BOOL xchanmode) { -#define COMPAT \ - (CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE|CHANNEL_HALF|CHANNEL_QUARTER) -#define IS_CHAN_PUBLIC_SAFETY(_c) \ - (((_c)->channelFlags & CHANNEL_5GHZ) && \ - ((_c)->channel > 4940 && (_c)->channel < 4990)) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = sc->sc_ifp; struct ath_hal *ah = sc->sc_ah; HAL_CHANNEL *chans; - int i, ix, nchan; + int i, nchan; u_int32_t regdomain; chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL), @@ -4814,70 +5460,49 @@ ath_getchannels(struct ath_softc *sc, } /* - * Convert HAL channels to ieee80211 ones and insert - * them in the table according to their channel number. + * Convert HAL channels to ieee80211 ones. */ memset(ic->ic_channels, 0, sizeof(ic->ic_channels)); for (i = 0; i < nchan; i++) { HAL_CHANNEL *c = &chans[i]; - u_int16_t flags; + struct ieee80211_channel *ichan = &ic->ic_channels[i]; - /* - * XXX we're not ready to handle the ieee number mapping - * for public safety channels as they overlap with any - * 2GHz channels; for now use a non-public safety - * numbering that is non-overlapping. - */ - ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags); - if (IS_CHAN_PUBLIC_SAFETY(c)) - ix += 37; /* XXX */ - if (ix > IEEE80211_CHAN_MAX) { - if_printf(ifp, "bad hal channel %d (%u/%x) ignored\n", - ix, c->channel, c->channelFlags); - continue; - } - if (ix < 0) { - /* XXX can't handle stuff <2400 right now */ - if (bootverbose) - if_printf(ifp, "hal channel %d (%u/%x) " - "cannot be handled; ignored\n", - ix, c->channel, c->channelFlags); - continue; - } + ichan->ic_ieee = ath_hal_mhz2ieee(ah, c->channel, + c->channelFlags); if (bootverbose) if_printf(ifp, "hal channel %u/%x -> %u\n", - c->channel, c->channelFlags, ix); - /* - * Calculate net80211 flags; most are compatible - * but some need massaging. Note the static turbo - * conversion can be removed once net80211 is updated - * to understand static vs. dynamic turbo. - */ - flags = c->channelFlags & COMPAT; - if (c->channelFlags & CHANNEL_STURBO) - flags |= IEEE80211_CHAN_TURBO; + c->channel, c->channelFlags, ichan->ic_ieee); + ichan->ic_freq = c->channel; + + if ((c->channelFlags & CHANNEL_PUREG) == CHANNEL_PUREG) { + /* + * Except for AR5211, HAL's PUREG means mixed + * DSSS and OFDM. + */ + ichan->ic_flags = c->channelFlags &~ CHANNEL_PUREG; + ichan->ic_flags |= IEEE80211_CHAN_G; + } else { + ichan->ic_flags = c->channelFlags; + } + if (ath_hal_isgsmsku(ah)) { /* remap to true frequencies */ - c->channel = 922 + (2422 - c->channel); - flags |= IEEE80211_CHAN_GSM; - ix = ieee80211_mhz2ieee(c->channel, flags); - } - if (ic->ic_channels[ix].ic_freq == 0) { - ic->ic_channels[ix].ic_freq = c->channel; - ic->ic_channels[ix].ic_flags = flags; - } else { - /* channels overlap; e.g. 11g and 11b */ - ic->ic_channels[ix].ic_flags |= flags; + ichan->ic_freq = 922 + (2422 - ichan->ic_freq); + ichan->ic_flags |= IEEE80211_CHAN_GSM; + ichan->ic_ieee = ieee80211_mhz2ieee(ichan->ic_freq, + ichan->ic_flags); } + ichan->ic_maxregpower = c->maxRegTxPower; /* dBm */ + ichan->ic_maxpower = c->maxTxPower; /* 1/2 dBm */ + ichan->ic_minpower = c->minTxPower; /* 1/2 dBm */ } + ic->ic_nchans = nchan; free(chans, M_TEMP); (void) ath_hal_getregdomain(ah, &sc->sc_regdomain); ath_hal_getcountrycode(ah, &sc->sc_countrycode); sc->sc_xchanmode = xchanmode; sc->sc_outdoor = outdoor; return 0; -#undef IS_CHAN_PUBLIC_SAFETY -#undef COMPAT } static void @@ -4950,7 +5575,7 @@ ath_update_txpow(struct ath_softc *sc) if (ath_hal_gettxpowlimit(ah, &txpow)) ic->ic_txpowlimit = sc->sc_curtxpow = txpow; } - /* + /* * Fetch max tx power level for status requests. */ if (ath_hal_getmaxtxpow(sc->sc_ah, &txpow)) @@ -4980,12 +5605,20 @@ ath_rate_setup(struct ath_softc *sc, u_int mode) rt = ath_hal_getratetable(ah, HAL_MODE_11G); break; case IEEE80211_MODE_TURBO_A: - /* XXX until static/dynamic turbo is fixed */ - rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); + rt = ath_hal_getratetable(ah, HAL_MODE_108A); break; case IEEE80211_MODE_TURBO_G: rt = ath_hal_getratetable(ah, HAL_MODE_108G); break; + case IEEE80211_MODE_STURBO_A: + rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); + break; + case IEEE80211_MODE_11NA: + rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); + break; + case IEEE80211_MODE_11NG: + rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); + break; default: DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", __func__, mode); @@ -5039,6 +5672,8 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) } sc->sc_hwmap[i].ieeerate = rt->info[ix].dot11Rate & IEEE80211_RATE_VAL; + if (rt->info[ix].phy == IEEE80211_T_HT) + sc->sc_hwmap[i].ieeerate |= 0x80; /* MCS */ sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; if (rt->info[ix].shortPreamble || rt->info[ix].phy == IEEE80211_T_OFDM) @@ -5120,21 +5755,13 @@ static void ath_watchdog(struct ifnet *ifp) { struct ath_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - ifp->if_timer = 0; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) - return; - if (sc->sc_tx_timer) { - if (--sc->sc_tx_timer == 0) { - if_printf(ifp, "device timeout\n"); - ath_reset(ifp); - ifp->if_oerrors++; - sc->sc_stats.ast_watchdog++; - } else - ifp->if_timer = 1; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) { + if_printf(ifp, "device timeout\n"); + ath_reset(ifp); + ifp->if_oerrors++; + sc->sc_stats.ast_watchdog++; } - ieee80211_watchdog(ic); } #ifdef ATH_DIAGAPI @@ -5249,9 +5876,8 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) /* NB: embed these numbers to get a consistent view */ sc->sc_stats.ast_tx_packets = ifp->if_opackets; sc->sc_stats.ast_rx_packets = ifp->if_ipackets; - sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic); - sc->sc_stats.ast_rx_noise = - ath_hal_getchannoise(sc->sc_ah, &sc->sc_curchan); + ieee80211_getsignal(ic, &sc->sc_stats.ast_rx_rssi, + &sc->sc_stats.ast_rx_noise); sc->sc_stats.ast_tx_rate = sc->sc_hwmap[sc->sc_txrate].ieeerate; ATH_UNLOCK(sc); /* @@ -5514,7 +6140,7 @@ ath_sysctl_countrycode(SYSCTL_HANDLER_ARGS) if (error || !req->newptr) return error; error = ath_getchannels(sc, sc->sc_regdomain, cc, - sc->sc_outdoor, sc->sc_xchanmode); + sc->sc_outdoor != 0, sc->sc_xchanmode != 0); if (error != 0) return error; ieee80211_media_init(ic, ath_media_change, ieee80211_media_status); @@ -5536,7 +6162,7 @@ ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS) if (!ath_hal_setregdomain(sc->sc_ah, rd)) return EINVAL; error = ath_getchannels(sc, rd, sc->sc_countrycode, - sc->sc_outdoor, sc->sc_xchanmode); + sc->sc_outdoor != 0, sc->sc_xchanmode != 0); if (error != 0) return error; ieee80211_media_init(ic, ath_media_change, ieee80211_media_status); @@ -5643,6 +6269,16 @@ ath_sysctlattach(struct ath_softc *sc) "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ath_sysctl_tpcts, "I", "tx power for cts frames"); } + if (ath_hal_hasfastframes(sc->sc_ah)) { + sc->sc_fftxqmin = ATH_FF_TXQMIN; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "fftxqmin", CTLFLAG_RW, &sc->sc_fftxqmin, 0, + "min frames before fast-frame staging"); + sc->sc_fftxqmax = ATH_FF_TXQMAX; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "fftxqmax", CTLFLAG_RW, &sc->sc_fftxqmax, 0, + "max queued frames before tail drop"); + } if (ath_hal_hasrfsilent(ah)) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0, @@ -5771,7 +6407,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, atype = HAL_PKT_TYPE_PSPOLL; if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) - ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len, + ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len, sc->sc_hwmap[txrate].ieeerate, -1); if (bpf_peers_present(ic->ic_rawbpf)) @@ -5899,8 +6535,7 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, if (ath_tx_raw_start(sc, ni, bf, m, params)) goto bad; } - sc->sc_tx_timer = 5; - ifp->if_timer = 1; + ifp->if_timer = 5; return 0; bad: diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h index 9f48ac2731ae..ccd7220bf19f 100644 --- a/sys/dev/ath/if_athioctl.h +++ b/sys/dev/ath/if_athioctl.h @@ -69,8 +69,8 @@ struct ath_stats { u_int32_t ast_tx_shortpre;/* tx frames with short preamble */ u_int32_t ast_tx_altrate; /* tx frames with alternate rate */ u_int32_t ast_tx_protect; /* tx frames with protection */ - u_int32_t ast_unused1; - u_int32_t ast_unused2; + u_int32_t ast_tx_ctsburst;/* tx frames with cts and bursting */ + u_int32_t ast_tx_ctsext; /* tx frames with cts extension */ u_int32_t ast_rx_nombuf; /* rx setup failed 'cuz no mbuf */ u_int32_t ast_rx_busdma; /* rx setup failed for dma resrcs */ u_int32_t ast_rx_orn; /* rx failed 'cuz of desc overrun */ @@ -87,7 +87,6 @@ struct ath_stats { u_int32_t ast_rx_ctl; /* rx discarded 'cuz ctl frame */ int8_t ast_tx_rssi; /* tx rssi of last ack */ int8_t ast_rx_rssi; /* rx rssi from histogram */ - int8_t ast_rx_noise; /* rx noise floor */ u_int8_t ast_tx_rate; /* IEEE rate of last unicast tx */ u_int32_t ast_be_xmit; /* beacons transmitted */ u_int32_t ast_be_nombuf; /* beacon setup failed 'cuz no mbuf */ @@ -104,7 +103,13 @@ struct ath_stats { u_int32_t ast_cabq_xmit; /* cabq frames transmitted */ u_int32_t ast_cabq_busy; /* cabq found busy */ u_int32_t ast_tx_raw; /* tx frames through raw api */ - u_int32_t ast_pad[29]; + u_int32_t ast_ff_txok; /* fast frames tx'd successfully */ + u_int32_t ast_ff_txerr; /* fast frames tx'd w/ error */ + u_int32_t ast_ff_rx; /* fast frames rx'd */ + u_int32_t ast_ff_flush; /* fast frames flushed from staging q */ + u_int32_t ast_tx_qfull; /* tx dropped 'cuz of queue limit */ + int8_t ast_rx_noise; /* rx noise floor */ + u_int32_t ast_pad[22]; }; #define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) @@ -131,10 +136,10 @@ struct ath_diag { (1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_XCHANNEL) | \ 0) struct ath_rx_radiotap_header { @@ -142,20 +147,23 @@ struct ath_rx_radiotap_header { u_int64_t wr_tsf; u_int8_t wr_flags; u_int8_t wr_rate; - u_int16_t wr_chan_freq; - u_int16_t wr_chan_flags; - u_int8_t wr_antsignal; - u_int8_t wr_antnoise; + int8_t wr_antsignal; + int8_t wr_antnoise; u_int8_t wr_antenna; -}; + u_int8_t wr_pad[3]; + u_int32_t wr_chan_flags; + u_int16_t wr_chan_freq; + u_int8_t wr_chan_ieee; + int8_t wr_chan_maxpow; +} __packed; #define ATH_TX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_XCHANNEL) | \ 0) struct ath_tx_radiotap_header { @@ -163,10 +171,12 @@ struct ath_tx_radiotap_header { u_int64_t wt_tsf; u_int8_t wt_flags; u_int8_t wt_rate; - u_int16_t wt_chan_freq; - u_int16_t wt_chan_flags; u_int8_t wt_txpower; u_int8_t wt_antenna; -}; + u_int32_t wt_chan_flags; + u_int16_t wt_chan_freq; + u_int8_t wt_chan_ieee; + int8_t wt_chan_maxpow; +} __packed; #endif /* _DEV_ATH_ATHIOCTL_H */ diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 7fd700e0a73f..af1045d10857 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -35,8 +35,6 @@ #ifndef _DEV_ATH_ATHVAR_H #define _DEV_ATH_ATHVAR_H -#include <sys/taskqueue.h> - #include <contrib/dev/ath/ah.h> #include <contrib/dev/ath/ah_desc.h> #include <net80211/ieee80211_radiotap.h> @@ -49,7 +47,7 @@ #define ATH_RXBUF 40 /* number of RX buffers */ #endif #ifndef ATH_TXBUF -#define ATH_TXBUF 100 /* number of TX buffers */ +#define ATH_TXBUF 200 /* number of TX buffers */ #endif #define ATH_TXDESC 10 /* number of descriptors per buffer */ #define ATH_TXMAXTRY 11 /* max number of transmit attempts */ @@ -71,10 +69,19 @@ #define ATH_KEYMAX 128 /* max key cache size we handle */ #define ATH_KEYBYTES (ATH_KEYMAX/NBBY) /* storage space in bytes */ +#define ATH_FF_TXQMIN 2 /* min txq depth for staging */ +#define ATH_FF_TXQMAX 50 /* maximum # of queued frames allowed */ +#define ATH_FF_STAGEMAX 5 /* max waiting period for staged frame*/ + +struct taskqueue; +struct kthread; +struct ath_buf; + /* driver-specific node state */ struct ath_node { struct ieee80211_node an_node; /* base class */ u_int32_t an_avgrssi; /* average rssi over all rx frames */ + struct ath_buf *an_ff_buf[WME_NUM_AC]; /* ff staging area */ /* variable-length rate control state follows */ }; #define ATH_NODE(ni) ((struct ath_node *)(ni)) @@ -93,6 +100,8 @@ struct ath_node { struct ath_buf { STAILQ_ENTRY(ath_buf) bf_list; + TAILQ_ENTRY(ath_buf) bf_stagelist; /* stage queue list */ + u_int32_t bf_age; /* age when placed on stageq */ int bf_nseg; int bf_flags; /* tx descriptor flags */ struct ath_desc *bf_desc; /* virtual addr of desc */ @@ -138,6 +147,13 @@ struct ath_txq { STAILQ_HEAD(, ath_buf) axq_q; /* transmit queue */ struct mtx axq_lock; /* lock on q and link */ char axq_name[12]; /* e.g. "ath0_txq4" */ + /* + * Fast-frame state. The staging queue holds awaiting + * a fast-frame pairing. Buffers on this queue are + * assigned an ``age'' and flushed when they wait too long. + */ + TAILQ_HEAD(axq_headtype, ath_buf) axq_stageq; + u_int32_t axq_curage; /* queue age */ }; #define ATH_TXQ_LOCK_INIT(_sc, _tq) do { \ @@ -153,6 +169,7 @@ struct ath_txq { #define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ (_tq)->axq_depth++; \ + (_tq)->axq_curage++; \ } while (0) #define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \ STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \ @@ -172,7 +189,7 @@ struct ath_softc { void (*sc_recv_mgmt)(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, - int, int, u_int32_t); + int, int, int, u_int32_t); int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); void (*sc_node_free)(struct ieee80211_node *); @@ -196,10 +213,12 @@ struct ath_softc { sc_ledstate: 1, /* LED on/off state */ sc_blinking: 1, /* LED blink operation active */ sc_mcastkey: 1, /* mcast key cache search */ + sc_scanning: 1, /* scanning active */ sc_syncbeacon:1,/* sync/resync beacon timers */ sc_hasclrkey:1, /* CLR key supported */ sc_xchanmode: 1,/* extended channel mode */ - sc_outdoor : 1;/* outdoor operation */ + sc_outdoor : 1,/* outdoor operation */ + sc_dturbo : 1; /* dynamic turbo in use */ /* rate tables */ #define IEEE80211_MODE_HALF (IEEE80211_MODE_MAX+0) #define IEEE80211_MODE_QUARTER (IEEE80211_MODE_MAX+1) @@ -208,7 +227,9 @@ struct ath_softc { enum ieee80211_phymode sc_curmode; /* current phy mode */ HAL_OPMODE sc_opmode; /* current operating mode */ u_int16_t sc_curtxpow; /* current tx power limit */ + u_int16_t sc_curaid; /* current association id */ HAL_CHANNEL sc_curchan; /* current h/w channel */ + u_int8_t sc_curbssid[IEEE80211_ADDR_LEN]; u_int8_t sc_rixmap[256]; /* IEEE to h/w rate table ix */ struct { u_int8_t ieeerate; /* IEEE rate */ @@ -220,7 +241,10 @@ struct ath_softc { u_int8_t sc_minrateix; /* min h/w rate index */ u_int8_t sc_mcastrix; /* mcast h/w rate index */ u_int8_t sc_protrix; /* protection rate index */ + u_int8_t sc_lastdatarix; /* last data frame rate index */ u_int sc_mcastrate; /* ieee rate for mcastrateix */ + u_int sc_fftxqmin; /* min frames before staging */ + u_int sc_fftxqmax; /* max frames before drop */ u_int sc_txantenna; /* tx antenna (fixed or auto) */ HAL_INT sc_imask; /* interrupt mask copy */ u_int sc_keymax; /* size of key cache */ @@ -253,6 +277,7 @@ struct ath_softc { struct ath_descdma sc_rxdma; /* RX descriptos */ ath_bufhead sc_rxbuf; /* receive buffer */ + struct mbuf *sc_rxpending; /* pending receive data */ u_int32_t *sc_rxlink; /* link ptr in last RX desc */ struct task sc_rxtask; /* rx int processing */ struct task sc_rxorntask; /* rxorn int processing */ @@ -264,7 +289,6 @@ struct ath_softc { ath_bufhead sc_txbuf; /* transmit buffer */ struct mtx sc_txbuflock; /* txbuf lock */ char sc_txname[12]; /* e.g. "ath0_buf" */ - int sc_tx_timer; /* transmit timeout */ u_int sc_txqsetup; /* h/w queues setup */ u_int sc_txintrperiod;/* tx interrupt batching */ struct ath_txq sc_txq[HAL_NUM_TX_QUEUES]; @@ -291,7 +315,6 @@ struct ath_softc { int sc_calinterval; /* current polling interval */ int sc_caltries; /* cals at current interval */ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */ - struct callout sc_scan_ch; /* callout handle for scan */ struct callout sc_dfs_ch; /* callout handle for dfs */ }; #define sc_tx_th u_tx_rt.th @@ -418,7 +441,7 @@ void ath_intr(void *); ((*(_ah)->ah_getDiagState)((_ah), (_id), \ (_indata), (_insize), (_outdata), (_outsize))) #define ath_hal_getfatalstate(_ah, _outdata, _outsize) \ - ath_hal_getdiagstate(_ah, 29, NULL, 0, (void **)(_outdata), _outsize) + ath_hal_getdiagstate(_ah, 29, NULL, 0, (_outdata), _outsize) #define ath_hal_setuptxqueue(_ah, _type, _irq) \ ((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq))) #define ath_hal_resettxqueue(_ah, _q) \ @@ -517,6 +540,8 @@ void ath_intr(void *); #else #define ath_hal_getmcastkeysearch(_ah) 0 #endif +#define ath_hal_hasfastframes(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK) #define ath_hal_hasrfsilent(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK) #define ath_hal_getrfkill(_ah) \ @@ -535,15 +560,8 @@ void ath_intr(void *); (ath_hal_getcapability(_ah, HAL_CAP_TPC_CTS, 0, _ptpcts) == HAL_OK) #define ath_hal_settpcts(_ah, _tpcts) \ ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL) -#if HAL_ABI_VERSION < 0x05120700 -#define ath_hal_process_noisefloor(_ah) -#define ath_hal_getchannoise(_ah, _c) (-96) -#define HAL_CAP_TPC_ACK 100 -#define HAL_CAP_TPC_CTS 101 -#else #define ath_hal_getchannoise(_ah, _c) \ ((*(_ah)->ah_getChanNoise)((_ah), (_c))) -#endif #if HAL_ABI_VERSION < 0x05122200 #define HAL_TXQ_TXOKINT_ENABLE TXQ_FLAG_TXOKINT_ENABLE #define HAL_TXQ_TXERRINT_ENABLE TXQ_FLAG_TXERRINT_ENABLE @@ -561,6 +579,14 @@ void ath_intr(void *); #define ath_hal_isgsmsku(ah) \ ((ah)->ah_countryCode == 843) #endif +#if HAL_ABI_VERSION < 0x07050400 +/* compat shims so code compilers--it won't work though */ +#define CHANNEL_HT20 0x10000 +#define CHANNEL_HT40PLUS 0x20000 +#define CHANNEL_HT40MINUS 0x40000 +#define HAL_MODE_11NG_HT20 0x008000 +#define HAL_MODE_11NA_HT20 0x010000 +#endif #define ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \ ((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq))) diff --git a/sys/dev/awi/awi.c b/sys/dev/awi/awi.c index 6b14f46bb11e..33f0a906c18b 100644 --- a/sys/dev/awi/awi.c +++ b/sys/dev/awi/awi.c @@ -181,7 +181,7 @@ static int awi_intr_lock(struct awi_softc *); static void awi_intr_unlock(struct awi_softc *); static int awi_newstate(struct ieee80211com *, enum ieee80211_state, int); static void awi_recv_mgmt(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, int, int, u_int32_t); + struct ieee80211_node *, int, int, int, u_int32_t); static int awi_send_mgmt(struct ieee80211com *, struct ieee80211_node *, int, int); static struct mbuf *awi_ether_encap(struct awi_softc *, struct mbuf *); @@ -525,9 +525,10 @@ awi_intr(void *arg) if (status & AWI_INT_CMD) awi_cmd_done(sc); if (status & AWI_INT_SCAN_CMPLT) { + /* XXX revisit scanning */ if (sc->sc_ic.ic_state == IEEE80211_S_SCAN && sc->sc_substate == AWI_ST_NONE) - ieee80211_next_scan(&sc->sc_ic); + ; } } sc->sc_cansleep = ocansleep; @@ -589,6 +590,7 @@ awi_init(struct ifnet *ifp) sc->sc_mib_local.Acting_as_AP = 1; break; case IEEE80211_M_MONITOR: + case IEEE80211_M_WDS: return ENODEV; } #if 0 @@ -596,9 +598,9 @@ awi_init(struct ifnet *ifp) #endif memset(&sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE); sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID; - sc->sc_mib_mac.aDesired_ESS_ID[1] = ic->ic_des_esslen; - memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], ic->ic_des_essid, - ic->ic_des_esslen); + sc->sc_mib_mac.aDesired_ESS_ID[1] = ic->ic_des_ssid[0].len; + memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); /* configure basic rate */ if (ic->ic_phytype == IEEE80211_T_FH) @@ -606,7 +608,7 @@ awi_init(struct ifnet *ifp) else rs = &ic->ic_sup_rates[IEEE80211_MODE_11B]; if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { - rate = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + rate = ic->ic_fixed_rate; } else { rate = 0; for (i = 0; i < rs->rs_nrates; i++) { @@ -659,18 +661,18 @@ awi_init(struct ifnet *ifp) if (ic->ic_opmode == IEEE80211_M_AHDEMO || ic->ic_opmode == IEEE80211_M_HOSTAP) { - ni->ni_chan = ic->ic_ibss_chan; + ni->ni_chan = ic->ic_des_chan; /* XXX? */ ni->ni_intval = ic->ic_bintval; ni->ni_rssi = 0; ni->ni_rstamp = 0; memset(&ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); ni->ni_rates = - ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + ic->ic_sup_rates[ieee80211_chan2mode(ni->ni_chan)]; IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - ni->ni_esslen = ic->ic_des_esslen; - memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); + ni->ni_esslen = ic->ic_des_ssid[0].len; + memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen); ni->ni_capinfo = IEEE80211_CAPINFO_ESS; if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ @@ -812,7 +814,7 @@ awi_start(struct ifnet *ifp) goto bad; if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && (m0->m_flags & M_PWR_SAV) == 0) { - ieee80211_pwrsave(ic, ni, m0); + ieee80211_pwrsave(ni, m0); continue; } m0 = ieee80211_encap(ic, m0, ni); @@ -865,7 +867,7 @@ awi_start(struct ifnet *ifp) #endif if ((ifp->if_flags & IFF_DEBUG) && (ifp->if_flags & IFF_LINK2)) - ieee80211_dump_pkt(m0->m_data, m0->m_len, + ieee80211_dump_pkt(ic, m0->m_data, m0->m_len, ic->ic_bss->ni_rates. rs_rates[ic->ic_bss->ni_txrate] & IEEE80211_RATE_VAL, -1); @@ -930,7 +932,6 @@ awi_watchdog(struct ifnet *ifp) ifp->if_timer = 1; } /* TODO: rate control */ - ieee80211_watchdog(&sc->sc_ic); out: sc->sc_cansleep = ocansleep; } @@ -1016,6 +1017,7 @@ awi_media_change(struct ifnet *ifp) ime = ic->ic_media.ifm_cur; if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) { i = -1; + rate = ic->ic_fixed_rate; } else { struct ieee80211_rateset *rs = &ic->ic_sup_rates[(ic->ic_phytype == IEEE80211_T_FH) @@ -1030,8 +1032,8 @@ awi_media_change(struct ifnet *ifp) if (i == rs->rs_nrates) return EINVAL; } - if (ic->ic_fixed_rate != i) { - ic->ic_fixed_rate = i; + if (ic->ic_fixed_rate != rate) { + ic->ic_fixed_rate = rate; error = ENETRESET; } @@ -1098,12 +1100,12 @@ awi_media_status(struct ifnet *ifp, struct ifmediareq *imr) if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) rate = 0; else - rate = ic->ic_sup_rates[mode]. - rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + rate = ic->ic_fixed_rate; } imr->ifm_active |= ieee80211_rate2media(ic, rate, mode); switch (ic->ic_opmode) { case IEEE80211_M_MONITOR: /* we should never reach here */ + case IEEE80211_M_WDS: break; case IEEE80211_M_STA: break; @@ -1240,7 +1242,8 @@ awi_rx_int(struct awi_softc *sc) } if ((ifp->if_flags & IFF_DEBUG) && (ifp->if_flags & IFF_LINK2)) - ieee80211_dump_pkt(m->m_data, m->m_len, + ieee80211_dump_pkt(ic, + m->m_data, m->m_len, rate / 5, rssi); if ((ifp->if_flags & IFF_LINK0) || sc->sc_adhoc_ap) @@ -1254,7 +1257,8 @@ awi_rx_int(struct awi_softc *sc) } ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); - ieee80211_input(ic, m, ni, rssi, rstamp); + /* XXX 0 for noise floor */ + ieee80211_input(ic, m, ni, rssi, 0, rstamp); ieee80211_free_node(ni); } else sc->sc_rxpend = m; @@ -1337,7 +1341,6 @@ awi_devget(struct awi_softc *sc, u_int32_t off, u_int16_t len) m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = MHLEN; - m->m_flags |= M_HASFCS; } else { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) { @@ -1367,6 +1370,10 @@ awi_devget(struct awi_softc *sc, u_int32_t off, u_int16_t len) *mp = m; mp = &m->m_next; } + if (top != NULL) { + /* Strip trailing 802.11 MAC FCS. */ + m_adj(top, -IEEE80211_CRC_LEN); + } return top; } @@ -1534,7 +1541,7 @@ awi_init_mibs(struct awi_softc *sc) } } sc->sc_cur_chan = cs->cs_def; - ic->ic_ibss_chan = &ic->ic_channels[cs->cs_def]; + ic->ic_curchan = &ic->ic_channels[cs->cs_def]; /* XXX? */ sc->sc_mib_local.Fragmentation_Dis = 1; sc->sc_mib_local.Add_PLCP_Dis = 0; @@ -1942,7 +1949,7 @@ awi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: case IEEE80211_S_INIT: - ieee80211_begin_scan(ic, 0); + /* XXX revisit scanning */; break; case IEEE80211_S_SCAN: /* scan next */ @@ -2112,14 +2119,14 @@ out: static void awi_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, struct ieee80211_node *ni, - int subtype, int rssi, u_int32_t rstamp) + int subtype, int rssi, int nf, u_int32_t rstamp) { struct awi_softc *sc = ic->ic_ifp->if_softc; /* probe request is handled by hardware */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_REQ) return; - (*sc->sc_recv_mgmt)(ic, m0, ni, subtype, rssi, rstamp); + (*sc->sc_recv_mgmt)(ic, m0, ni, subtype, rssi, nf, rstamp); } static int diff --git a/sys/dev/awi/awivar.h b/sys/dev/awi/awivar.h index f805b8fbfe97..762d1dad4d83 100644 --- a/sys/dev/awi/awivar.h +++ b/sys/dev/awi/awivar.h @@ -92,7 +92,7 @@ struct awi_softc { enum ieee80211_state, int); void (*sc_recv_mgmt)(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, - int, int, u_int32_t); + int, int, int, u_int32_t); int (*sc_send_mgmt)(struct ieee80211com *, struct ieee80211_node *, int, int); diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c index 03663984ea01..f4a3e8c09223 100644 --- a/sys/dev/if_ndis/if_ndis.c +++ b/sys/dev/if_ndis/if_ndis.c @@ -478,7 +478,7 @@ ndis_attach(dev) device_object *pdo; struct ifnet *ifp = NULL; int error = 0, len; - int i; + int i, j; sc = device_get_softc(dev); #if __FreeBSD_version < 600031 @@ -817,10 +817,11 @@ nonettypes: } #undef SETRATE #undef INCRATE + ic->ic_nchans = 12; /* * Taking yet more guesses here. */ - for (i = 1; i < IEEE80211_CHAN_MAX; i++) { + for (j = 0, i = 1; i <= 12; i++, j++) { int chanflag = 0; if (ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates) @@ -829,12 +830,11 @@ nonettypes: chanflag |= IEEE80211_CHAN_B; if (ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates && i > 14) - chanflag = IEEE80211_CHAN_A; + chanflag = IEEE80211_CHAN_A; if (chanflag == 0) break; - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, chanflag); - ic->ic_channels[i].ic_flags = chanflag; + ic->ic_channels[j].ic_freq = ieee80211_ieee2mhz(i, chanflag); + ic->ic_channels[j].ic_flags = chanflag; } /* @@ -897,8 +897,8 @@ got_crypto: ieee80211_media_init(ic, ieee80211_media_change, ndis_media_status); #endif - ic->ic_ibss_chan = IEEE80211_CHAN_ANYC; - ic->ic_bss->ni_chan = ic->ic_ibss_chan; + ic->ic_bsschan = IEEE80211_CHAN_ANYC; + ic->ic_bss->ni_chan = ic->ic_bsschan; #ifdef IEEE80211_F_WPA /* install key handing routines */ ic->ic_crypto.cs_key_set = ndis_add_key; @@ -2392,16 +2392,16 @@ ndis_setstate_80211(sc) config.nc_atimwin = 100; if (config.nc_fhconfig.ncf_dwelltime == 0) config.nc_fhconfig.ncf_dwelltime = 200; - if (rval == 0 && ic->ic_ibss_chan != IEEE80211_CHAN_ANYC) { + if (rval == 0 && ic->ic_bsschan != IEEE80211_CHAN_ANYC) { int chan, chanflag; - chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + chan = ieee80211_chan2ieee(ic, ic->ic_bsschan); chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ; if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) { config.nc_dsconfig = - ic->ic_ibss_chan->ic_freq * 1000; - ic->ic_bss->ni_chan = ic->ic_ibss_chan; + ic->ic_bsschan->ic_freq * 1000; + ic->ic_bss->ni_chan = ic->ic_bsschan; len = sizeof(config); config.nc_length = len; config.nc_fhconfig.ncf_length = @@ -2447,11 +2447,11 @@ ndis_setstate_80211(sc) len = sizeof(ssid); bzero((char *)&ssid, len); - ssid.ns_ssidlen = ic->ic_des_esslen; + ssid.ns_ssidlen = ic->ic_des_ssid[0].len; if (ssid.ns_ssidlen == 0) { ssid.ns_ssidlen = 1; } else - bcopy(ic->ic_des_essid, ssid.ns_ssid, ssid.ns_ssidlen); + bcopy(ic->ic_des_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen); rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); @@ -2497,6 +2497,9 @@ ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_M_MONITOR: imr->ifm_active |= IFM_IEEE80211_MONITOR; break; + case IEEE80211_M_WDS: + printf("WARNING: WDS operation mode not supported by NDIS\n"); + break; } switch (ic->ic_curmode) { case IEEE80211_MODE_11A: @@ -2682,7 +2685,7 @@ ndis_getstate_80211(sc) ic->ic_bss->ni_chan = &ic->ic_channels[chan]; ic->ic_des_chan = &ic->ic_channels[chan]; - ic->ic_ibss_chan = &ic->ic_channels[chan]; + ic->ic_bsschan = &ic->ic_channels[chan]; #if __FreeBSD_version >= 600007 ic->ic_curchan = &ic->ic_channels[chan]; #endif @@ -3190,6 +3193,8 @@ ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) bcopy((char *)wb->nwbx_ies + sizeof(struct ndis_80211_fixed_ies), cp, sr->isr_ie_len); + } else { + sr->isr_ie_len = 0; } sr->isr_len = roundup(sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len, sizeof(uint32_t)); diff --git a/sys/dev/ipw/if_ipw.c b/sys/dev/ipw/if_ipw.c index 974bcc9c545e..2dca3cdf85bf 100644 --- a/sys/dev/ipw/if_ipw.c +++ b/sys/dev/ipw/if_ipw.c @@ -197,6 +197,7 @@ ipw_attach(device_t dev) struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp; struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c; uint16_t val; int error, i; @@ -287,11 +288,17 @@ ipw_attach(device_t dev) ic->ic_myaddr[4] = val >> 8; ic->ic_myaddr[5] = val & 0xff; - /* set supported .11b channels */ - for (i = 1; i < 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_B); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B; + /* set supported .11b channels (read from EEPROM) */ + if ((val = ipw_read_prom_word(sc, IPW_EEPROM_CHANNEL_LIST)) == 0) + val = 0x7ff; /* default to channels 1-11 */ + val <<= 1; + for (i = 1; i < 16; i++) { + if (val & (1 << i)) { + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + c->ic_flags = IEEE80211_CHAN_B; + c->ic_ieee = i; + } } /* check support for radio transmitter switch in EEPROM */ @@ -791,6 +798,7 @@ ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: + case IEEE80211_M_WDS: /* should not get there */ break; } @@ -802,7 +810,6 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; - struct ieee80211_node *ni; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint32_t len; @@ -813,6 +820,7 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) len = IEEE80211_ADDR_LEN; ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, macaddr, &len); +#if 0 ni = ieee80211_find_node(&ic->ic_scan, macaddr); if (ni == NULL) break; @@ -823,6 +831,7 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) if (ic->ic_opmode == IEEE80211_M_STA) ieee80211_notify_node_join(ic, ni, 1); +#endif break; case IEEE80211_S_INIT: @@ -980,7 +989,9 @@ ipw_fix_channel(struct ieee80211com *ic, struct mbuf *m) #if IEEE80211_CHAN_MAX < 255 if (frm[2] <= IEEE80211_CHAN_MAX) #endif - ic->ic_curchan = &ic->ic_channels[frm[2]]; + ic->ic_bsschan = ieee80211_find_channel(ic, + ieee80211_ieee2mhz(frm[2], 0), + IEEE80211_MODE_AUTO); frm += frm[1] + 2; } @@ -1069,7 +1080,7 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, status->rssi, 0); + ieee80211_input(ic, m, ni, status->rssi, -95/*XXX*/, 0); /* node is no longer needed */ ieee80211_free_node(ni); @@ -1162,6 +1173,8 @@ ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); + if (sbuf->m->m_flags & M_TXCB) + ieee80211_process_callback(sbuf->ni, sbuf->m, 0/*XXX*/); m_freem(sbuf->m); ieee80211_free_node(sbuf->ni); @@ -1514,7 +1527,6 @@ static void ipw_watchdog(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; mtx_lock(&sc->sc_mtx); @@ -1532,8 +1544,6 @@ ipw_watchdog(struct ifnet *ifp) ifp->if_timer = 1; } - ieee80211_watchdog(ic); - mtx_unlock(&sc->sc_mtx); } @@ -1744,6 +1754,7 @@ ipw_config(struct ipw_softc *sc) switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_HOSTAP: + case IEEE80211_M_WDS: /* XXX */ data = htole32(IPW_MODE_BSS); break; case IEEE80211_M_IBSS: @@ -1839,8 +1850,8 @@ ipw_config(struct ipw_softc *sc) printf("\n"); } #endif - error = ipw_cmd(sc, IPW_CMD_SET_ESSID, ic->ic_des_essid, - ic->ic_des_esslen); + error = ipw_cmd(sc, IPW_CMD_SET_ESSID, ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); if (error != 0) return error; diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c index c694c92e0f3b..ae9aeb6f35ba 100644 --- a/sys/dev/iwi/if_iwi.c +++ b/sys/dev/iwi/if_iwi.c @@ -72,6 +72,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -137,7 +138,7 @@ 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_setparams(struct iwi_softc *); static int iwi_wme_update(struct ieee80211com *); static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, @@ -151,7 +152,7 @@ static void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); static int iwi_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *, int); static void iwi_start(struct ifnet *); -static void iwi_watchdog(struct ifnet *); +static void iwi_watchdog(void *); 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 *); @@ -161,13 +162,22 @@ static void iwi_release_fw_dma(struct iwi_softc *sc); static int iwi_config(struct iwi_softc *); static int iwi_get_firmware(struct iwi_softc *); static void iwi_put_firmware(struct iwi_softc *); +static int iwi_scanchan(struct iwi_softc *, unsigned long, int); +static void iwi_scan_start(struct ieee80211com *); +static void iwi_scan_end(struct ieee80211com *); 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 void iwi_set_channel(struct ieee80211com *); +static void iwi_scan_curchan(struct ieee80211com *, unsigned long maxdwell); +#if 0 +static void iwi_scan_allchan(struct ieee80211com *, unsigned long maxdwell); +#endif +static void iwi_scan_mindwell(struct ieee80211com *); +static void iwi_assoc(struct ieee80211com *ic); +static void iwi_disassoc(struct ieee80211com *); +static void iwi_ops(void *, int); +static int iwi_queue_cmd(struct iwi_softc *, 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 *, int); static void iwi_stop(void *); @@ -247,35 +257,32 @@ iwi_attach(device_t dev) struct ifnet *ifp; struct ieee80211com *ic = &sc->sc_ic; uint16_t val; - int error, i; + int i, error, bands; sc->sc_dev = dev; - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF); + IWI_LOCK_INIT(sc); + IWI_CMD_LOCK_INIT(sc); sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); #if __FreeBSD_version >= 700000 - sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT, + sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); #else - sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT, + sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO, 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)); #endif 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); + TASK_INIT(&sc->sc_opstask, 0, iwi_ops, sc); + TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc); + callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " @@ -343,18 +350,17 @@ iwi_attach(device_t dev) device_printf(dev, "can not if_alloc()\n"); goto fail; } + ic->ic_ifp = ifp; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = iwi_init; ifp->if_ioctl = iwi_ioctl; ifp->if_start = iwi_start; - ifp->if_watchdog = iwi_watchdog; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); - ic->ic_ifp = ifp; ic->ic_wme.wme_update = iwi_wme_update; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ @@ -362,12 +368,14 @@ iwi_attach(device_t dev) /* set device capabilities */ ic->ic_caps = - IEEE80211_C_IBSS | /* IBSS mode supported */ - IEEE80211_C_MONITOR | /* monitor mode supported */ - IEEE80211_C_PMGT | /* power save supported */ - IEEE80211_C_SHPREAMBLE | /* short preamble supported */ - IEEE80211_C_WPA | /* 802.11i */ - IEEE80211_C_WME; /* 802.11e */ + IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_PMGT /* power save supported */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_WPA /* 802.11i */ + | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + ; /* read MAC address from EEPROM */ val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); @@ -379,29 +387,13 @@ iwi_attach(device_t dev) val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); ic->ic_myaddr[4] = val & 0xff; ic->ic_myaddr[5] = val >> 8; - - if (pci_get_device(dev) >= 0x4223) { - /* set supported .11a channels */ - for (i = 36; i <= 64; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 149; i <= 165; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - } - - /* set supported .11b and .11g channels (1 through 14) */ - for (i = 1; i <= 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); - ic->ic_channels[i].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - } + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (pci_get_device(dev) >= 0x4223) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); ieee80211_ifattach(ic); ic->ic_bmissthreshold = 10; /* override default */ @@ -409,6 +401,12 @@ iwi_attach(device_t dev) ic->ic_node_alloc = iwi_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = iwi_node_free; + ic->ic_scan_start = iwi_scan_start; + ic->ic_scan_end = iwi_scan_end; + ic->ic_set_channel = iwi_set_channel; + ic->ic_scan_curchan = iwi_scan_curchan; + ic->ic_scan_mindwell = iwi_scan_mindwell; + /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = iwi_newstate; @@ -454,12 +452,17 @@ iwi_detach(device_t dev) struct iwi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; + IWI_LOCK_DECL; if (ifp != NULL) { + IWI_LOCK(sc); iwi_stop(sc); + IWI_UNLOCK(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); } + + callout_drain(&sc->sc_wdtimer); iwi_put_firmware(sc); iwi_release_fw_dma(sc); @@ -486,7 +489,8 @@ iwi_detach(device_t dev) if (sc->sc_unr != NULL) delete_unrhdr(sc->sc_unr); - mtx_destroy(&sc->sc_mtx); + IWI_LOCK_DESTROY(sc); + IWI_CMD_LOCK_DESTROY(sc); return 0; } @@ -793,8 +797,11 @@ static int iwi_shutdown(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); + IWI_LOCK_DECL; + IWI_LOCK(sc); iwi_stop(sc); + IWI_UNLOCK(sc); iwi_put_firmware(sc); /* ??? XXX */ return 0; @@ -804,8 +811,11 @@ static int iwi_suspend(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); + IWI_LOCK_DECL; + IWI_LOCK(sc); iwi_stop(sc); + IWI_UNLOCK(sc); return 0; } @@ -935,38 +945,18 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; + int error = 0; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, ieee80211_state_name[ic->ic_state], ieee80211_state_name[nstate], sc->flags)); /* XXX state change race with taskqueue */ switch (nstate) { - case IEEE80211_S_SCAN: - 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. Note - * this only happens with auto roaming; otherwise - * just notify users and wait to be directed. - */ - /* notify directly as we bypass net80211 */ - ieee80211_sta_leave(ic, ic->ic_bss); - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) - taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask); - break; - } - 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: - iwi_auth_and_assoc(sc); + iwi_assoc(ic); break; - case IEEE80211_S_RUN: if (ic->ic_opmode == IEEE80211_M_IBSS) { /* @@ -978,17 +968,9 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) * This is all totally bogus and needs to be redone. */ if (ic->ic_state == IEEE80211_S_SCAN) - iwi_auth_and_assoc(sc); - } else if (ic->ic_opmode == IEEE80211_M_MONITOR) - taskqueue_enqueue(sc->sc_tq, &sc->sc_scantask); - - /* XXX way wrong */ - return sc->sc_newstate(ic, nstate, - IEEE80211_FC0_SUBTYPE_ASSOC_RESP); - - case IEEE80211_S_ASSOC: + iwi_assoc(ic); + } break; - case IEEE80211_S_INIT: /* * NB: don't try to do this if iwi_stop_master has @@ -996,12 +978,24 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) */ if (ic->ic_state == IEEE80211_S_RUN && (sc->flags & IWI_FLAG_FW_INITED)) - taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask); + iwi_disassoc(ic); + if (ic->ic_state == IEEE80211_S_SCAN && + (sc->fw_state == IWI_FW_SCANNING)) + ieee80211_cancel_scan(ic); + break; + case IEEE80211_S_ASSOC: + /* + * If we are not transitioning from AUTH the resend the + * association request. + */ + if (ic->ic_state != IEEE80211_S_AUTH) + iwi_assoc(ic); + break; + default: break; } + return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg); - ic->ic_state = nstate; - return 0; } /* @@ -1052,7 +1046,7 @@ iwi_wme_init(struct iwi_softc *sc) } static int -iwi_wme_setparams_locked(struct iwi_softc *sc) +iwi_wme_setparams(struct iwi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; const struct wmeParams *wmep; @@ -1071,17 +1065,6 @@ iwi_wme_setparams_locked(struct iwi_softc *sc) DPRINTF(("Setting WME parameters\n")); 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_DECL; - - IWI_LOCK(sc); - (void) iwi_wme_setparams_locked(sc); - IWI_UNLOCK(sc); -} #undef IWI_USEC #undef IWI_EXP2 @@ -1098,9 +1081,7 @@ iwi_wme_update(struct ieee80211com *ic) * will get sent down to the adapter as part of the * work iwi_auth_and_assoc does. */ - if (ic->ic_state == IEEE80211_S_RUN) - taskqueue_enqueue(sc->sc_tq, &sc->sc_setwmetask); - return 0; + return (iwi_queue_cmd(sc, IWI_SET_WME)); } static int @@ -1183,8 +1164,6 @@ iwi_setcurchan(struct iwi_softc *sc, int chan) { struct ieee80211com *ic = &sc->sc_ic; - IWI_LOCK_CHECK(sc); - ic->ic_curchan = &ic->ic_channels[chan]; sc->curchan = chan; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = @@ -1288,7 +1267,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); /* send the frame to the 802.11 layer */ - type = ieee80211_input(ic, m, ni, frame->rssi_dbm, 0); + type = ieee80211_input(ic, m, ni, frame->rssi_dbm, 0, 0); /* node is no longer needed */ ieee80211_free_node(ni); @@ -1347,6 +1326,7 @@ 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; + uint16_t capinfo, status, associd; /* NB: +8 for capinfo, status, associd, and first ie */ if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || @@ -1363,7 +1343,13 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len) */ frm = (const uint8_t *)&wh[1]; efrm = ((const uint8_t *) wh) + len; - frm += 6; + + capinfo = le16toh(*(const uint16_t *)frm); + frm += 2; + status = le16toh(*(const uint16_t *)frm); + frm += 2; + associd = le16toh(*(const uint16_t *)frm); + frm += 2; wme = NULL; while (frm < efrm) { @@ -1378,6 +1364,8 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len) } ni = sc->sc_ic.ic_bss; + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; if (wme != NULL) ni->ni_flags |= IEEE80211_NODE_QOS; else @@ -1400,7 +1388,10 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) chan = (struct iwi_notif_scan_channel *)(notif + 1); DPRINTFN(3, ("Scan of channel %u complete (%u)\n", - ic->ic_channels[chan->nchan].ic_freq, chan->nchan)); + ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); + + /* Reset the timer, the scan is still going */ + sc->sc_state_timer = 3; break; case IWI_NOTIF_TYPE_SCAN_COMPLETE: @@ -1409,21 +1400,11 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, scan->status)); - sc->sc_scan_timer = 0; + IWI_STATE_END(sc, IWI_FW_SCANNING); + + if (scan->status == IWI_SCAN_COMPLETED) + ieee80211_scan_next(ic); - if (ic->ic_opmode == IEEE80211_M_MONITOR) { - /* - * 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. - */ - taskqueue_enqueue(sc->sc_tq, &sc->sc_scantask); - } else { - sc->flags &= ~IWI_FLAG_SCANNING; - taskqueue_enqueue(sc->sc_tq, &sc->sc_scandonetask); - } break; case IWI_NOTIF_TYPE_AUTHENTICATION: @@ -1439,6 +1420,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) case IWI_AUTH_FAIL: DPRINTFN(2, ("Authentication failed\n")); sc->flags &= ~IWI_FLAG_ASSOCIATED; + IWI_STATE_END(sc, IWI_FW_ASSOCIATING); /* XXX */ break; @@ -1450,6 +1432,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) case IWI_AUTH_SEQ1_FAIL: DPRINTFN(2, ("Initial authentication handshake failed; " "you probably need shared key\n")); + IWI_STATE_END(sc, IWI_FW_ASSOCIATING); /* XXX retry shared key when in auto */ break; @@ -1470,16 +1453,29 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) case IWI_ASSOC_SUCCESS: DPRINTFN(2, ("Association succeeded\n")); sc->flags |= IWI_FLAG_ASSOCIATED; + IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 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_ASSOC_FAIL: - DPRINTFN(2, ("Association failed\n")); + case IWI_ASSOC_INIT: + switch (sc->fw_state) { + case IWI_FW_ASSOCIATING: + DPRINTFN(2, ("Association failed\n")); + IWI_STATE_END(sc, IWI_FW_ASSOCIATING); + ieee80211_new_state(ic, + IEEE80211_S_SCAN, -1); + break; + + case IWI_FW_DISASSOCIATING: + DPRINTFN(2, ("Dissassociated\n")); + IWI_STATE_END(sc, + IWI_FW_DISASSOCIATING); + break; + } sc->flags &= ~IWI_FLAG_ASSOCIATED; - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; default: @@ -1496,15 +1492,6 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) 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 @@ -1592,6 +1579,8 @@ iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(txq->data_dmat, data->map); + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, 0/*XXX*/); m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); @@ -1633,7 +1622,13 @@ iwi_intr(void *arg) if (r & IWI_INTR_FATAL_ERROR) { device_printf(sc->sc_dev, "firmware error\n"); - taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask); + /* don't restart if the interface isn't up */ + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask); + + sc->flags &= ~IWI_FLAG_BUSY; + sc->sc_busy_timer = 0; + wakeup(sc); } if (r & IWI_INTR_FW_INITED) { @@ -1646,6 +1641,7 @@ iwi_intr(void *arg) if (r & IWI_INTR_CMD_DONE) { sc->flags &= ~IWI_FLAG_BUSY; + sc->sc_busy_timer = 0; wakeup(sc); } @@ -1677,7 +1673,7 @@ iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) { struct iwi_cmd_desc *desc; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); if (sc->flags & IWI_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", @@ -1685,6 +1681,7 @@ iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) return EAGAIN; } sc->flags |= IWI_FLAG_BUSY; + sc->sc_busy_timer = 2; desc = &sc->cmdq.desc[sc->cmdq.cur]; @@ -1741,7 +1738,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, int error, nsegs, hdrlen, i; int ismcast, flags, xflags, staid; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); wh = mtod(m0, const struct ieee80211_frame *); /* NB: only data frames use this path */ hdrlen = ieee80211_hdrsize(wh); @@ -1972,20 +1969,18 @@ iwi_start(struct ifnet *ifp) } sc->sc_tx_timer = 5; - ifp->if_timer = 1; } IWI_UNLOCK(sc); } static void -iwi_watchdog(struct ifnet *ifp) +iwi_watchdog(void *arg) { - struct iwi_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - IWI_LOCK_DECL; + struct iwi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; - IWI_LOCK(sc); + IWI_LOCK_ASSERT(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { @@ -2007,22 +2002,25 @@ iwi_watchdog(struct ifnet *ifp) 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); - } + if (sc->sc_state_timer > 0) { + if (--sc->sc_state_timer == 0) { + if_printf(ifp, "firmware stuck in state %d, resetting\n", + sc->fw_state); + taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask); + if (sc->fw_state == IWI_FW_SCANNING) + ieee80211_cancel_scan(&sc->sc_ic); + sc->sc_state_timer = 3; + } + } + if (sc->sc_busy_timer > 0) { + if (--sc->sc_busy_timer == 0) { + if_printf(ifp, "firmware command timeout, resetting\n"); + taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask); } } - 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); - IWI_UNLOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); } static int @@ -2084,8 +2082,6 @@ iwi_stop_master(struct iwi_softc *sc) uint32_t tmp; int ntries; - IWI_LOCK_CHECK(sc); - /* disable interrupts */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); @@ -2352,7 +2348,7 @@ iwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) size_t size = fw->size; int i, ntries, error; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); error = 0; CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | IWI_RST_STOP_MASTER); @@ -2425,7 +2421,7 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; int ntries, error; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); /* copy firmware image to DMA memory */ memcpy(sc->fw_virtaddr, fw->data, fw->size); @@ -2567,7 +2563,7 @@ iwi_config(struct iwi_softc *sc) struct iwi_txpower power; uint32_t data; int error, i; - IWI_LOCK_CHECK(sc); + IWI_LOCK_ASSERT(sc); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":")); @@ -2645,17 +2641,17 @@ iwi_config(struct iwi_softc *sc) return error; /* if we have a desired ESSID, set it now */ - if (ic->ic_des_esslen != 0) { + if (ic->ic_des_ssid[0].len != 0) { #ifdef IWI_DEBUG if (iwi_debug > 0) { printf("Setting desired ESSID to "); - ieee80211_print_essid(ic->ic_des_essid, - ic->ic_des_esslen); + ieee80211_print_essid(ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); printf("\n"); } #endif - error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid, - ic->ic_des_esslen); + error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); if (error != 0) return error; } @@ -2686,94 +2682,142 @@ set_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) } static int -iwi_scan(struct iwi_softc *sc) +scan_type(const struct ieee80211_scan_state *ss, + const struct ieee80211_channel *chan) { - struct ieee80211com *ic = &sc->sc_ic; - const struct ieee80211_channel *c; - struct iwi_scan_ext scan; - int i, ix, start, scan_type, error; + /* We can only set one essid for a directed scan */ + if (ss->ss_nssid != 0) + return IWI_SCAN_TYPE_BDIRECTED; + if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) + return IWI_SCAN_TYPE_BROADCAST; + return IWI_SCAN_TYPE_PASSIVE; +} - IWI_LOCK_CHECK(sc); +static __inline int +scan_band(const struct ieee80211_channel *c) +{ + return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; +} - memset(&scan, 0, sizeof scan); +/* + * Start a scan on the current channel or all channels. + */ +static int +iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode) +{ + struct ieee80211com *ic; + struct ieee80211_channel *chan; + struct ieee80211_scan_state *ss; + struct iwi_scan_ext scan; + int error = 0; - /* 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); + IWI_LOCK_ASSERT(sc); + if (sc->fw_state == IWI_FW_SCANNING) { + /* + * This should not happen as we only trigger scan_next after + * completion + */ + DPRINTF(("%s: called too early - still scanning\n", __func__)); + return (EBUSY); + } + IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); - scan.full_scan_index = htole32(ic->ic_scan.nt_scangen); + ic = &sc->sc_ic; + ss = ic->ic_scan; - if (ic->ic_des_esslen != 0) { - scan_type = IWI_SCAN_TYPE_BDIRECTED; -#ifdef IWI_DEBUG - if (iwi_debug > 0) { - printf("Setting desired ESSID to "); - ieee80211_print_essid(ic->ic_des_essid, - ic->ic_des_esslen); - printf("\n"); - } -#endif - error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid, - ic->ic_des_esslen); - if (error != 0) - return error; - } else - scan_type = IWI_SCAN_TYPE_BROADCAST; + memset(&scan, 0, sizeof scan); + scan.full_scan_index = htole32(++sc->sc_scangen); + scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); + if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { + /* + * Use very short dwell times for when we send probe request + * frames. Without this bg scans hang. Ideally this should + * be handled with early-termination as done by net80211 but + * that's not feasible (aborting a scan is problematic). + */ + scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); + scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); + } else { + scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); + scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); + } - ix = 0; - if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) { - start = ix; - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - c = &ic->ic_channels[i]; - /* - * NB: ieee80211_next_scan clears curchan from the - * channel list so we must explicitly check; this - * will be fixed when the new scanning support arrives. - */ - if (!IEEE80211_IS_CHAN_5GHZ(c) || - !(isset(ic->ic_chan_scan,i) || c == ic->ic_curchan)) - 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++; - } + /* We can only set one essid for a directed scan */ + if (ss->ss_nssid != 0) { + error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, + ss->ss_ssid[0].len); + if (error) + return (error); } - if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) { - start = ix; - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - c = &ic->ic_channels[i]; - /* NB: see above */ - if (!IEEE80211_IS_CHAN_2GHZ(c) || - !(isset(ic->ic_chan_scan,i) || c == ic->ic_curchan)) - 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 (mode == IWI_SCAN_ALLCHAN) { + int i, next, band, b, bstart; + /* + * Convert scan list to run-length encoded channel list + * the firmware requires (preserving the order setup by + * net80211). The first entry in each run specifies the + * band and the count of items in the run. + */ + next = 0; /* next open slot */ + bstart = 0; /* NB: not needed, silence compiler */ + band = -1; /* NB: impossible value */ + KASSERT(ss->ss_last > 0, ("no channels")); + for (i = 0; i < ss->ss_last; i++) { + chan = ss->ss_chans[i]; + b = scan_band(chan); + if (b != band) { + if (band != -1) + scan.channels[bstart] = + (next - bstart) | band; + /* NB: this allocates a slot for the run-len */ + band = b, bstart = next++; + } + if (next >= IWI_SCAN_CHANNELS) { + DPRINTF(("truncating scan list\n")); + break; + } + scan.channels[next] = ieee80211_chan2ieee(ic, chan); + set_scan_type(&scan, next, scan_type(ss, chan)); + next++; } - if (start != ix) - scan.channels[start] = IWI_CHAN_2GHZ | (ix - start); + scan.channels[bstart] = (next - bstart) | band; + } else { + /* Scan the current channel only */ + chan = ic->ic_curchan; + scan.channels[0] = 1 | scan_band(chan); + scan.channels[1] = ieee80211_chan2ieee(ic, chan); + set_scan_type(&scan, 1, scan_type(ss, chan)); } +#ifdef IWI_DEBUG + if (iwi_debug > 0) { + static const char *scantype[8] = + { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; + int i; + printf("Scan request: index %u dwell %d/%d/%d\n" + , le32toh(scan.full_scan_index) + , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) + , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) + , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) + ); + i = 0; + do { + int run = scan.channels[i]; + if (run == 0) + break; + printf("Scan %d %s channels:", run & 0x3f, + run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); + for (run &= 0x3f, i++; run > 0; run--, i++) { + uint8_t type = scan.scan_type[i/2]; + printf(" %u/%s", scan.channels[i], + scantype[(i & 1 ? type : type>>4) & 7]); + } + printf("\n"); + } while (i < IWI_SCAN_CHANNELS); + } +#endif - DPRINTF(("Start scanning\n")); - /* - * 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); + return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); } static void @@ -2783,94 +2827,13 @@ iwi_scanabort(void *arg, int npending) IWI_LOCK_DECL; IWI_LOCK(sc); + sc->flags &= ~IWI_FLAG_CHANNEL_SCAN; /* NB: make sure we're still scanning */ - if (sc->flags & IWI_FLAG_SCANNING) + if (sc->fw_state == IWI_FW_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_DECL; - - 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_DECL; - - 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_DECL; - - 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) { @@ -2894,9 +2857,17 @@ iwi_auth_and_assoc(struct iwi_softc *sc) struct iwi_rateset rs; uint16_t capinfo; int error; - - IWI_LOCK_CHECK(sc); - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { + + IWI_LOCK_ASSERT(sc); + + if (sc->flags & IWI_FLAG_ASSOCIATED) { + DPRINTF(("Already associated\n")); + return (-1); + } + + IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); + error = 0; + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { memset(&config, 0, sizeof config); config.bluetooth_coexistence = sc->bluetooth; config.antenna = sc->antenna; @@ -2909,7 +2880,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc) DPRINTF(("Configuring adapter\n")); error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); if (error != 0) - return error; + goto done; } #ifdef IWI_DEBUG @@ -2921,11 +2892,16 @@ iwi_auth_and_assoc(struct iwi_softc *sc) #endif error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); if (error != 0) - return error; + goto done; /* the rate set has already been "negotiated" */ - rs.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A : - IWI_MODE_11G; + if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) + rs.mode = IWI_MODE_11A; + else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) + rs.mode = IWI_MODE_11G; + if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) + rs.mode = IWI_MODE_11B; + rs.type = IWI_RATESET_TYPE_NEGOTIATED; rs.nrates = ni->ni_rates.rs_nrates; if (rs.nrates > IWI_RATESET_SIZE) { @@ -2937,13 +2913,13 @@ iwi_auth_and_assoc(struct iwi_softc *sc) DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); if (error != 0) - return error; + goto done; memset(assoc, 0, sizeof *assoc); 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) + if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0) assoc->policy |= htole16(IWI_POLICY_WME); /* XXX complain on failure? */ } @@ -2953,21 +2929,21 @@ iwi_auth_and_assoc(struct iwi_softc *sc) error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie, ic->ic_opt_ie_len); if (error != 0) - return error; + goto done; } error = iwi_set_sensitivity(sc, ni->ni_rssi); if (error != 0) - return error; + goto done; - if (IEEE80211_IS_CHAN_A(ni->ni_chan)) + if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) assoc->mode = IWI_MODE_11A; - else if (IEEE80211_IS_CHAN_G(ni->ni_chan)) + else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) assoc->mode = IWI_MODE_11G; - else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) + else if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) assoc->mode = IWI_MODE_11B; - /* XXX else error */ - assoc->chan = ieee80211_chan2ieee(ic, ni->ni_chan); + + assoc->chan = ic->ic_curchan->ic_ieee; /* * NB: do not arrange for shared key auth w/o privacy * (i.e. a wep key); it causes a firmware error. @@ -2986,7 +2962,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc) error = iwi_setwepkeys(sc); if (error != 0) - return error; + goto done; } if (ic->ic_flags & IEEE80211_F_WPA) assoc->policy |= htole16(IWI_POLICY_WPA); @@ -3003,7 +2979,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc) if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && - IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; @@ -3024,7 +3000,12 @@ iwi_auth_and_assoc(struct iwi_softc *sc) 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); + error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); +done: + if (error) + IWI_STATE_END(sc, IWI_FW_ASSOCIATING); + + return (error); } static int @@ -3032,6 +3013,13 @@ iwi_disassociate(struct iwi_softc *sc, int quiet) { struct iwi_associate *assoc = &sc->assoc; + if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { + DPRINTF(("Not associated\n")); + return (-1); + } + + IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); + if (quiet) assoc->type = IWI_HC_DISASSOC_QUIET; else @@ -3043,17 +3031,6 @@ iwi_disassociate(struct iwi_softc *sc, int quiet) } static void -iwi_down(void *arg, int npending) -{ - struct iwi_softc *sc = arg; - IWI_LOCK_DECL; - - IWI_LOCK(sc); - iwi_disassociate(sc, 0); - IWI_UNLOCK(sc); -} - -static void iwi_init(void *priv) { struct iwi_softc *sc = priv; @@ -3134,21 +3111,20 @@ iwi_init_locked(void *priv, int force) int i; IWI_LOCK_DECL; - IWI_LOCK_CHECK(sc); - if (sc->flags & IWI_FLAG_FW_LOADING) { + IWI_LOCK_ASSERT(sc); + if (sc->fw_state == IWI_FW_LOADING) { device_printf(sc->sc_dev, "%s: already loading\n", __func__); return; /* XXX: condvar? */ } iwi_stop(sc); + IWI_STATE_BEGIN(sc, IWI_FW_LOADING); if (iwi_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset adapter\n"); goto fail; } - sc->flags |= IWI_FLAG_FW_LOADING; - IWI_UNLOCK(sc); if (iwi_get_firmware(sc)) { IWI_LOCK(sc); @@ -3233,14 +3209,15 @@ iwi_init_locked(void *priv, int force) } else ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; - sc->flags &= ~IWI_FLAG_FW_LOADING; + IWI_STATE_END(sc, IWI_FW_LOADING); return; fail: ifp->if_flags &= ~IFF_UP; - sc->flags &= ~IWI_FLAG_FW_LOADING; + IWI_STATE_END(sc, IWI_FW_LOADING); iwi_stop(sc); iwi_put_firmware(sc); } @@ -3252,12 +3229,13 @@ iwi_stop(void *priv) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; - IWI_LOCK_CHECK(sc); /* XXX: pretty sure this triggers */ + IWI_LOCK_ASSERT(sc); if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); sc->sc_blinking = 0; } + callout_stop(&sc->sc_wdtimer); iwi_stop_master(sc); CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); @@ -3270,13 +3248,15 @@ iwi_stop(void *priv) iwi_reset_tx_ring(sc, &sc->txq[3]); iwi_reset_rx_ring(sc, &sc->rxq); - 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); + sc->sc_state_timer = 0; + sc->sc_busy_timer = 0; + sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); + sc->fw_state = IWI_FW_IDLE; + wakeup(sc); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); } @@ -3315,11 +3295,13 @@ static void iwi_radio_off(void *arg, int pending) { struct iwi_softc *sc = arg; + IWI_LOCK_DECL; device_printf(sc->sc_dev, "radio turned off\n"); + IWI_LOCK(sc); iwi_stop(sc); sc->sc_rfkill_timer = 2; - sc->sc_ifp->if_timer = 1; + IWI_UNLOCK(sc); } static int @@ -3365,11 +3347,6 @@ iwi_sysctlattach(struct iwi_softc *sc) 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"); @@ -3568,3 +3545,154 @@ iwi_ledattach(struct iwi_softc *sc) sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; } } + +static void +iwi_ops(void *arg, int npending) +{ + struct iwi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + IWI_LOCK_DECL; + int cmd; + +again: + IWI_CMD_LOCK(sc); + cmd = sc->sc_cmd[sc->sc_cmd_cur]; + if (cmd == 0) { + /* No more commands to process */ + IWI_CMD_UNLOCK(sc); + return; + } + sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */ + sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWI_CMD_MAXOPS; + IWI_CMD_UNLOCK(sc); + + IWI_LOCK(sc); + while (sc->fw_state != IWI_FW_IDLE || (sc->flags & IWI_FLAG_BUSY)) { + msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz/10); + } + + if (!(sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) + goto done; + + switch (cmd) { + case IWI_ASSOC: + iwi_auth_and_assoc(sc); + break; + case IWI_DISASSOC: + iwi_disassociate(sc, 0); + break; + case IWI_SET_WME: + if (ic->ic_state == IEEE80211_S_RUN) + (void) iwi_wme_setparams(sc); + break; + case IWI_SCAN_START: + sc->flags |= IWI_FLAG_CHANNEL_SCAN; + break; + case IWI_SCAN_CURCHAN: + case IWI_SCAN_ALLCHAN: + if (!(sc->flags & IWI_FLAG_CHANNEL_SCAN)) { + DPRINTF(("%s: ic_scan_curchan while not scanning\n", + __func__)); + goto done; + } + if (iwi_scanchan(sc, sc->sc_maxdwell, cmd)) + ieee80211_cancel_scan(ic); + + break; + } +done: + IWI_UNLOCK(sc); + + /* Take another pass */ + goto again; +} + +static int +iwi_queue_cmd(struct iwi_softc *sc, int cmd) +{ + IWI_CMD_LOCK(sc); + if (sc->sc_cmd[sc->sc_cmd_next] != 0) { + IWI_CMD_UNLOCK(sc); + DPRINTF(("%s: command %d dropped\n", __func__, cmd)); + return (EBUSY); + } + + sc->sc_cmd[sc->sc_cmd_next] = cmd; + sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWI_CMD_MAXOPS; + taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask); + IWI_CMD_UNLOCK(sc); + return (0); +} + +static void +iwi_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + iwi_queue_cmd(sc, IWI_SCAN_START); +} + +static void +iwi_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + if (sc->fw_state == IWI_FW_IDLE) + iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); +} + +static void +iwi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + sc->sc_maxdwell = maxdwell; + iwi_queue_cmd(sc, IWI_SCAN_CURCHAN); +} + +#if 0 +static void +iwi_scan_allchan(struct ieee80211com *ic, unsigned long maxdwell) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + sc->sc_maxdwell = maxdwell; + iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN); +} +#endif + +static void +iwi_scan_mindwell(struct ieee80211com *ic) +{ + /* NB: don't try to abort scan; wait for firmware to finish */ +} + +static void +iwi_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + taskqueue_enqueue(sc->sc_tq, &sc->sc_scanaborttask); +} + +static void +iwi_assoc(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + iwi_queue_cmd(sc, IWI_ASSOC); +} + +static void +iwi_disassoc(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + + iwi_queue_cmd(sc, IWI_DISASSOC); +} diff --git a/sys/dev/iwi/if_iwireg.h b/sys/dev/iwi/if_iwireg.h index 5c7435edcee1..fb56fa59bbff 100644 --- a/sys/dev/iwi/if_iwireg.h +++ b/sys/dev/iwi/if_iwireg.h @@ -213,7 +213,7 @@ struct iwi_notif_authentication { /* structure for notification IWI_NOTIF_TYPE_ASSOCIATION */ struct iwi_notif_association { uint8_t state; -#define IWI_ASSOC_FAIL 0 +#define IWI_ASSOC_INIT 0 #define IWI_ASSOC_SUCCESS 12 uint8_t pad[11]; } __packed; @@ -417,6 +417,10 @@ struct iwi_scan { #define IWI_SCAN_TYPE_BDIRECTED 4 /* active, directed+bcast probe */ #define IWI_SCAN_TYPES 5 +/* scan result codes */ +#define IWI_SCAN_COMPLETED 1 /* scan compeleted sucessfully */ +#define IWI_SCAN_ABORTED 2 /* scan was aborted by the driver */ + /* structure for command IWI_CMD_SCAN_EXT */ struct iwi_scan_ext { uint32_t full_scan_index; diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h index f652c4d8381a..a1c39150e90b 100644 --- a/sys/dev/iwi/if_iwivar.h +++ b/sys/dev/iwi/if_iwivar.h @@ -123,6 +123,8 @@ struct iwi_softc { device_t sc_dev; struct mtx sc_mtx; + struct mtx sc_cmdlock; + char sc_cmdname[12]; /* e.g. "iwi0_cmd" */ uint8_t sc_mcast[IEEE80211_ADDR_LEN]; struct unrhdr *sc_unr; struct taskqueue *sc_tq; /* private task queue */ @@ -132,11 +134,15 @@ struct iwi_softc { uint32_t flags; #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) /* currently associated */ - +#define IWI_FLAG_CHANNEL_SCAN (1 << 5) + uint32_t fw_state; +#define IWI_FW_IDLE 0 +#define IWI_FW_LOADING 1 +#define IWI_FW_ASSOCIATING 2 +#define IWI_FW_DISASSOCIATING 3 +#define IWI_FW_SCANNING 4 struct iwi_cmd_ring cmdq; struct iwi_tx_ring txq[WME_NUM_AC]; struct iwi_rx_ring rxq; @@ -176,20 +182,16 @@ struct iwi_softc { int curchan; /* current h/w channel # */ int antenna; - int dwelltime; int bluetooth; struct iwi_associate assoc; struct iwi_wme_params wme[3]; + u_int sc_scangen; 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_scanaborttask; /* cancel active scan */ struct task sc_restarttask; /* restart adapter processing */ + struct task sc_opstask; /* scan / auth processing */ unsigned int sc_softled : 1, /* enable LED gpio status */ sc_ledstate: 1, /* LED on/off state */ @@ -204,11 +206,26 @@ struct iwi_softc { u_int8_t sc_txrix; u_int16_t sc_ledoff; /* off time for current blink */ struct callout sc_ledtimer; /* led off timer */ + struct callout sc_wdtimer; /* watchdog timer */ int sc_tx_timer; int sc_rfkill_timer;/* poll for rfkill change */ - int sc_scan_timer; /* scan request timeout */ + int sc_state_timer; /* firmware state timer */ + int sc_busy_timer; /* firmware cmd timer */ +#define IWI_SCAN_START (1 << 0) +#define IWI_SET_CHANNEL (1 << 1) +#define IWI_SCAN_END (1 << 2) +#define IWI_ASSOC (1 << 3) +#define IWI_DISASSOC (1 << 4) +#define IWI_SCAN_CURCHAN (1 << 5) +#define IWI_SCAN_ALLCHAN (1 << 6) +#define IWI_SET_WME (1 << 7) +#define IWI_CMD_MAXOPS 10 + int sc_cmd[IWI_CMD_MAXOPS]; + int sc_cmd_cur; /* current queued scan task */ + int sc_cmd_next; /* last queued scan task */ + unsigned long sc_maxdwell; /* max dwell time for curchan */ struct bpf_if *sc_drvbpf; union { @@ -226,15 +243,34 @@ struct iwi_softc { int sc_txtap_len; }; +#define IWI_STATE_BEGIN(_sc, _state) do { \ + KASSERT(_sc->fw_state == IWI_FW_IDLE, \ + ("iwi firmware not idle")); \ + _sc->fw_state = _state; \ + _sc->sc_state_timer = 5; \ + DPRINTF(("enter FW state %d\n", _state)); \ +} while (0) + +#define IWI_STATE_END(_sc, _state) do { \ + if (_sc->fw_state == _state) \ + DPRINTF(("exit FW state %d\n", _state)); \ + else \ + DPRINTF(("expected FW state %d, got %d\n", \ + _state, _sc->fw_state)); \ + _sc->fw_state = IWI_FW_IDLE; \ + wakeup(_sc); \ + _sc->sc_state_timer = 0; \ +} while (0) /* * NB.: This models the only instance of async locking in iwi_init_locked * and must be kept in sync. */ +#define IWI_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \ + MTX_NETWORK_LOCK, MTX_DEF) +#define IWI_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define IWI_LOCK_DECL int __waslocked = 0 -#define IWI_LOCK_CHECK(sc) do { \ - if (!mtx_owned(&(sc)->sc_mtx)) \ - DPRINTF(("%s iwi_lock not held\n", __func__)); \ -} while (0) +#define IWI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) #define IWI_LOCK(sc) do { \ if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \ mtx_lock(&(sc)->sc_mtx); \ @@ -243,3 +279,11 @@ struct iwi_softc { if (!__waslocked) \ mtx_unlock(&(sc)->sc_mtx); \ } while (0) +#define IWI_CMD_LOCK_INIT(sc) do { \ + snprintf((sc)->sc_cmdname, sizeof((sc)->sc_cmdname), "%s_cmd", \ + device_get_nameunit((sc)->sc_dev)); \ + mtx_init(&(sc)->sc_cmdlock, (sc)->sc_cmdname, NULL, MTX_DEF); \ +} while (0) +#define IWI_CMD_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_cmdlock) +#define IWI_CMD_LOCK(sc) mtx_lock(&(sc)->sc_cmdlock) +#define IWI_CMD_UNLOCK(sc) mtx_unlock(&(sc)->sc_cmdlock) diff --git a/sys/dev/ral/if_ral_pci.c b/sys/dev/ral/if_ral_pci.c index 3d9976d64743..2ceafe4a97ae 100644 --- a/sys/dev/ral/if_ral_pci.c +++ b/sys/dev/ral/if_ral_pci.c @@ -87,8 +87,8 @@ static struct ral_opns { } ral_rt2560_opns = { rt2560_attach, rt2560_detach, - rt2560_shutdown, - rt2560_suspend, + rt2560_stop, + rt2560_stop, rt2560_resume, rt2560_intr @@ -192,7 +192,8 @@ ral_pci_attach(device_t dev) sc->sc_st = rman_get_bustag(psc->mem); sc->sc_sh = rman_get_bushandle(psc->mem); - + sc->sc_invalid = 1; + psc->irq_rid = 0; psc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &psc->irq_rid, RF_ACTIVE | RF_SHAREABLE); @@ -214,7 +215,8 @@ ral_pci_attach(device_t dev) device_printf(dev, "could not set up interrupt\n"); return error; } - + sc->sc_invalid = 0; + return 0; } @@ -222,7 +224,11 @@ static int ral_pci_detach(device_t dev) { struct ral_pci_softc *psc = device_get_softc(dev); - + struct rt2560_softc *sc = &psc->u.sc_rt2560; + + /* check if device was removed */ + sc->sc_invalid = !bus_child_present(dev); + (*psc->sc_opns->detach)(psc); bus_generic_detach(dev); diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c index 36a849d94f27..f09c4f35d199 100644 --- a/sys/dev/ral/rt2560.c +++ b/sys/dev/ral/rt2560.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -64,6 +65,10 @@ __FBSDID("$FreeBSD$"); #include <dev/ral/rt2560reg.h> #include <dev/ral/rt2560var.h> +#define RT2560_RSSI(sc, rssi) \ + ((rssi) > (RT2560_NOISE_FLOOR + (sc)->rssi_corr) ? \ + ((rssi) - RT2560_NOISE_FLOOR - (sc)->rssi_corr) : 0) + #ifdef RAL_DEBUG #define DPRINTF(x) do { if (ral_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0) @@ -90,7 +95,6 @@ static void rt2560_free_rx_ring(struct rt2560_softc *, static struct ieee80211_node *rt2560_node_alloc( struct ieee80211_node_table *); static int rt2560_media_change(struct ifnet *); -static void rt2560_next_scan(void *); static void rt2560_iter_func(void *, struct ieee80211_node *); static void rt2560_update_rssadapt(void *); static int rt2560_newstate(struct ieee80211com *, @@ -105,6 +109,9 @@ static void rt2560_beacon_expire(struct rt2560_softc *); static void rt2560_wakeup_expire(struct rt2560_softc *); static uint8_t rt2560_rxrate(struct rt2560_rx_desc *); static int rt2560_ack_rate(struct ieee80211com *, int); +static void rt2560_scan_start(struct ieee80211com *); +static void rt2560_scan_end(struct ieee80211com *); +static void rt2560_set_channel(struct ieee80211com *); static uint16_t rt2560_txtime(int, int, uint32_t); static uint8_t rt2560_plcp_signal(int); static void rt2560_setup_tx_desc(struct rt2560_softc *, @@ -137,7 +144,7 @@ static void rt2560_update_plcp(struct rt2560_softc *); static void rt2560_update_slot(struct ifnet *); static void rt2560_set_basicrates(struct rt2560_softc *); static void rt2560_update_led(struct rt2560_softc *, int, int); -static void rt2560_set_bssid(struct rt2560_softc *, uint8_t *); +static void rt2560_set_bssid(struct rt2560_softc *, const uint8_t *); static void rt2560_set_macaddr(struct rt2560_softc *, uint8_t *); static void rt2560_get_macaddr(struct rt2560_softc *, uint8_t *); static void rt2560_update_promisc(struct rt2560_softc *); @@ -147,7 +154,6 @@ static int rt2560_bbp_init(struct rt2560_softc *); static void rt2560_set_txantenna(struct rt2560_softc *, int); static void rt2560_set_rxantenna(struct rt2560_softc *, int); static void rt2560_init(void *); -static void rt2560_stop(void *); static int rt2560_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); @@ -187,7 +193,7 @@ rt2560_attach(device_t dev, int id) struct rt2560_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp; - int error, i; + int error, bands; sc->sc_dev = dev; @@ -195,7 +201,6 @@ rt2560_attach(device_t dev, int id) MTX_DEF | MTX_RECURSE); callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); - callout_init(&sc->scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE); /* retrieve RT2560 rev. no */ @@ -272,37 +277,20 @@ rt2560_attach(device_t dev, int id) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_BGSCAN | /* bg scanning support */ IEEE80211_C_WPA; /* 802.11i */ - if (sc->rf_rev == RT2560_RF_5222) { - /* set supported .11a channels */ - for (i = 36; i <= 64; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 100; i <= 140; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 149; i <= 161; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - } - - /* set supported .11b and .11g channels (1 through 14) */ - for (i = 1; i <= 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); - ic->ic_channels[i].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - } + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RT2560_RF_5222) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); ieee80211_ifattach(ic); + ic->ic_scan_start = rt2560_scan_start; + ic->ic_scan_end = rt2560_scan_end; + ic->ic_set_channel = rt2560_set_channel; ic->ic_node_alloc = rt2560_node_alloc; ic->ic_updateslot = rt2560_update_slot; ic->ic_reset = rt2560_reset; @@ -365,10 +353,9 @@ rt2560_detach(void *xsc) struct rt2560_softc *sc = xsc; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; - + rt2560_stop(sc); callout_stop(&sc->watchdog_ch); - callout_stop(&sc->scan_ch); callout_stop(&sc->rssadapt_ch); bpfdetach(ifp); @@ -388,22 +375,6 @@ rt2560_detach(void *xsc) } void -rt2560_shutdown(void *xsc) -{ - struct rt2560_softc *sc = xsc; - - rt2560_stop(sc); -} - -void -rt2560_suspend(void *xsc) -{ - struct rt2560_softc *sc = xsc; - - rt2560_stop(sc); -} - -void rt2560_resume(void *xsc) { struct rt2560_softc *sc = xsc; @@ -733,28 +704,13 @@ rt2560_media_change(struct ifnet *ifp) int error; error = ieee80211_media_change(ifp); - if (error != ENETRESET) - return error; - - if ((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING)) - rt2560_init(sc); - - return 0; -} - -/* - * This function is called periodically (every 200ms) during scanning to - * switch from one channel to another. - */ -static void -rt2560_next_scan(void *arg) -{ - struct rt2560_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_next_scan(ic); + if (error == ENETRESET) { + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING)) + rt2560_init(sc); + } + return error; } /* @@ -796,7 +752,6 @@ rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) int error = 0; ostate = ic->ic_state; - callout_stop(&sc->scan_ch); switch (nstate) { case IEEE80211_S_INIT: @@ -810,24 +765,7 @@ rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) rt2560_update_led(sc, 0, 0); } break; - - case IEEE80211_S_SCAN: - rt2560_set_chan(sc, ic->ic_curchan); - callout_reset(&sc->scan_ch, (sc->dwelltime * hz) / 1000, - rt2560_next_scan, sc); - break; - - case IEEE80211_S_AUTH: - rt2560_set_chan(sc, ic->ic_curchan); - break; - - case IEEE80211_S_ASSOC: - rt2560_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_RUN: - rt2560_set_chan(sc, ic->ic_curchan); - ni = ic->ic_bss; if (ic->ic_opmode != IEEE80211_M_MONITOR) { @@ -862,6 +800,10 @@ rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) rt2560_enable_tsf_sync(sc); } break; + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + break; } return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg); @@ -1060,6 +1002,9 @@ rt2560_prio_intr(struct rt2560_softc *sc) struct ifnet *ifp = ic->ic_ifp; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; + struct ieee80211_node *ni; + struct mbuf *m; + int flags; bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, BUS_DMASYNC_POSTREAD); @@ -1068,18 +1013,18 @@ rt2560_prio_intr(struct rt2560_softc *sc) desc = &sc->prioq.desc[sc->prioq.next]; data = &sc->prioq.data[sc->prioq.next]; - if ((le32toh(desc->flags) & RT2560_TX_BUSY) || - !(le32toh(desc->flags) & RT2560_TX_VALID)) + flags = le32toh(desc->flags); + if ((flags & RT2560_TX_BUSY) || (flags & RT2560_TX_VALID) == 0) break; - switch (le32toh(desc->flags) & RT2560_TX_RESULT_MASK) { + switch (flags & RT2560_TX_RESULT_MASK) { case RT2560_TX_SUCCESS: DPRINTFN(10, ("mgt frame sent successfully\n")); break; case RT2560_TX_SUCCESS_RETRY: DPRINTFN(9, ("mgt frame sent after %u retries\n", - (le32toh(desc->flags) >> 5) & 0x7)); + (flags >> 5) & 0x7)); break; case RT2560_TX_FAIL_RETRY: @@ -1091,15 +1036,17 @@ rt2560_prio_intr(struct rt2560_softc *sc) case RT2560_TX_FAIL_OTHER: default: device_printf(sc->sc_dev, "sending mgt frame failed " - "0x%08x\n", le32toh(desc->flags)); + "0x%08x\n", flags); + break; } bus_dmamap_sync(sc->prioq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->prioq.data_dmat, data->map); - m_freem(data->m); + + m = data->m; data->m = NULL; - ieee80211_free_node(data->ni); + ni = data->ni; data->ni = NULL; /* descriptor is no longer valid */ @@ -1109,6 +1056,13 @@ rt2560_prio_intr(struct rt2560_softc *sc) sc->prioq.queued--; sc->prioq.next = (sc->prioq.next + 1) % RT2560_PRIO_RING_COUNT; + + if (m->m_flags & M_TXCB) + ieee80211_process_callback(ni, m, + (flags & RT2560_TX_RESULT_MASK) &~ + (RT2560_TX_SUCCESS | RT2560_TX_SUCCESS_RETRY)); + m_freem(m); + ieee80211_free_node(ni); } bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, @@ -1227,25 +1181,31 @@ rt2560_decryption_intr(struct rt2560_softc *sc) tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->rx_ant; - tap->wr_antsignal = desc->rssi; + tap->wr_antsignal = RT2560_RSSI(sc, desc->rssi); bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } + sc->sc_flags |= RAL_INPUT_RUNNING; + RAL_UNLOCK(sc); wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, desc->rssi, 0); + ieee80211_input(ic, m, ni, RT2560_RSSI(sc, desc->rssi), + RT2560_NOISE_FLOOR, 0); /* give rssi to the rate adatation algorithm */ rn = (struct rt2560_node *)ni; - ral_rssadapt_input(ic, ni, &rn->rssadapt, desc->rssi); + ral_rssadapt_input(ic, ni, &rn->rssadapt, + RT2560_RSSI(sc, desc->rssi)); /* node is no longer needed */ ieee80211_free_node(ni); + RAL_LOCK(sc); + sc->sc_flags &= ~RAL_INPUT_RUNNING; skip: desc->flags = htole32(RT2560_RX_BUSY); DPRINTFN(15, ("decryption done idx=%u\n", sc->rxq.cur_decrypt)); @@ -1324,9 +1284,14 @@ rt2560_beacon_expire(struct rt2560_softc *sc) if (ic->ic_opmode != IEEE80211_M_IBSS && ic->ic_opmode != IEEE80211_M_HOSTAP) - return; + return; data = &sc->bcnq.data[sc->bcnq.next]; + /* + * Don't send beacon if bsschan isn't set + */ + if (data->ni == NULL) + return; bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->bcnq.data_dmat, data->map); @@ -1808,7 +1773,6 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct rt2560_node *rn; - struct ieee80211_rateset *rs; struct ieee80211_frame *wh; struct ieee80211_key *k; struct mbuf *mnew; @@ -1820,9 +1784,10 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { - rs = &ic->ic_sup_rates[ic->ic_curmode]; - rate = rs->rs_rates[ic->ic_fixed_rate]; + rate = ic->ic_fixed_rate; } else { + struct ieee80211_rateset *rs; + rs = &ni->ni_rates; rn = (struct rt2560_node *)ni; ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs, wh, @@ -2020,9 +1985,10 @@ rt2560_start(struct ifnet *ifp) if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); - if (rt2560_tx_mgt(sc, m0, ni) != 0) + if (rt2560_tx_mgt(sc, m0, ni) != 0) { + ieee80211_free_node(ni); break; - + } } else { if (ic->ic_state != IEEE80211_S_RUN) break; @@ -2045,6 +2011,28 @@ rt2560_start(struct ifnet *ifp) m_freem(m0); continue; } + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m0->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + */ + ieee80211_pwrsave(ni, m0); + /* + * If we're in power save mode 'cuz of a bg + * scan cancel it so the traffic can flow. + * The packet we just queued will automatically + * get sent when we drop out of power save. + * XXX locking + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_scan(ic); + ieee80211_free_node(ni); + continue; + + } + BPF_MTAP(ifp, m0); m0 = ieee80211_encap(ic, m0, ni); @@ -2052,7 +2040,7 @@ rt2560_start(struct ifnet *ifp) ieee80211_free_node(ni); continue; } - + if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); @@ -2074,7 +2062,6 @@ static void rt2560_watchdog(void *arg) { struct rt2560_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { @@ -2085,8 +2072,6 @@ rt2560_watchdog(void *arg) } callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc); } - - ieee80211_watchdog(ic); } /* @@ -2115,19 +2100,22 @@ rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ieee80211com *ic = &sc->sc_ic; int error = 0; - RAL_LOCK(sc); + switch (cmd) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { + RAL_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) rt2560_update_promisc(sc); else rt2560_init(sc); + RAL_UNLOCK(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rt2560_stop(sc); } + break; default: @@ -2142,7 +2130,6 @@ rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = 0; } - RAL_UNLOCK(sc); return error; } @@ -2295,6 +2282,8 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c) rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040); rt2560_rf_write(sc, RAL_RF4, rt2560_rf5222[i].r4); break; + default: + printf("unknown ral rev=%d\n", sc->rf_rev); } if (ic->ic_state != IEEE80211_S_SCAN) { @@ -2312,6 +2301,18 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c) } } +static void +rt2560_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2560_softc *sc = ifp->if_softc; + + RAL_LOCK(sc); + rt2560_set_chan(sc, ic->ic_curchan); + RAL_UNLOCK(sc); + +} + #if 0 /* * Disable RF auto-tuning. @@ -2456,7 +2457,7 @@ rt2560_update_led(struct rt2560_softc *sc, int led1, int led2) } static void -rt2560_set_bssid(struct rt2560_softc *sc, uint8_t *bssid) +rt2560_set_bssid(struct rt2560_softc *sc, const uint8_t *bssid) { uint32_t tmp; @@ -2559,6 +2560,37 @@ rt2560_read_eeprom(struct rt2560_softc *sc) sc->txpow[i * 2] = val >> 8; sc->txpow[i * 2 + 1] = val & 0xff; } + + val = rt2560_eeprom_read(sc, RT2560_EEPROM_CALIBRATE); + if ((val & 0xff) == 0xff) + sc->rssi_corr = RT2560_DEFAULT_RSSI_CORR; + else + sc->rssi_corr = val & 0xff; + DPRINTF(("rssi correction %d, calibrate 0x%02x\n", + sc->rssi_corr, val)); +} + + +static void +rt2560_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2560_softc *sc = ifp->if_softc; + + /* abort TSF synchronization */ + RAL_WRITE(sc, RT2560_CSR14, 0); + rt2560_set_bssid(sc, ifp->if_broadcastaddr); +} + +static void +rt2560_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2560_softc *sc = ifp->if_softc; + + rt2560_enable_tsf_sync(sc); + /* XXX keep local copy */ + rt2560_set_bssid(sc, ic->ic_bss->ni_bssid); } static int @@ -2653,10 +2685,11 @@ rt2560_init(void *priv) uint32_t tmp; int i; - RAL_LOCK(sc); + rt2560_stop(sc); + RAL_LOCK(sc); /* setup tx rings */ tmp = RT2560_PRIO_RING_COUNT << 24 | RT2560_ATIM_RING_COUNT << 16 | @@ -2739,36 +2772,44 @@ rt2560_init(void *priv) } void -rt2560_stop(void *priv) +rt2560_stop(void *arg) { - struct rt2560_softc *sc = priv; + struct rt2560_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; + volatile int *flags = &sc->sc_flags; - sc->sc_tx_timer = 0; - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); - - /* abort Tx */ - RAL_WRITE(sc, RT2560_TXCSR0, RT2560_ABORT_TX); - - /* disable Rx */ - RAL_WRITE(sc, RT2560_RXCSR0, RT2560_DISABLE_RX); - - /* reset ASIC (imply reset BBP) */ - RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC); - RAL_WRITE(sc, RT2560_CSR1, 0); - - /* disable interrupts */ - RAL_WRITE(sc, RT2560_CSR8, 0xffffffff); + while (*flags & RAL_INPUT_RUNNING) { + tsleep(sc, 0, "ralrunning", hz/10); + } - /* reset Tx and Rx rings */ - rt2560_reset_tx_ring(sc, &sc->txq); - rt2560_reset_tx_ring(sc, &sc->atimq); - rt2560_reset_tx_ring(sc, &sc->prioq); - rt2560_reset_tx_ring(sc, &sc->bcnq); - rt2560_reset_rx_ring(sc, &sc->rxq); + RAL_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + sc->sc_tx_timer = 0; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + /* abort Tx */ + RAL_WRITE(sc, RT2560_TXCSR0, RT2560_ABORT_TX); + + /* disable Rx */ + RAL_WRITE(sc, RT2560_RXCSR0, RT2560_DISABLE_RX); + + /* reset ASIC (imply reset BBP) */ + RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC); + RAL_WRITE(sc, RT2560_CSR1, 0); + + /* disable interrupts */ + RAL_WRITE(sc, RT2560_CSR8, 0xffffffff); + + /* reset Tx and Rx rings */ + rt2560_reset_tx_ring(sc, &sc->txq); + rt2560_reset_tx_ring(sc, &sc->atimq); + rt2560_reset_tx_ring(sc, &sc->prioq); + rt2560_reset_tx_ring(sc, &sc->bcnq); + rt2560_reset_rx_ring(sc, &sc->rxq); + } + RAL_UNLOCK(sc); } static int @@ -2828,3 +2869,4 @@ bad: RAL_UNLOCK(sc); return EIO; /* XXX */ } + diff --git a/sys/dev/ral/rt2560reg.h b/sys/dev/ral/rt2560reg.h index 6b1cf0d09bd3..3871ed3f28be 100644 --- a/sys/dev/ral/rt2560reg.h +++ b/sys/dev/ral/rt2560reg.h @@ -17,6 +17,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define RT2560_DEFAULT_RSSI_CORR 0x79 +#define RT2560_NOISE_FLOOR -95 + #define RT2560_TX_RING_COUNT 48 #define RT2560_ATIM_RING_COUNT 4 #define RT2560_PRIO_RING_COUNT 16 @@ -296,6 +299,7 @@ struct rt2560_rx_desc { #define RT2560_EEPROM_CONFIG0 16 #define RT2560_EEPROM_BBP_BASE 19 #define RT2560_EEPROM_TXPOWER 35 +#define RT2560_EEPROM_CALIBRATE 62 /* * control and status registers access macros diff --git a/sys/dev/ral/rt2560var.h b/sys/dev/ral/rt2560var.h index 50112b5f2c23..c01abb1093d2 100644 --- a/sys/dev/ral/rt2560var.h +++ b/sys/dev/ral/rt2560var.h @@ -109,14 +109,18 @@ struct rt2560_softc { struct mtx sc_mtx; struct callout watchdog_ch; - struct callout scan_ch; struct callout rssadapt_ch; int sc_tx_timer; - + int sc_invalid; +/* + * The same in both up to here + * ------------------------------------------------ + */ uint32_t asic_rev; uint32_t eeprom_rev; uint8_t rf_rev; + uint8_t rssi_corr; struct rt2560_tx_ring txq; struct rt2560_tx_ring prioq; @@ -157,12 +161,13 @@ struct rt2560_softc { } sc_txtapu; #define sc_txtap sc_txtapu.th int sc_txtap_len; +#define RAL_INPUT_RUNNING 1 + int sc_flags; }; int rt2560_attach(device_t, int); int rt2560_detach(void *); -void rt2560_shutdown(void *); -void rt2560_suspend(void *); +void rt2560_stop(void *); void rt2560_resume(void *); void rt2560_intr(void *); diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c index 8f13eb6eca1e..224da32c657a 100644 --- a/sys/dev/ral/rt2661.c +++ b/sys/dev/ral/rt2661.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -92,7 +93,6 @@ static void rt2661_free_rx_ring(struct rt2661_softc *, static struct ieee80211_node *rt2661_node_alloc( struct ieee80211_node_table *); static int rt2661_media_change(struct ifnet *); -static void rt2661_next_scan(void *); static int rt2661_newstate(struct ieee80211com *, enum ieee80211_state, int); static uint16_t rt2661_eeprom_read(struct rt2661_softc *, uint8_t); @@ -104,6 +104,9 @@ static void rt2661_mcu_beacon_expire(struct rt2661_softc *); static void rt2661_mcu_wakeup(struct rt2661_softc *); static void rt2661_mcu_cmd_intr(struct rt2661_softc *); static int rt2661_ack_rate(struct ieee80211com *, int); +static void rt2661_scan_start(struct ieee80211com *); +static void rt2661_scan_end(struct ieee80211com *); +static void rt2661_set_channel(struct ieee80211com *); static uint16_t rt2661_txtime(int, int, uint32_t); static uint8_t rt2661_rxrate(struct rt2661_rx_desc *); static uint8_t rt2661_plcp_signal(int); @@ -148,6 +151,7 @@ static void rt2661_read_eeprom(struct rt2661_softc *); static int rt2661_bbp_init(struct rt2661_softc *); static void rt2661_init(void *); static void rt2661_stop(void *); +static void rt2661_stop_locked(struct rt2661_softc *); static int rt2661_load_microcode(struct rt2661_softc *, const uint8_t *, int); #ifdef notyet @@ -190,7 +194,7 @@ rt2661_attach(device_t dev, int id) struct ifnet *ifp; uint32_t val; const uint8_t *ucode = NULL; - int error, i, ac, ntries, size = 0; + int bands, error, ac, ntries, size = 0; sc->sc_dev = dev; @@ -198,7 +202,6 @@ rt2661_attach(device_t dev, int id) MTX_DEF | MTX_RECURSE); callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); - callout_init(&sc->scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE); /* wait for NIC to initialize */ @@ -302,39 +305,22 @@ rt2661_attach(device_t dev, int id) #ifdef notyet IEEE80211_C_WME | /* 802.11e */ #endif + IEEE80211_C_BGSCAN | /* bg scanning support */ IEEE80211_C_WPA; /* 802.11i */ - if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325) { - /* set supported .11a channels */ - for (i = 36; i <= 64; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 100; i <= 140; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 149; i <= 165; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - } - - /* set supported .11b and .11g channels (1 through 14) */ - for (i = 1; i <= 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); - ic->ic_channels[i].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - } + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); ieee80211_ifattach(ic); ic->ic_node_alloc = rt2661_node_alloc; /* ic->ic_wme.wme_update = rt2661_wme_update;*/ + ic->ic_scan_start = rt2661_scan_start; + ic->ic_scan_end = rt2661_scan_end; + ic->ic_set_channel = rt2661_set_channel; ic->ic_updateslot = rt2661_update_slot; ic->ic_reset = rt2661_reset; /* enable s/w bmiss handling in sta mode */ @@ -356,6 +342,7 @@ rt2661_attach(device_t dev, int id) sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RT2661_TX_RADIOTAP_PRESENT); + /* * Add a few sysctl knobs. */ @@ -376,7 +363,6 @@ fail3: rt2661_free_tx_ring(sc, &sc->mgtq); fail2: while (--ac >= 0) rt2661_free_tx_ring(sc, &sc->txq[ac]); fail1: mtx_destroy(&sc->sc_mtx); - return error; } @@ -386,10 +372,9 @@ rt2661_detach(void *xsc) struct rt2661_softc *sc = xsc; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; - + rt2661_stop(sc); callout_stop(&sc->watchdog_ch); - callout_stop(&sc->scan_ch); callout_stop(&sc->rssadapt_ch); bpfdetach(ifp); @@ -759,20 +744,6 @@ rt2661_media_change(struct ifnet *ifp) } /* - * This function is called periodically (every 200ms) during scanning to - * switch from one channel to another. - */ -static void -rt2661_next_scan(void *arg) -{ - struct rt2661_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_next_scan(ic); -} - -/* * This function is called for each node present in the node station table. */ static void @@ -811,7 +782,6 @@ rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) int error = 0; ostate = ic->ic_state; - callout_stop(&sc->scan_ch); switch (nstate) { case IEEE80211_S_INIT: @@ -823,21 +793,7 @@ rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff); } break; - - case IEEE80211_S_SCAN: - rt2661_set_chan(sc, ic->ic_curchan); - callout_reset(&sc->scan_ch, (sc->dwelltime * hz) / 1000, - rt2661_next_scan, sc); - break; - - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - rt2661_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_RUN: - rt2661_set_chan(sc, ic->ic_curchan); - ni = ic->ic_bss; if (ic->ic_opmode != IEEE80211_M_MONITOR) { @@ -859,6 +815,10 @@ rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) rt2661_enable_tsf_sync(sc); } break; + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + break; } return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg); @@ -934,6 +894,9 @@ rt2661_tx_intr(struct rt2661_softc *sc) int qid, retrycnt; for (;;) { + struct ieee80211_node *ni; + struct mbuf *m; + val = RAL_READ(sc, RT2661_STA_CSR4); if (!(val & RT2661_TX_STAT_VALID)) break; @@ -944,12 +907,17 @@ rt2661_tx_intr(struct rt2661_softc *sc) /* retrieve rate control algorithm context */ data = &txq->data[txq->stat]; - rn = (struct rt2661_node *)data->ni; + m = data->m; + data->m = NULL; + ni = data->ni; + data->ni = NULL; /* if no frame has been sent, ignore */ - if (rn == NULL) + if (ni == NULL) continue; + rn = (struct rt2661_node *)ni; + switch (RT2661_TX_RESULT(val)) { case RT2661_TX_SUCCESS: retrycnt = RT2661_TX_RETRYCNT(val); @@ -967,7 +935,7 @@ rt2661_tx_intr(struct rt2661_softc *sc) DPRINTFN(9, ("sending data frame failed (too much " "retries)\n")); if (data->id.id_node != NULL) { - ral_rssadapt_lower_rate(ic, data->ni, + ral_rssadapt_lower_rate(ic, ni, &rn->rssadapt, &data->id); } ifp->if_oerrors++; @@ -980,14 +948,17 @@ rt2661_tx_intr(struct rt2661_softc *sc) ifp->if_oerrors++; } - ieee80211_free_node(data->ni); - data->ni = NULL; - DPRINTFN(15, ("tx done q=%d idx=%u\n", qid, txq->stat)); txq->queued--; if (++txq->stat >= txq->count) /* faster than % count */ txq->stat = 0; + + if (m->m_flags & M_TXCB) + ieee80211_process_callback(ni, m, + RT2661_TX_RESULT(val) != RT2661_TX_SUCCESS); + m_freem(m); + ieee80211_free_node(ni); } sc->sc_tx_timer = 0; @@ -1014,9 +985,6 @@ rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq) bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(txq->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - /* node reference is released in rt2661_tx_intr() */ /* descriptor is no longer valid */ desc->flags &= ~htole32(RT2661_TX_VALID); @@ -1048,6 +1016,8 @@ rt2661_rx_intr(struct rt2661_softc *sc) BUS_DMASYNC_POSTREAD); for (;;) { + int rssi; + desc = &sc->rxq.desc[sc->rxq.cur]; data = &sc->rxq.data[sc->rxq.cur]; @@ -1120,6 +1090,8 @@ rt2661_rx_intr(struct rt2661_softc *sc) m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; + rssi = rt2661_get_rssi(sc, desc->rssi); + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2661_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_lo, tsf_hi; @@ -1134,22 +1106,28 @@ rt2661_rx_intr(struct rt2661_softc *sc) tap->wr_rate = rt2661_rxrate(desc); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); - tap->wr_antsignal = desc->rssi; + tap->wr_antsignal = rssi < 0 ? 0 : rssi; bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } - + sc->sc_flags |= RAL_INPUT_RUNNING; + RAL_UNLOCK(sc); wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + /* Error happened during RSSI conversion. */ + if (rssi < 0) + rssi = ni->ni_rssi; + /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, desc->rssi, 0); + ieee80211_input(ic, m, ni, rssi, RT2661_NOISE_FLOOR, 0); /* give rssi to the rate adatation algorithm */ rn = (struct rt2661_node *)ni; - ral_rssadapt_input(ic, ni, &rn->rssadapt, - rt2661_get_rssi(sc, desc->rssi)); + RAL_LOCK(sc); + sc->sc_flags &= ~RAL_INPUT_RUNNING; + ral_rssadapt_input(ic, ni, &rn->rssadapt, rssi); /* node is no longer needed */ ieee80211_free_node(ni); @@ -1556,7 +1534,6 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0, struct rt2661_tx_desc *desc; struct rt2661_tx_data *data; struct rt2661_node *rn; - struct ieee80211_rateset *rs; struct ieee80211_frame *wh; struct ieee80211_key *k; const struct chanAccParams *cap; @@ -1569,9 +1546,10 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { - rs = &ic->ic_sup_rates[ic->ic_curmode]; - rate = rs->rs_rates[ic->ic_fixed_rate]; + rate = ic->ic_fixed_rate; } else { + struct ieee80211_rateset *rs; + rs = &ni->ni_rates; rn = (struct rt2661_node *)ni; ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs, @@ -1753,7 +1731,7 @@ rt2661_start(struct ifnet *ifp) RAL_LOCK(sc); /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->sc_invalid) { RAL_UNLOCK(sc); return; } @@ -1773,9 +1751,10 @@ rt2661_start(struct ifnet *ifp) if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); - if (rt2661_tx_mgt(sc, m0, ni) != 0) + if (rt2661_tx_mgt(sc, m0, ni) != 0) { + ieee80211_free_node(ni); break; - + } } else { if (ic->ic_state != IEEE80211_S_RUN) break; @@ -1812,6 +1791,7 @@ rt2661_start(struct ifnet *ifp) /* there is no place left in this ring */ IFQ_DRV_PREPEND(&ifp->if_snd, m0); ifp->if_drv_flags |= IFF_DRV_OACTIVE; + ieee80211_free_node(ni); break; } @@ -1845,9 +1825,8 @@ static void rt2661_watchdog(void *arg) { struct rt2661_softc *sc = (struct rt2661_softc *)arg; - struct ieee80211com *ic = &sc->sc_ic; - if (sc->sc_tx_timer > 0) { + if (sc->sc_tx_timer > 0 && !sc->sc_invalid) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); rt2661_init(sc); @@ -1856,8 +1835,6 @@ rt2661_watchdog(void *arg) } callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc); } - - ieee80211_watchdog(ic); } /* @@ -1886,8 +1863,6 @@ rt2661_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ieee80211com *ic = &sc->sc_ic; int error = 0; - RAL_LOCK(sc); - switch (cmd) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { @@ -1913,8 +1888,6 @@ rt2661_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = 0; } - RAL_UNLOCK(sc); - return error; } @@ -2066,6 +2039,12 @@ rt2661_set_txpreamble(struct rt2661_softc *sc) RAL_WRITE(sc, RT2661_TXRX_CSR4, tmp); } +/* + * Supported rates for 802.11g. XXX should use ic_sup_rates. + */ +static const struct ieee80211_rateset rt2661_rateset_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + static void rt2661_set_basicrates(struct rt2661_softc *sc, const struct ieee80211_rateset *rs) @@ -2373,10 +2352,18 @@ rt2661_read_eeprom(struct rt2661_softc *sc) if ((val & 0xff) != 0xff) sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + /* Only [-10, 10] is valid */ + if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) + sc->rssi_2ghz_corr = 0; + val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_5GHZ_OFFSET); if ((val & 0xff) != 0xff) sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + /* Only [-10, 10] is valid */ + if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) + sc->rssi_5ghz_corr = 0; + /* adjust RSSI correction for external low-noise amplifier */ if (sc->ext_2ghz_lna) sc->rssi_2ghz_corr -= 14; @@ -2465,7 +2452,7 @@ rt2661_init(void *priv) RAL_LOCK(sc); - rt2661_stop(sc); + rt2661_stop_locked(sc); /* initialize Tx rings */ RAL_WRITE(sc, RT2661_AC1_BASE_CSR, sc->txq[1].physaddr); @@ -2525,13 +2512,13 @@ rt2661_init(void *priv) } if (ntries == 1000) { printf("timeout waiting for BBP/RF to wakeup\n"); - rt2661_stop(sc); + rt2661_stop_locked(sc); RAL_UNLOCK(sc); return; } if (rt2661_bbp_init(sc) != 0) { - rt2661_stop(sc); + rt2661_stop_locked(sc); RAL_UNLOCK(sc); return; } @@ -2572,6 +2559,7 @@ rt2661_init(void *priv) /* kick Rx */ RAL_WRITE(sc, RT2661_RX_CNTL_CSR, 1); + RAL_UNLOCK(sc); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; @@ -2582,7 +2570,7 @@ rt2661_init(void *priv) } else ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - RAL_UNLOCK(sc); + #undef N } @@ -2590,41 +2578,57 @@ void rt2661_stop(void *priv) { struct rt2661_softc *sc = priv; + + RAL_LOCK(sc); + rt2661_stop_locked(sc); + RAL_UNLOCK(sc); +} + +void +rt2661_stop_locked(struct rt2661_softc *sc) +{ struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; uint32_t tmp; - - sc->sc_tx_timer = 0; - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); - - /* abort Tx (for all 5 Tx rings) */ - RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16); - - /* disable Rx (value remains after reset!) */ - tmp = RAL_READ(sc, RT2661_TXRX_CSR0); - RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX); - - /* reset ASIC */ - RAL_WRITE(sc, RT2661_MAC_CSR1, 3); - RAL_WRITE(sc, RT2661_MAC_CSR1, 0); - - /* disable interrupts */ - RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffffff); - RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff); - - /* clear any pending interrupt */ - RAL_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff); - RAL_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, 0xffffffff); - - /* reset Tx and Rx rings */ - rt2661_reset_tx_ring(sc, &sc->txq[0]); - rt2661_reset_tx_ring(sc, &sc->txq[1]); - rt2661_reset_tx_ring(sc, &sc->txq[2]); - rt2661_reset_tx_ring(sc, &sc->txq[3]); - rt2661_reset_tx_ring(sc, &sc->mgtq); - rt2661_reset_rx_ring(sc, &sc->rxq); + volatile int *flags = &sc->sc_flags; + + while (*flags & RAL_INPUT_RUNNING) { + msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10); + } + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + sc->sc_tx_timer = 0; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + /* abort Tx (for all 5 Tx rings) */ + RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16); + + /* disable Rx (value remains after reset!) */ + tmp = RAL_READ(sc, RT2661_TXRX_CSR0); + RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX); + + /* reset ASIC */ + RAL_WRITE(sc, RT2661_MAC_CSR1, 3); + RAL_WRITE(sc, RT2661_MAC_CSR1, 0); + + /* disable interrupts */ + RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffffff); + RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff); + + /* clear any pending interrupt */ + RAL_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff); + RAL_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, 0xffffffff); + + /* reset Tx and Rx rings */ + rt2661_reset_tx_ring(sc, &sc->txq[0]); + rt2661_reset_tx_ring(sc, &sc->txq[1]); + rt2661_reset_tx_ring(sc, &sc->txq[2]); + rt2661_reset_tx_ring(sc, &sc->txq[3]); + rt2661_reset_tx_ring(sc, &sc->mgtq); + rt2661_reset_rx_ring(sc, &sc->rxq); + } } static int @@ -2858,7 +2862,17 @@ rt2661_get_rssi(struct rt2661_softc *sc, uint8_t raw) lna = (raw >> 5) & 0x3; agc = raw & 0x1f; - rssi = 2 * agc; + if (lna == 0) { + /* + * No mapping available. + * + * NB: Since RSSI is relative to noise floor, -1 is + * adequate for caller to know error happened. + */ + return -1; + } + + rssi = (2 * agc) - RT2661_NOISE_FLOOR; if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) { rssi += sc->rssi_2ghz_corr; @@ -2881,3 +2895,39 @@ rt2661_get_rssi(struct rt2661_softc *sc, uint8_t raw) } return rssi; } + +static void +rt2661_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2661_softc *sc = ifp->if_softc; + uint32_t tmp; + + /* abort TSF synchronization */ + tmp = RAL_READ(sc, RT2661_TXRX_CSR9); + RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0xffffff); + rt2661_set_bssid(sc, ifp->if_broadcastaddr); +} + +static void +rt2661_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2661_softc *sc = ifp->if_softc; + + rt2661_enable_tsf_sync(sc); + /* XXX keep local copy */ + rt2661_set_bssid(sc, ic->ic_bss->ni_bssid); +} + +static void +rt2661_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2661_softc *sc = ifp->if_softc; + + RAL_LOCK(sc); + rt2661_set_chan(sc, ic->ic_curchan); + RAL_UNLOCK(sc); + +} diff --git a/sys/dev/ral/rt2661reg.h b/sys/dev/ral/rt2661reg.h index d79f9264260e..b4325b053d33 100644 --- a/sys/dev/ral/rt2661reg.h +++ b/sys/dev/ral/rt2661reg.h @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define RT2661_NOISE_FLOOR -95 + #define RT2661_TX_RING_COUNT 32 #define RT2661_MGT_RING_COUNT 32 #define RT2661_RX_RING_COUNT 64 diff --git a/sys/dev/ral/rt2661var.h b/sys/dev/ral/rt2661var.h index fab28924c6c1..9f12a15fca61 100644 --- a/sys/dev/ral/rt2661var.h +++ b/sys/dev/ral/rt2661var.h @@ -102,11 +102,15 @@ struct rt2661_softc { struct mtx sc_mtx; struct callout watchdog_ch; - struct callout scan_ch; struct callout rssadapt_ch; int sc_tx_timer; - + int sc_invalid; +/* + * The same in both up to here + * ------------------------------------------------ + */ + struct ieee80211_channel *sc_curchan; uint8_t rf_rev; @@ -159,6 +163,8 @@ struct rt2661_softc { } sc_txtapu; #define sc_txtap sc_txtapu.th int sc_txtap_len; +#define RAL_INPUT_RUNNING 1 + int sc_flags; }; int rt2661_attach(device_t, int); diff --git a/sys/dev/usb/if_rum.c b/sys/dev/usb/if_rum.c index 2f22117d4e76..16ed1adbcced 100644 --- a/sys/dev/usb/if_rum.c +++ b/sys/dev/usb/if_rum.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_amrr.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> @@ -128,8 +129,8 @@ static void rum_free_tx_list(struct rum_softc *); static int rum_alloc_rx_list(struct rum_softc *); static void rum_free_rx_list(struct rum_softc *); static int rum_media_change(struct ifnet *); -static void rum_next_scan(void *); static void rum_task(void *); +static void rum_scantask(void *); static int rum_newstate(struct ieee80211com *, enum ieee80211_state, int); static void rum_txeof(usbd_xfer_handle, usbd_private_handle, @@ -187,11 +188,15 @@ static int rum_load_microcode(struct rum_softc *, const u_char *, static int rum_prepare_beacon(struct rum_softc *); static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); +static void rum_scan_start(struct ieee80211com *); +static void rum_scan_end(struct ieee80211com *); +static void rum_set_channel(struct ieee80211com *); +static int rum_get_rssi(struct rum_softc *, uint8_t); static void rum_amrr_start(struct rum_softc *, struct ieee80211_node *); static void rum_amrr_timeout(void *); static void rum_amrr_update(usbd_xfer_handle, usbd_private_handle, - usbd_status status); + usbd_status); static const struct { uint32_t reg; @@ -374,7 +379,7 @@ USB_ATTACH(rum) usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usbd_status error; - int i, ntries, size; + int i, ntries, size, bands; uint32_t tmp; sc->sc_udev = uaa->device; @@ -426,9 +431,8 @@ USB_ATTACH(rum) MTX_DEF | MTX_RECURSE); usb_init_task(&sc->sc_task, rum_task, sc); - callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); - callout_init(&sc->scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); - + usb_init_task(&sc->sc_scantask, rum_scantask, sc); + callout_init(&sc->watchdog_ch, 0); callout_init(&sc->amrr_ch, 0); /* retrieve RT2573 rev. no */ @@ -490,42 +494,49 @@ USB_ATTACH(rum) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_BGSCAN | /* bg scanning supported */ IEEE80211_C_WPA; /* 802.11i */ + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) { + struct ieee80211_channel *c; + /* set supported .11a channels */ for (i = 34; i <= 46; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; } for (i = 36; i <= 64; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; } for (i = 100; i <= 140; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; } for (i = 149; i <= 165; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; } } - /* set supported .11b and .11g channels (1 through 14) */ - for (i = 1; i <= 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); - ic->ic_channels[i].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - } - ieee80211_ifattach(ic); + ic->ic_scan_start = rum_scan_start; + ic->ic_scan_end = rum_scan_end; + ic->ic_set_channel = rum_set_channel; + /* enable s/w bmiss handling in sta mode */ ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; @@ -535,7 +546,9 @@ USB_ATTACH(rum) ic->ic_raw_xmit = rum_raw_xmit; ieee80211_media_init(ic, rum_media_change, ieee80211_media_status); - ieee80211_amrr_init(&sc->amrr, ic, 1, 10); + ieee80211_amrr_init(&sc->amrr, ic, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD); bpfattach2(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN, @@ -563,8 +576,8 @@ USB_DETACH(rum) rum_stop(sc); usb_rem_task(sc->sc_udev, &sc->sc_task); + usb_rem_task(sc->sc_udev, &sc->sc_scantask); callout_stop(&sc->watchdog_ch); - callout_stop(&sc->scan_ch); callout_stop(&sc->amrr_ch); if (sc->amrr_xfer != NULL) { @@ -737,20 +750,6 @@ rum_media_change(struct ifnet *ifp) return 0; } -/* - * This function is called periodically (every 200ms) during scanning to - * switch from one channel to another. - */ -static void -rum_next_scan(void *arg) -{ - struct rum_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_next_scan(ic); -} - static void rum_task(void *arg) { @@ -773,22 +772,7 @@ rum_task(void *arg) } break; - case IEEE80211_S_SCAN: - rum_set_chan(sc, ic->ic_curchan); - callout_reset(&sc->scan_ch, hz / 5, rum_next_scan, sc); - break; - - case IEEE80211_S_AUTH: - rum_set_chan(sc, ic->ic_curchan); - break; - - case IEEE80211_S_ASSOC: - rum_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_RUN: - rum_set_chan(sc, ic->ic_curchan); - ni = ic->ic_bss; if (ic->ic_opmode != IEEE80211_M_MONITOR) { @@ -811,6 +795,8 @@ rum_task(void *arg) ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) rum_amrr_start(sc, ni); break; + default: + break; } RUM_UNLOCK(sc); @@ -823,7 +809,6 @@ rum_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct rum_softc *sc = ic->ic_ifp->if_softc; - callout_stop(&sc->scan_ch); callout_stop(&sc->amrr_ch); /* do it in a process context */ @@ -851,6 +836,10 @@ rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) struct rum_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ic.ic_ifp; + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, + status == USBD_NORMAL_COMPLETION ? 0 : ETIMEDOUT); + if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; @@ -891,7 +880,7 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *mnew, *m; - int len; + int len, rssi; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) @@ -938,6 +927,15 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) m->m_data = (caddr_t)(desc + 1); m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; + rssi = rum_get_rssi(sc, desc->rssi); + + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + + /* Error happened during RSSI conversion. */ + if (rssi < 0) + rssi = ni->ni_rssi; + if (bpf_peers_present(sc->sc_drvbpf)) { struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; @@ -946,16 +944,13 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->rx_ant; - tap->wr_antsignal = desc->rssi; + tap->wr_antsignal = rssi; 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); - /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, desc->rssi, 0); + ieee80211_input(ic, m, ni, rssi, RT2573_NOISE_FLOOR, 0); /* node is no longer needed */ ieee80211_free_node(ni); @@ -1293,7 +1288,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) - rate = ic->ic_bss->ni_rates.rs_rates[ic->ic_fixed_rate]; + rate = ic->ic_fixed_rate; else rate = ni->ni_rates.rs_rates[ni->ni_txrate]; @@ -1378,8 +1373,6 @@ rum_start(struct ifnet *ifp) struct mbuf *m0; struct ether_header *eh; - RUM_LOCK(sc); - for (;;) { IF_POLL(&ic->ic_mgtq, m0); if (m0 != NULL) { @@ -1442,27 +1435,27 @@ rum_start(struct ifnet *ifp) sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc); } - - RUM_UNLOCK(sc); } static void rum_watchdog(void *arg) { - struct rum_softc *sc = (struct rum_softc *)arg; - struct ieee80211com *ic = &sc->sc_ic; + struct rum_softc *sc = arg; + + RUM_LOCK(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /*rum_init(ifp); XXX needs a process context! */ sc->sc_ifp->if_oerrors++; + RUM_UNLOCK(sc); return; } callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc); } - ieee80211_watchdog(ic); + RUM_UNLOCK(sc); } static int @@ -1984,11 +1977,24 @@ rum_read_eeprom(struct rum_softc *sc) if ((val & 0xff) != 0xff) sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + /* Only [-10, 10] is valid */ + if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) + sc->rssi_2ghz_corr = 0; + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + /* Only [-10, 10] is valid */ + if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) + sc->rssi_5ghz_corr = 0; + + if (sc->ext_2ghz_lna) + sc->rssi_2ghz_corr -= 14; + if (sc->ext_5ghz_lna) + sc->rssi_5ghz_corr -= 14; + DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", sc->rssi_2ghz_corr, sc->rssi_5ghz_corr)); @@ -2407,4 +2413,124 @@ rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv, callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc); } +static void +rum_scan_start(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = RUM_SCAN_START; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); +} + +static void +rum_scan_end(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = RUM_SCAN_END; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); +} + +static void +rum_set_channel(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = RUM_SET_CHANNEL; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); +} + +static void +rum_scantask(void *arg) +{ + struct rum_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; + uint32_t tmp; + + RUM_LOCK(sc); + + switch (sc->sc_scan_action) { + case RUM_SCAN_START: + /* abort TSF synchronization */ + tmp = rum_read(sc, RT2573_TXRX_CSR9); + rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); + rum_set_bssid(sc, ifp->if_broadcastaddr); + break; + + case RUM_SCAN_END: + rum_enable_tsf_sync(sc); + /* XXX keep local copy */ + rum_set_bssid(sc, ic->ic_bss->ni_bssid); + break; + + case RUM_SET_CHANNEL: + mtx_lock(&Giant); + rum_set_chan(sc, ic->ic_curchan); + mtx_unlock(&Giant); + break; + + default: + panic("unknown scan action %d\n", sc->sc_scan_action); + /* NEVER REACHED */ + break; + } + + RUM_UNLOCK(sc); +} + +static int +rum_get_rssi(struct rum_softc *sc, uint8_t raw) +{ + int lna, agc, rssi; + + lna = (raw >> 5) & 0x3; + agc = raw & 0x1f; + + if (lna == 0) { + /* + * No RSSI mapping + * + * NB: Since RSSI is relative to noise floor, -1 is + * adequate for caller to know error happened. + */ + return -1; + } + + rssi = (2 * agc) - RT2573_NOISE_FLOOR; + + if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)) { + rssi += sc->rssi_2ghz_corr; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 74; + else if (lna == 3) + rssi -= 90; + } else { + rssi += sc->rssi_5ghz_corr; + + if (!sc->ext_5ghz_lna && lna != 1) + rssi += 4; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 86; + else if (lna == 3) + rssi -= 100; + } + return rssi; +} + DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, usbd_driver_load, 0); diff --git a/sys/dev/usb/if_rumreg.h b/sys/dev/usb/if_rumreg.h index 78277759eb63..75a51bcd4ad4 100644 --- a/sys/dev/usb/if_rumreg.h +++ b/sys/dev/usb/if_rumreg.h @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define RT2573_NOISE_FLOOR -95 + #define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc)) #define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc)) diff --git a/sys/dev/usb/if_rumvar.h b/sys/dev/usb/if_rumvar.h index 1577c6c7e9ba..e7d6d15cd6c3 100644 --- a/sys/dev/usb/if_rumvar.h +++ b/sys/dev/usb/if_rumvar.h @@ -97,6 +97,12 @@ struct rum_softc { struct ieee80211_amrr amrr; struct ieee80211_amrr_node amn; + struct usb_task sc_scantask; + int sc_scan_action; +#define RUM_SCAN_START 0 +#define RUM_SCAN_END 1 +#define RUM_SET_CHANNEL 2 + struct rum_rx_data rx_data[RUM_RX_LIST_COUNT]; struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; int tx_queued; @@ -106,7 +112,6 @@ struct rum_softc { struct mtx sc_mtx; struct callout watchdog_ch; - struct callout scan_ch; struct callout amrr_ch; int sc_tx_timer; diff --git a/sys/dev/usb/if_ural.c b/sys/dev/usb/if_ural.c index 46e2f5863542..4ca311b49044 100644 --- a/sys/dev/usb/if_ural.c +++ b/sys/dev/usb/if_ural.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_amrr.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> @@ -73,6 +74,10 @@ SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &uraldebug, 0, #define DPRINTFN(n, x) #endif +#define URAL_RSSI(rssi) \ + ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ + ((rssi) - RAL_NOISE_FLOOR + RAL_RSSI_CORR) : 0) + /* various supported device vendors/products */ static const struct usb_devno ural_devs[] = { { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G }, @@ -113,8 +118,8 @@ static void ural_free_tx_list(struct ural_softc *); static int ural_alloc_rx_list(struct ural_softc *); static void ural_free_rx_list(struct ural_softc *); static int ural_media_change(struct ifnet *); -static void ural_next_scan(void *); static void ural_task(void *); +static void ural_scantask(void *); static int ural_newstate(struct ieee80211com *, enum ieee80211_state, int); static int ural_rxrate(struct ural_rx_desc *); @@ -149,6 +154,9 @@ static void ural_write_multi(struct ural_softc *, uint16_t, void *, static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); +static void ural_scan_start(struct ieee80211com *); +static void ural_scan_end(struct ieee80211com *); +static void ural_set_channel(struct ieee80211com *); static void ural_set_chan(struct ural_softc *, struct ieee80211_channel *); static void ural_disable_rf_tune(struct ural_softc *); @@ -156,7 +164,7 @@ static void ural_enable_tsf_sync(struct ural_softc *); static void ural_update_slot(struct ifnet *); static void ural_set_txpreamble(struct ural_softc *); static void ural_set_basicrates(struct ural_softc *); -static void ural_set_bssid(struct ural_softc *, uint8_t *); +static void ural_set_bssid(struct ural_softc *, const uint8_t *); static void ural_set_macaddr(struct ural_softc *, uint8_t *); static void ural_update_promisc(struct ural_softc *); static const char *ural_get_rf(int); @@ -357,7 +365,7 @@ USB_ATTACH(ural) usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usbd_status error; - int i; + int i, bands; sc->sc_udev = uaa->device; sc->sc_dev = self; @@ -399,7 +407,8 @@ USB_ATTACH(ural) sc->sc_tx_no = ed->bEndpointAddress; } if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) { - printf("%s: missing endpoint\n", device_get_nameunit(sc->sc_dev)); + printf("%s: missing endpoint\n", + device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } @@ -407,8 +416,8 @@ USB_ATTACH(ural) MTX_DEF | MTX_RECURSE); usb_init_task(&sc->sc_task, ural_task, sc); - callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); - callout_init(&sc->scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); + usb_init_task(&sc->sc_scantask, ural_scantask, sc); + callout_init(&sc->watchdog_ch, 0); callout_init(&sc->amrr_ch, 0); /* retrieve RT2570 rev. no */ @@ -418,11 +427,13 @@ USB_ATTACH(ural) ural_read_eeprom(sc); printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", - device_get_nameunit(sc->sc_dev), sc->asic_rev, ural_get_rf(sc->rf_rev)); + device_get_nameunit(sc->sc_dev), sc->asic_rev, + ural_get_rf(sc->rf_rev)); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - printf("%s: can not if_alloc()\n", device_get_nameunit(sc->sc_dev)); + printf("%s: can not if_alloc()\n", + device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } @@ -444,46 +455,30 @@ USB_ATTACH(ural) /* set device capabilities */ ic->ic_caps = - IEEE80211_C_IBSS | /* IBSS mode supported */ - IEEE80211_C_MONITOR | /* monitor mode supported */ - IEEE80211_C_HOSTAP | /* HostAp mode supported */ - IEEE80211_C_TXPMGT | /* tx power management */ - IEEE80211_C_SHPREAMBLE | /* short preamble supported */ - IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WPA; /* 802.11i */ - - if (sc->rf_rev == RAL_RF_5222) { - /* set supported .11a channels */ - for (i = 36; i <= 64; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 100; i <= 140; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - for (i = 149; i <= 161; i += 4) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; - } - } - - /* set supported .11b and .11g channels (1 through 14) */ - for (i = 1; i <= 14; i++) { - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); - ic->ic_channels[i].ic_flags = - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; - } + IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RAL_RF_5222) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1); ieee80211_ifattach(ic); ic->ic_reset = ural_reset; /* enable s/w bmiss handling in sta mode */ ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + ic->ic_scan_start = ural_scan_start; + ic->ic_scan_end = ural_scan_end; + ic->ic_set_channel = ural_set_channel; /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; @@ -491,7 +486,9 @@ USB_ATTACH(ural) ic->ic_raw_xmit = ural_raw_xmit; ieee80211_media_init(ic, ural_media_change, ieee80211_media_status); - ieee80211_amrr_init(&sc->amrr, ic, 1, 15); + ieee80211_amrr_init(&sc->amrr, ic, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD); bpfattach2(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf); @@ -519,7 +516,6 @@ USB_DETACH(ural) ural_stop(sc); usb_rem_task(sc->sc_udev, &sc->sc_task); callout_stop(&sc->watchdog_ch); - callout_stop(&sc->scan_ch); callout_stop(&sc->amrr_ch); if (sc->amrr_xfer != NULL) { @@ -694,24 +690,10 @@ ural_media_change(struct ifnet *ifp) return 0; } -/* - * This function is called periodically (every 200ms) during scanning to - * switch from one channel to another. - */ -static void -ural_next_scan(void *arg) -{ - struct ural_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_next_scan(ic); -} - static void -ural_task(void *arg) +ural_task(void *xarg) { - struct ural_softc *sc = arg; + struct ural_softc *sc = xarg; struct ieee80211com *ic = &sc->sc_ic; enum ieee80211_state ostate; struct ieee80211_node *ni; @@ -720,7 +702,6 @@ ural_task(void *arg) ostate = ic->ic_state; RAL_LOCK(sc); - switch (sc->sc_state) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { @@ -732,22 +713,7 @@ ural_task(void *arg) } break; - case IEEE80211_S_SCAN: - ural_set_chan(sc, ic->ic_curchan); - callout_reset(&sc->scan_ch, hz / 5, ural_next_scan, sc); - break; - - case IEEE80211_S_AUTH: - ural_set_chan(sc, ic->ic_curchan); - break; - - case IEEE80211_S_ASSOC: - ural_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_RUN: - ural_set_chan(sc, ic->ic_curchan); - ni = ic->ic_bss; if (ic->ic_opmode != IEEE80211_M_MONITOR) { @@ -785,18 +751,44 @@ ural_task(void *arg) ural_amrr_start(sc, ni); break; + + default: + break; } RAL_UNLOCK(sc); sc->sc_newstate(ic, sc->sc_state, sc->sc_arg); } +static void +ural_scantask(void *arg) +{ + struct ural_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; + + RAL_LOCK(sc); + if (sc->sc_scan_action == URAL_SCAN_START) { + /* abort TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_set_bssid(sc, ifp->if_broadcastaddr); + } else if (sc->sc_scan_action == URAL_SET_CHANNEL) { + mtx_lock(&Giant); + ural_set_chan(sc, ic->ic_curchan); + mtx_unlock(&Giant); + } else { + ural_enable_tsf_sync(sc); + /* XXX keep local copy */ + ural_set_bssid(sc, ic->ic_bss->ni_bssid); + } + RAL_UNLOCK(sc); +} + static int ural_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct ural_softc *sc = ic->ic_ifp->if_softc; - callout_stop(&sc->scan_ch); callout_stop(&sc->amrr_ch); /* do it in a process context */ @@ -859,6 +851,9 @@ ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) struct ural_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ic.ic_ifp; + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, + status == USBD_NORMAL_COMPLETION ? 0 : ETIMEDOUT); if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; @@ -870,6 +865,7 @@ ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); ifp->if_oerrors++; + /* XXX mbuf leak? */ return; } @@ -946,7 +942,6 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; - m->m_flags |= M_HASFCS; /* h/w leaves FCS */ if (bpf_peers_present(sc->sc_drvbpf)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; @@ -956,16 +951,19 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->rx_ant; - tap->wr_antsignal = desc->rssi; + tap->wr_antsignal = URAL_RSSI(desc->rssi); bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } + /* Strip trailing 802.11 MAC FCS. */ + m_adj(m, -IEEE80211_CRC_LEN); + wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, desc->rssi, 0); + ieee80211_input(ic, m, ni, URAL_RSSI(desc->rssi), RAL_NOISE_FLOOR, 0); /* node is no longer needed */ ieee80211_free_node(ni); @@ -1303,8 +1301,12 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, ural_txeof); error = usbd_transfer(data->xfer); - if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) + if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { + m_freem(m0); + data->m = NULL; + data->ni = NULL; return error; + } sc->tx_queued++; @@ -1327,7 +1329,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) - rate = ic->ic_bss->ni_rates.rs_rates[ic->ic_fixed_rate]; + rate = ic->ic_fixed_rate; else rate = ni->ni_rates.rs_rates[ni->ni_txrate]; @@ -1481,19 +1483,21 @@ static void ural_watchdog(void *arg) { struct ural_softc *sc = (struct ural_softc *)arg; - struct ieee80211com *ic = &sc->sc_ic; + + RAL_LOCK(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /*ural_init(sc); XXX needs a process context! */ sc->sc_ifp->if_oerrors++; + RAL_UNLOCK(sc); return; } callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc); } - ieee80211_watchdog(ic); + RAL_UNLOCK(sc); } /* @@ -1738,6 +1742,45 @@ ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) } static void +ural_scan_start(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = URAL_SCAN_START; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); + +} + +static void +ural_scan_end(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = URAL_SCAN_END; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); + +} + +static void +ural_set_channel(struct ieee80211com *ic) +{ + + struct ural_softc *sc = ic->ic_ifp->if_softc; + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + + /* do it in a process context */ + sc->sc_scan_action = URAL_SET_CHANNEL; + usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); +} + +static void ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; @@ -1820,7 +1863,7 @@ ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) } if (ic->ic_opmode != IEEE80211_M_MONITOR && - ic->ic_state != IEEE80211_S_SCAN) { + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* set Japan filter bit for channel 14 */ tmp = ural_bbp_read(sc, 70); @@ -1836,6 +1879,18 @@ ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) DELAY(10000); ural_disable_rf_tune(sc); } + + /* update basic rate set */ + if (IEEE80211_IS_CHAN_B(c)) { + /* 11b basic rates: 1, 2Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x3); + } else if (IEEE80211_IS_CHAN_A(c)) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x150); + } else { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + } } /* @@ -1948,7 +2003,7 @@ ural_set_basicrates(struct ural_softc *sc) } static void -ural_set_bssid(struct ural_softc *sc, uint8_t *bssid) +ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) { uint16_t tmp; @@ -2166,7 +2221,6 @@ ural_init(void *priv) if (ural_bbp_init(sc) != 0) goto fail; - /* set default BSS channel */ ural_set_chan(sc, ic->ic_curchan); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ diff --git a/sys/dev/usb/if_uralreg.h b/sys/dev/usb/if_uralreg.h index 70e06f1887a7..428089f94589 100644 --- a/sys/dev/usb/if_uralreg.h +++ b/sys/dev/usb/if_uralreg.h @@ -17,6 +17,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define RAL_NOISE_FLOOR -95 +#define RAL_RSSI_CORR 120 + #define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc)) #define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc)) diff --git a/sys/dev/usb/if_uralvar.h b/sys/dev/usb/if_uralvar.h index 18b5cdfe7555..665b05bd4070 100644 --- a/sys/dev/usb/if_uralvar.h +++ b/sys/dev/usb/if_uralvar.h @@ -20,6 +20,11 @@ #define RAL_RX_LIST_COUNT 1 #define RAL_TX_LIST_COUNT 1 +#define URAL_SCAN_START 1 +#define URAL_SCAN_END 2 +#define URAL_SET_CHANNEL 3 + + struct ural_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; @@ -91,7 +96,9 @@ struct ural_softc { enum ieee80211_state sc_state; int sc_arg; + int sc_scan_action; /* should be an enum */ struct usb_task sc_task; + struct usb_task sc_scantask; struct ieee80211_amrr amrr; struct ieee80211_amrr_node amn; @@ -105,9 +112,7 @@ struct ural_softc { struct mtx sc_mtx; struct callout watchdog_ch; - struct callout scan_ch; struct callout amrr_ch; - int sc_tx_timer; uint16_t sta[11]; diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index ebe35d260add..aeb8a4e4c0e2 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -118,7 +118,7 @@ static int wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr, struct mbuf *m0); static int wi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); -static int wi_reset(struct wi_softc *); +static int wi_reset(struct ifnet *); static void wi_watchdog(void *); static int wi_ioctl(struct ifnet *, u_long, caddr_t); static int wi_media_change(struct ifnet *); @@ -164,6 +164,14 @@ static int wi_symbol_write_firm(struct wi_softc *, const void *, int, const void *, int); static int wi_symbol_set_hcr(struct wi_softc *, int); +static void wi_scan_start(struct ieee80211com *); +static void wi_scan_end(struct ieee80211com *); +static void wi_set_channel(struct ieee80211com *); +static void wi_update_slot(struct ifnet *); +static struct ieee80211_node *wi_node_alloc(struct ieee80211_node_table *); +static int wi_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data); +static int wi_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data); + static __inline int wi_write_val(struct wi_softc *sc, int rid, u_int16_t val) { @@ -253,6 +261,7 @@ wi_attach(device_t dev) int error; ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + ifp->if_softc = sc; if (ifp == NULL) { device_printf(dev, "can not if_alloc\n"); wi_free(dev); @@ -279,7 +288,7 @@ wi_attach(device_t dev) sc->sc_firmware_type = WI_NOTYPE; sc->wi_cmd_count = 500; /* Reset the NIC. */ - if (wi_reset(sc) != 0) + if (wi_reset(ifp) != 0) return ENXIO; /* XXX */ /* @@ -308,7 +317,6 @@ wi_attach(device_t dev) /* Read NIC identification */ wi_read_nicid(sc); - ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = wi_ioctl; @@ -338,11 +346,14 @@ wi_attach(device_t dev) val <<= 1; /* shift for base 1 indices */ for (i = 1; i < 16; i++) { + struct ieee80211_channel *c; + if (!isset((u_int8_t*)&val, i)) continue; - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, IEEE80211_CHAN_B); - ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B; + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_B); + c->ic_flags = IEEE80211_CHAN_B; + c->ic_ieee = i; } /* @@ -356,14 +367,14 @@ wi_attach(device_t dev) buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0) { val = le16toh(val); - KASSERT(val < IEEE80211_CHAN_MAX && - ic->ic_channels[val].ic_flags != 0, - ("wi_attach: invalid own channel %u!", val)); - ic->ic_ibss_chan = &ic->ic_channels[val]; + ic->ic_bsschan = ieee80211_find_channel(ic, + ieee80211_ieee2mhz(val, IEEE80211_CHAN_B), + IEEE80211_MODE_AUTO); + /* XXX check return value */ } else { device_printf(dev, "WI_RID_OWN_CHNL failed, using first channel!\n"); - ic->ic_ibss_chan = &ic->ic_channels[0]; + ic->ic_bsschan = &ic->ic_channels[0]; } /* @@ -469,7 +480,7 @@ wi_attach(device_t dev) sc->sc_system_scale = 1; sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN; sc->sc_roaming_mode = 1; - + sc->wi_channel = IEEE80211_CHAN_ANYC; sc->sc_portnum = WI_DEFAULT_PORT; sc->sc_authtype = WI_DEFAULT_AUTHTYPE; @@ -491,6 +502,14 @@ wi_attach(device_t dev) ic->ic_crypto.cs_key_alloc = wi_key_alloc; ic->ic_newstate = wi_newstate; ic->ic_raw_xmit = wi_raw_xmit; + + ic->ic_scan_start = wi_scan_start; + ic->ic_scan_end = wi_scan_end; + ic->ic_set_channel = wi_set_channel; + ic->ic_node_alloc = wi_node_alloc; + ic->ic_updateslot = wi_update_slot; + ic->ic_reset = wi_reset; + ieee80211_media_init(ic, wi_media_change, wi_media_status); #if NBPFILTER > 0 @@ -650,19 +669,20 @@ wi_init(void *arg) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = &sc->sc_ic; struct wi_joinreq join; + struct ieee80211_channel *chan; int i; int error = 0, wasenabled; - WI_LOCK(sc); - if (sc->wi_gone) { - WI_UNLOCK(sc); + + if (sc->wi_gone) return; - } if ((wasenabled = sc->sc_enabled)) wi_stop(ifp, 1); - wi_reset(sc); + + WI_LOCK(sc); + wi_reset(ifp); /* common 802.11 configuration */ ic->ic_flags &= ~IEEE80211_F_IBSSON; @@ -684,9 +704,9 @@ wi_init(void *arg) * HostAP mode the controller will lock up otherwise. */ if (sc->sc_firmware_type == WI_INTERSIL && - ic->ic_des_esslen == 0) { - ic->ic_des_essid[0] = ' '; - ic->ic_des_esslen = 1; + ic->ic_des_ssid[0].len == 0) { + ic->ic_des_ssid[0].ssid[0] = ' '; + ic->ic_des_ssid[0].len = 1; } wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP); break; @@ -703,20 +723,24 @@ wi_init(void *arg) wi_cmd(sc, WI_CMD_DEBUG | (WI_TEST_MONITOR << 8), 0, 0, 0); break; + case IEEE80211_M_WDS: + /* XXXX */ + break; } /* Intersil interprets this RID as joining ESS even in IBSS mode */ if (sc->sc_firmware_type == WI_LUCENT && - (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen > 0) + (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_ssid[0].len > 0) wi_write_val(sc, WI_RID_CREATE_IBSS, 1); else wi_write_val(sc, WI_RID_CREATE_IBSS, 0); wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval); - wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_essid, - ic->ic_des_esslen); + wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); wi_write_val(sc, WI_RID_OWN_CHNL, - ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); - wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_essid, ic->ic_des_esslen); + ieee80211_chan2ieee(ic, ic->ic_bsschan)); + wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_ssid[0].ssid, + ic->ic_des_ssid[0].len); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN); @@ -803,9 +827,11 @@ wi_init(void *arg) if (ic->ic_opmode == IEEE80211_M_AHDEMO || ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_MONITOR || - ic->ic_opmode == IEEE80211_M_HOSTAP) - ieee80211_create_ibss(ic, ic->ic_ibss_chan); - + ic->ic_opmode == IEEE80211_M_HOSTAP) { + chan = (sc->wi_channel == IEEE80211_CHAN_ANYC) ? + ic->ic_curchan : sc->wi_channel; + ieee80211_create_ibss(ic, chan); + } /* Enable interrupts */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); @@ -851,11 +877,10 @@ wi_stop(struct ifnet *ifp, int disable) struct wi_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; - WI_LOCK(sc); + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); DELAY(100000); - - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + WI_LOCK(sc); if (sc->sc_enabled && !sc->wi_gone) { CSR_WRITE_2(sc, WI_INT_EN, 0); wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0); @@ -973,8 +998,7 @@ wi_start_locked(struct ifnet *ifp) k = ieee80211_crypto_encap(ic, ni, m0); if (k == NULL) { - if (ni != NULL) - ieee80211_free_node(ni); + ieee80211_free_node(ni); m_freem(m0); continue; } @@ -994,8 +1018,7 @@ wi_start_locked(struct ifnet *ifp) frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len); if (IFF_DUMPPKTS(ifp)) wi_dump_pkt(&frmhdr, NULL, -1); - if (ni != NULL) - ieee80211_free_node(ni); + ieee80211_free_node(ni); if (wi_start_tx(ifp, &frmhdr, m0)) continue; sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; @@ -1131,9 +1154,9 @@ out: } static int -wi_reset(struct wi_softc *sc) +wi_reset(struct ifnet *ifp) { - struct ifnet *ifp = sc->sc_ifp; + struct wi_softc *sc = ifp->if_softc; #define WI_INIT_TRIES 3 int i; int error = 0; @@ -1196,7 +1219,6 @@ wi_watchdog(void *arg) } /* TODO: rate control */ - ieee80211_watchdog(&sc->sc_ic); callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc); } @@ -1207,8 +1229,6 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct wi_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ifreq *ifr = (struct ifreq *)data; - struct ieee80211req *ireq; - u_int8_t nodename[IEEE80211_NWID_LEN]; int error = 0; struct thread *td = curthread; struct wi_req wreq; @@ -1289,47 +1309,102 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) WI_UNLOCK(sc); break; case SIOCG80211: - ireq = (struct ieee80211req *) data; - if (ireq->i_type == IEEE80211_IOC_STATIONNAME) { - ireq->i_len = sc->sc_nodelen + 1; - error = copyout(sc->sc_nodename, ireq->i_data, - ireq->i_len); + error = wi_ioctl_get(ifp, cmd, data); + break; + case SIOCS80211: + error = priv_check(td, PRIV_NET80211_MANAGE); + if (error) + break; + error = wi_ioctl_set(ifp, cmd, data); + + break; + default: + error = ieee80211_ioctl(ic, cmd, data); + WI_LOCK(sc); + if (error == ENETRESET) { + if (sc->sc_enabled) + wi_init(sc); /* XXX no error return */ + error = 0; } - goto ioctl_common; - case SIOCS80211: - ireq = (struct ieee80211req *) data; - if (ireq->i_type == IEEE80211_IOC_STATIONNAME) { - error = priv_check(td, PRIV_NET80211_MANAGE); - if (error) - break; - if (ireq->i_val != 0 || - ireq->i_len > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - memset(nodename, 0, IEEE80211_NWID_LEN); - error = copyin(ireq->i_data, nodename, ireq->i_len); - if (error) - break; - WI_LOCK(sc); - if (sc->sc_enabled) { - error = wi_write_ssid(sc, WI_RID_NODENAME, - nodename, ireq->i_len); - } - if (error == 0) { - memcpy(sc->sc_nodename, nodename, - IEEE80211_NWID_LEN); - sc->sc_nodelen = ireq->i_len; - } - WI_UNLOCK(sc); + WI_UNLOCK(sc); + break; + } + return (error); +} + +static int +wi_ioctl_get(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int error; + struct wi_softc *sc; + struct ieee80211req *ireq; + struct ieee80211com *ic; + + + sc = ifp->if_softc; + ic = &sc->sc_ic; + ireq = (struct ieee80211req *) data; + + switch (ireq->i_type) { + case IEEE80211_IOC_STATIONNAME: + ireq->i_len = sc->sc_nodelen + 1; + error = copyout(sc->sc_nodename, ireq->i_data, + ireq->i_len); + break; + default: + error = ieee80211_ioctl(ic, cmd, data); + WI_LOCK(sc); + if (error == ENETRESET) { + if (sc->sc_enabled) + wi_init(sc); /* XXX no error return */ + error = 0; + } + WI_UNLOCK(sc); + + break; + } + + return (error); +} + +static int +wi_ioctl_set(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int error; + struct wi_softc *sc; + struct ieee80211req *ireq; + u_int8_t nodename[IEEE80211_NWID_LEN]; + + sc = ifp->if_softc; + ireq = (struct ieee80211req *) data; + switch (ireq->i_type) { + case IEEE80211_IOC_STATIONNAME: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; break; } - goto ioctl_common; + memset(nodename, 0, IEEE80211_NWID_LEN); + error = copyin(ireq->i_data, nodename, ireq->i_len); + if (error) + break; + WI_LOCK(sc); + if (sc->sc_enabled) { + error = wi_write_ssid(sc, WI_RID_NODENAME, + nodename, ireq->i_len); + } + if (error == 0) { + memcpy(sc->sc_nodename, nodename, + IEEE80211_NWID_LEN); + sc->sc_nodelen = ireq->i_len; + } + WI_UNLOCK(sc); + + break; default: - ioctl_common: + error = ieee80211_ioctl(&sc->sc_ic, cmd, data); WI_LOCK(sc); - error = ieee80211_ioctl(ic, cmd, data); if (error == ENETRESET) { if (sc->sc_enabled) wi_init(sc); /* XXX no error return */ @@ -1338,9 +1413,21 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) WI_UNLOCK(sc); break; } + return (error); } +static struct ieee80211_node * +wi_node_alloc(struct ieee80211_node_table *nt) +{ + struct wi_node *rn; + + rn = malloc(sizeof (struct wi_node), M_80211_NODE, + M_NOWAIT | M_ZERO); + + return (rn != NULL) ? &rn->ni : NULL; +} + static int wi_media_change(struct ifnet *ifp) { @@ -1413,6 +1500,9 @@ wi_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_M_MONITOR: imr->ifm_active |= IFM_IEEE80211_MONITOR; break; + case IEEE80211_M_WDS: + /* XXXX */ + break; } } @@ -1439,6 +1529,7 @@ wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) return; sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1); +#if 0 /* * XXX hack; we should create a new node with the new bssid * and replace the existing ic_bss with it but since we don't @@ -1447,6 +1538,7 @@ wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) * called and it will overwrite the node state. */ ieee80211_sta_join(ic, ieee80211_ref_node(ni)); +#endif } static void @@ -1666,7 +1758,7 @@ wi_rx_intr(struct wi_softc *sc) /* * Send frame up for processing. */ - ieee80211_input(ic, m, ni, rssi, rstamp); + ieee80211_input(ic, m, ni, rssi, -95/*XXXXwi_rx_silence?*/, rstamp); /* * The frame may have caused the node to be marked for * reclamation (e.g. in response to a DEAUTH message) @@ -1826,8 +1918,9 @@ wi_info_intr(struct wi_softc *sc) case WI_INFO_SCAN_RESULTS: case WI_INFO_HOST_SCAN_RESULTS: wi_scan_result(sc, fid, le16toh(ltbuf[0])); + ieee80211_notify_scan_done(ic); break; - + default: DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid, le16toh(ltbuf[1]), le16toh(ltbuf[0]))); @@ -1992,7 +2085,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_TX_CRYPT_KEY: case WI_RID_DEFLT_CRYPT_KEYS: case WI_RID_TX_RATE: - return ieee80211_cfgget(ic, cmd, data); + return ieee80211_ioctl(ic, cmd, data); case WI_RID_MICROWAVE_OVEN: if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) { @@ -2046,7 +2139,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_READ_APS: if (ic->ic_opmode == IEEE80211_M_HOSTAP) - return ieee80211_cfgget(ic, cmd, data); + return ieee80211_ioctl(ic, cmd, data); if (sc->sc_scan_timer > 0) { error = EINPROGRESS; break; @@ -2083,11 +2176,11 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) break; case WI_RID_READ_CACHE: - return ieee80211_cfgget(ic, cmd, data); + return ieee80211_ioctl(ic, cmd, data); case WI_RID_SCAN_RES: /* compatibility interface */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) - return ieee80211_cfgget(ic, cmd, data); + return ieee80211_ioctl(ic, cmd, data); if (sc->sc_scan_timer > 0) { error = EINPROGRESS; break; @@ -2169,7 +2262,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) sc->sc_nodelen); break; default: - return ieee80211_cfgget(ic, cmd, data); + return ieee80211_ioctl(ic, cmd, data); } break; } @@ -2289,7 +2382,7 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) WI_UNLOCK(sc); return EINVAL; } - ic->ic_fixed_rate = i; + ic->ic_fixed_rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; } if (sc->sc_enabled) error = wi_write_txrate(sc); @@ -2347,9 +2440,10 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) break; } WI_LOCK(sc); - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; - memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); + memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + ic->ic_des_ssid[0].len = le16toh(wreq.wi_val[0]) * 2; + memcpy(ic->ic_des_ssid[0].ssid, &wreq.wi_val[1], + ic->ic_des_ssid[0].len); if (sc->sc_enabled) wi_init(sc); /* XXX no error return */ WI_UNLOCK(sc); @@ -2361,8 +2455,8 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val, len); if (error == 0) { - /* XXX ieee80211_cfgset does a copyin */ - error = ieee80211_cfgset(ic, cmd, data); + /* XXX ieee80211_ioctl does a copyin */ + error = ieee80211_ioctl(ic, cmd, data); if (error == ENETRESET) { if (sc->sc_enabled) wi_init(sc); @@ -2385,8 +2479,7 @@ wi_write_txrate(struct wi_softc *sc) if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) rate = 0; /* auto */ else - rate = (ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL) / 2; + rate = ic->ic_fixed_rate / 2; /* rate: 0, 1, 2, 5, 11 */ @@ -2868,8 +2961,7 @@ wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen); /* XXX validate channel */ ni->ni_chan = &ic->ic_channels[le16toh(val)]; - ic->ic_curchan = ni->ni_chan; - ic->ic_ibss_chan = ni->ni_chan; + ic->ic_curchan = ic->ic_bsschan = ni->ni_chan; #if NBPFILTER > 0 sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = htole16(ni->ni_chan->ic_freq); @@ -2944,6 +3036,16 @@ wi_scan_result(struct wi_softc *sc, int fid, int cnt) struct wi_scan_header ws_hdr; /* Prism2 header */ struct wi_scan_data_p2 ws_dat; /* Prism2 scantable*/ struct wi_apinfo *ap; + struct ieee80211_scanparams sp; + struct ieee80211_frame wh; + static long rstamp; + struct ieee80211com *ic; + uint8_t ssid[2+IEEE80211_NWID_LEN]; + + printf("wi_scan_result\n"); + ic = &sc->sc_ic; + rstamp++; + memset(&sp, 0, sizeof(sp)); off = sizeof(u_int16_t) * 2; memset(&ws_hdr, 0, sizeof(ws_hdr)); @@ -2973,25 +3075,45 @@ wi_scan_result(struct wi_softc *sc, int fid, int cnt) /* Read Data */ ap = sc->sc_aps; memset(&ws_dat, 0, sizeof(ws_dat)); + for (i = 0; i < naps; i++, ap++) { + uint8_t rates[2]; + uint16_t *bssid; wi_read_bap(sc, fid, off, &ws_dat, (sizeof(ws_dat) < szbuf ? sizeof(ws_dat) : szbuf)); DPRINTF2(("wi_scan_result: #%d: off %d bssid %s\n", i, off, ether_sprintf(ws_dat.wi_bssid))); + off += szbuf; - ap->scanreason = le16toh(ws_hdr.wi_reason); + ap->scanreason = le16toh(ws_hdr.wi_reason); memcpy(ap->bssid, ws_dat.wi_bssid, sizeof(ap->bssid)); - ap->channel = le16toh(ws_dat.wi_chid); + + bssid = (uint16_t *)&ap->bssid; + if (bssid[0] == 0 && bssid[1] == 0 && bssid[2] == 0) + break; + + memcpy(wh.i_addr2, ws_dat.wi_bssid, sizeof(ap->bssid)); + memcpy(wh.i_addr3, ws_dat.wi_bssid, sizeof(ap->bssid)); + sp.chan = ap->channel = le16toh(ws_dat.wi_chid); ap->signal = le16toh(ws_dat.wi_signal); ap->noise = le16toh(ws_dat.wi_noise); ap->quality = ap->signal - ap->noise; - ap->capinfo = le16toh(ws_dat.wi_capinfo); - ap->interval = le16toh(ws_dat.wi_interval); - ap->rate = le16toh(ws_dat.wi_rate); + sp.capinfo = ap->capinfo = le16toh(ws_dat.wi_capinfo); + sp.bintval = ap->interval = le16toh(ws_dat.wi_interval); + ap->rate = le16toh(ws_dat.wi_rate); + rates[1] = 1; + rates[2] = (uint8_t)ap->rate; ap->namelen = le16toh(ws_dat.wi_namelen); if (ap->namelen > sizeof(ap->name)) ap->namelen = sizeof(ap->name); memcpy(ap->name, ws_dat.wi_name, ap->namelen); + sp.ssid = (uint8_t *)&ssid[0]; + memcpy(sp.ssid + 2, ap->name, ap->namelen); + sp.ssid[1] = ap->namelen; + sp.rates = &rates[0]; + sp.tstamp = (uint8_t *)&rstamp; + printf("calling add_scan \n"); + ieee80211_add_scan(ic, &sp, &wh, 0, ap->signal, ap->noise, rstamp); } done: /* Done scanning */ @@ -3003,8 +3125,11 @@ done: static void wi_dump_pkt(struct wi_frame *wh, struct ieee80211_node *ni, int rssi) { - ieee80211_dump_pkt((u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr), - ni ? ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL : -1, rssi); + if (ni != NULL) + ieee80211_dump_pkt(ni->ni_ic, + (u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr), + ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL, + rssi); printf(" status 0x%x rx_tstamp1 %u rx_tstamp0 0x%u rx_silence %u\n", le16toh(wh->wi_status), le16toh(wh->wi_rx_tstamp1), le16toh(wh->wi_rx_tstamp0), wh->wi_rx_silence); @@ -3364,3 +3489,50 @@ wi_symbol_set_hcr(struct wi_softc *sc, int mode) tsleep(sc, PWAIT, "wiinit", 1); return 0; } + +/* + * This function can be called by ieee80211_set_shortslottime(). Refer to + * IEEE Std 802.11-1999 pp. 85 to know how these values are computed. + */ +static void +wi_update_slot(struct ifnet *ifp) +{ + DPRINTF(("wi update slot unimplemented\n")); +} + +static void +wi_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct wi_softc *sc = ifp->if_softc; + + WI_LOCK(sc); + if (!(sc->sc_flags & WI_FLAGS_SCANNING)) { + sc->wi_channel = ic->ic_curchan; + } + WI_UNLOCK(sc); +} + +static void +wi_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct wi_softc *sc = ifp->if_softc; + + WI_LOCK(sc); + sc->sc_flags |= WI_FLAGS_SCANNING; + wi_scan_ap(sc, 0x3fff, 0x000f); + WI_UNLOCK(sc); + +} + +static void +wi_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct wi_softc *sc = ifp->if_softc; + + WI_LOCK(sc); + sc->sc_flags &= ~WI_FLAGS_SCANNING; + WI_UNLOCK(sc); +} diff --git a/sys/dev/wi/if_wivar.h b/sys/dev/wi/if_wivar.h index f7159afa8e87..88238a346534 100644 --- a/sys/dev/wi/if_wivar.h +++ b/sys/dev/wi/if_wivar.h @@ -100,6 +100,7 @@ struct wi_softc { bus_space_handle_t wi_bmemhandle; bus_space_tag_t wi_bmemtag; void * wi_intrhand; + struct ieee80211_channel *wi_channel; int wi_io_addr; int wi_cmd_count; @@ -108,7 +109,7 @@ struct wi_softc { int sc_if_flags; int sc_bap_id; int sc_bap_off; - + u_int16_t sc_procframe; u_int16_t sc_portnum; @@ -201,6 +202,13 @@ struct wi_softc { #define WI_FLAGS_BUG_AUTOINC 0x0100 #define WI_FLAGS_HAS_FRAGTHR 0x0200 #define WI_FLAGS_HAS_DBMADJUST 0x0400 +#define WI_FLAGS_SCANNING 0x0800 + + +/* driver-specific node state */ +struct wi_node { + struct ieee80211_node ni; /* base class */ +}; struct wi_card_ident { u_int16_t card_id; diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c index 231001666f31..31f306e0f557 100644 --- a/sys/kern/subr_witness.c +++ b/sys/kern/subr_witness.c @@ -363,6 +363,18 @@ static struct witness_order_list_entry order_lists[] = { { "nfsd_mtx", &lock_class_mtx_sleep }, { "so_snd", &lock_class_mtx_sleep }, { NULL, NULL }, + + /* + * IEEE 802.11 + */ + { "802.11 com lock", &lock_class_mtx_sleep}, + { NULL, NULL }, + /* + * Network drivers + */ + { "network driver", &lock_class_mtx_sleep}, + { NULL, NULL }, + /* * Netgraph */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 748a92e5604e..e0522ba43dde 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -296,6 +296,8 @@ SUBDIR= ${_3dfx} \ wb \ ${_wi} \ wlan \ + wlan_scan_ap \ + wlan_scan_sta \ wlan_acl \ wlan_amrr \ wlan_ccmp \ diff --git a/sys/modules/wlan_scan_ap/Makefile b/sys/modules/wlan_scan_ap/Makefile new file mode 100644 index 000000000000..117a6bca3d87 --- /dev/null +++ b/sys/modules/wlan_scan_ap/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../net80211 + +KMOD= wlan_scan_ap +SRCS= ieee80211_scan_ap.c + +.include <bsd.kmod.mk> diff --git a/sys/modules/wlan_scan_sta/Makefile b/sys/modules/wlan_scan_sta/Makefile new file mode 100644 index 000000000000..8d96875d5fdf --- /dev/null +++ b/sys/modules/wlan_scan_sta/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../net80211 + +KMOD= wlan_scan_sta +SRCS= ieee80211_scan_sta.c + +.include <bsd.kmod.mk> diff --git a/sys/net/if_media.h b/sys/net/if_media.h index 89bfe4f44245..eb77d2bfaa52 100644 --- a/sys/net/if_media.h +++ b/sys/net/if_media.h @@ -202,6 +202,8 @@ uint64_t ifmedia_baudrate(int); #define IFM_IEEE80211_OFDM3 21 /* OFDM 3Mbps */ #define IFM_IEEE80211_OFDM4 22 /* OFDM 4.5Mbps */ #define IFM_IEEE80211_OFDM27 23 /* OFDM 27Mbps */ +/* NB: not enough bits to express MCS fully */ +#define IFM_IEEE80211_MCS 24 /* HT MCS rate */ #define IFM_IEEE80211_ADHOC 0x00000100 /* Operate in Adhoc mode */ #define IFM_IEEE80211_HOSTAP 0x00000200 /* Operate in Host AP mode */ @@ -209,12 +211,16 @@ uint64_t ifmedia_baudrate(int); #define IFM_IEEE80211_IBSSMASTER 0x00000800 /* Operate as an IBSS master */ #define IFM_IEEE80211_TURBO 0x00001000 /* Operate in turbo mode */ #define IFM_IEEE80211_MONITOR 0x00002000 /* Operate in monitor mode */ +#define IFM_IEEE80211_HT40PLUS 0x00004000 /* Operate in 11n HT40+ mode */ +#define IFM_IEEE80211_HT40MINUS 0x00008000 /* Operate in 11n HT40- mode */ /* operating mode for multi-mode devices */ #define IFM_IEEE80211_11A 0x00010000 /* 5Ghz, OFDM mode */ #define IFM_IEEE80211_11B 0x00020000 /* Direct Sequence mode */ #define IFM_IEEE80211_11G 0x00030000 /* 2Ghz, CCK mode */ #define IFM_IEEE80211_FH 0x00040000 /* 2Ghz, GFSK mode */ +#define IFM_IEEE80211_11NA 0x00050000 /* 5Ghz, HT mode */ +#define IFM_IEEE80211_11NG 0x00060000 /* 2Ghz, HT mode */ /* * ATM @@ -494,6 +500,8 @@ struct ifmedia_description { { IFM_IEEE80211_IBSSMASTER, "ibss-master" }, \ { IFM_IEEE80211_TURBO, "turbo" }, \ { IFM_IEEE80211_MONITOR, "monitor" }, \ + { IFM_IEEE80211_HT40MINUS, "ht-" }, \ + { IFM_IEEE80211_HT40PLUS, "ht+" }, \ { 0, NULL }, \ } @@ -503,6 +511,8 @@ struct ifmedia_description { { IFM_IEEE80211_11B, "11b" }, \ { IFM_IEEE80211_11G, "11g" }, \ { IFM_IEEE80211_FH, "fh" }, \ + { IFM_IEEE80211_11NA, "11na" }, \ + { IFM_IEEE80211_11NG, "11ng" }, \ { 0, NULL }, \ } diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h index 053de92a476a..8747bfc7f3af 100644 --- a/sys/net80211/_ieee80211.h +++ b/sys/net80211/_ieee80211.h @@ -33,6 +33,7 @@ enum ieee80211_phytype { IEEE80211_T_FH, /* frequency hopping */ IEEE80211_T_OFDM, /* frequency division multiplexing */ IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ + IEEE80211_T_HT, /* high throughput, full GI */ }; #define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ @@ -45,20 +46,24 @@ enum ieee80211_phymode { IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */ IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */ + IEEE80211_MODE_STURBO_A = 7, /* 5GHz, OFDM, 2x clock, static */ + IEEE80211_MODE_11NA = 8, /* 5GHz, w/ HT */ + IEEE80211_MODE_11NG = 9, /* 2GHz, w/ HT */ }; -#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1) +#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1) enum ieee80211_opmode { IEEE80211_M_STA = 1, /* infrastructure station */ IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ IEEE80211_M_HOSTAP = 6, /* Software Access Point */ - IEEE80211_M_MONITOR = 8 /* Monitor mode */ + IEEE80211_M_MONITOR = 8, /* Monitor mode */ + IEEE80211_M_WDS = 2 /* WDS link */ }; #define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1) /* - * 802.11g protection mode. + * 802.11g/802.11n protection mode. */ enum ieee80211_protmode { IEEE80211_PROT_NONE = 0, /* no protection */ @@ -98,8 +103,13 @@ enum ieee80211_roamingmode { * Channels are specified by frequency and attributes. */ struct ieee80211_channel { - u_int16_t ic_freq; /* setting in Mhz */ - u_int16_t ic_flags; /* see below */ + uint32_t ic_flags; /* see below */ + uint16_t ic_freq; /* setting in Mhz */ + uint8_t ic_ieee; /* IEEE channel number */ + int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */ + int8_t ic_maxpower; /* maximum tx power in .5 dBm */ + int8_t ic_minpower; /* minimum tx power in .5 dBm */ + /* NB: hole, to be used for dfs */ }; #define IEEE80211_CHAN_MAX 255 @@ -110,17 +120,24 @@ struct ieee80211_channel { /* bits 0-3 are for private use by drivers */ /* channel attributes */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ -#define IEEE80211_CHAN_GSM 0x1000 /* 900 MHz spectrum channel */ -#define IEEE80211_CHAN_HALF 0x4000 /* Half rate channel */ -#define IEEE80211_CHAN_QUARTER 0x8000 /* Quarter rate channel */ +#define IEEE80211_CHAN_TURBO 0x00010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x00020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x00040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x00080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x00100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x00200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x00400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x00800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_CHAN_GSM 0x01000 /* 900 MHz spectrum channel */ +#define IEEE80211_CHAN_STURBO 0x02000 /* 11a static turbo channel only */ +#define IEEE80211_CHAN_HALF 0x04000 /* Half rate channel */ +#define IEEE80211_CHAN_QUARTER 0x08000 /* Quarter rate channel */ +#define IEEE80211_CHAN_HT20 0x10000 /* HT 20 channel */ +#define IEEE80211_CHAN_HT40U 0x20000 /* HT 40 channel w/ ext above */ +#define IEEE80211_CHAN_HT40D 0x40000 /* HT 40 channel w/ ext below */ + +#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D) +#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40) /* * Useful combinations of channel characteristics. @@ -135,16 +152,19 @@ struct ieee80211_channel { (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) #define IEEE80211_CHAN_G \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) -#define IEEE80211_CHAN_T \ +#define IEEE80211_CHAN_108A \ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) #define IEEE80211_CHAN_108G \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) +#define IEEE80211_CHAN_ST \ + (IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO) #define IEEE80211_CHAN_ALL \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \ - IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN) + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \ + IEEE80211_CHAN_HT) #define IEEE80211_CHAN_ALLTURBO \ - (IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO) + (IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO) #define IEEE80211_IS_CHAN_FHSS(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) @@ -158,8 +178,10 @@ struct ieee80211_channel { (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) #define IEEE80211_IS_CHAN_ANYG(_c) \ (IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c)) -#define IEEE80211_IS_CHAN_T(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) +#define IEEE80211_IS_CHAN_ST(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) +#define IEEE80211_IS_CHAN_108A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) #define IEEE80211_IS_CHAN_108G(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) @@ -167,12 +189,21 @@ struct ieee80211_channel { (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) #define IEEE80211_IS_CHAN_5GHZ(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) +#define IEEE80211_IS_CHAN_PASSIVE(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 0) #define IEEE80211_IS_CHAN_OFDM(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) #define IEEE80211_IS_CHAN_CCK(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) #define IEEE80211_IS_CHAN_GFSK(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) +#define IEEE80211_IS_CHAN_TURBO(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0) +#define IEEE80211_IS_CHAN_STURBO(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_STURBO) != 0) +#define IEEE80211_IS_CHAN_DTURBO(_c) \ + (((_c)->ic_flags & \ + (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO) #define IEEE80211_IS_CHAN_HALF(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0) #define IEEE80211_IS_CHAN_QUARTER(_c) \ @@ -181,8 +212,22 @@ struct ieee80211_channel { (((_c)->ic_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF)) == 0) #define IEEE80211_IS_CHAN_GSM(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_GSM) != 0) -#define IEEE80211_IS_CHAN_PASSIVE(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 0) +#define IEEE80211_IS_CHAN_HT(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT) != 0) +#define IEEE80211_IS_CHAN_HT20(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT20) != 0) +#define IEEE80211_IS_CHAN_HT40(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT40) != 0) +#define IEEE80211_IS_CHAN_HT40U(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT40U) != 0) +#define IEEE80211_IS_CHAN_HT40D(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT40D) != 0) +#define IEEE80211_IS_CHAN_HTA(_c) \ + (IEEE80211_IS_CHAN_5GHZ(_c) && \ + ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0) +#define IEEE80211_IS_CHAN_HTG(_c) \ + (IEEE80211_IS_CHAN_2GHZ(_c) && \ + ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0) /* ni_chan encoding for FH phy */ #define IEEE80211_FH_CHANMOD 80 @@ -197,8 +242,38 @@ struct ieee80211_channel { #define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ struct ieee80211_rateset { - u_int8_t rs_nrates; - u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t rs_nrates; + uint8_t rs_rates[IEEE80211_RATE_MAXSIZE]; }; +/* + * 802.11n variant of ieee80211_rateset. Instead + * legacy rates the entries are MCS rates. We define + * the structure such that it can be used interchangeably + * with an ieee80211_rateset (modulo structure size). + */ +#define IEEE80211_HTRATE_MAXSIZE 127 + +struct ieee80211_htrateset { + uint8_t rs_nrates; + uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE]; +}; + +/* + * Roaming state visible to user space. There are two + * thresholds that control whether roaming is considered; + * when either is exceeded the 802.11 layer will check + * the scan cache for another AP. If the cache is stale + * then a scan may be triggered. + */ +struct ieee80211_roam { + int8_t rssi11a; /* rssi thresh for 11a bss */ + int8_t rssi11b; /* for 11g sta in 11b bss */ + int8_t rssi11bOnly; /* for 11b sta */ + uint8_t pad1; + uint8_t rate11a; /* rate thresh for 11a bss */ + uint8_t rate11b; /* for 11g sta in 11b bss */ + uint8_t rate11bOnly; /* for 11b sta */ + uint8_t pad2; +}; #endif /* _NET80211__IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 1dd4ce45a43f..cc570f39eda6 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -53,6 +53,9 @@ const char *ieee80211_phymode_name[] = { "FH", /* IEEE80211_MODE_FH */ "turboA", /* IEEE80211_MODE_TURBO_A */ "turboG", /* IEEE80211_MODE_TURBO_G */ + "sturboA", /* IEEE80211_MODE_STURBO_A */ + "11na", /* IEEE80211_MODE_11NA */ + "11ng", /* IEEE80211_MODE_11NG */ }; /* @@ -72,11 +75,14 @@ static const struct ieee80211_rateset ieee80211_rateset_11g = { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B +static int media_status(enum ieee80211_opmode , + const struct ieee80211_channel *); + /* list of all instances */ SLIST_HEAD(ieee80211_list, ieee80211com); static struct ieee80211_list ieee80211_list = SLIST_HEAD_INITIALIZER(ieee80211_list); -static u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ +static uint8_t ieee80211_vapmap[32]; /* enough for 256 */ static struct mtx ieee80211_vap_mtx; MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); @@ -85,7 +91,7 @@ ieee80211_add_vap(struct ieee80211com *ic) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; - u_int8_t b; + uint8_t b; mtx_lock(&ieee80211_vap_mtx); ic->ic_vap = 0; @@ -141,47 +147,49 @@ ieee80211_chan_init(struct ieee80211com *ic) if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \ ic->ic_sup_rates[m] = def; \ } while (0) - struct ifnet *ifp = ic->ic_ifp; struct ieee80211_channel *c; int i; + KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, + ("invalid number of channels specified: %u", ic->ic_nchans)); memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; - if (c->ic_flags) { - /* - * Verify driver passed us valid data. - */ - if (i != ieee80211_chan2ieee(ic, c)) { - if_printf(ifp, "bad channel ignored; " - "freq %u flags %x number %u\n", - c->ic_freq, c->ic_flags, i); - c->ic_flags = 0; /* NB: remove */ - continue; - } - setbit(ic->ic_chan_avail, i); - /* - * Identify mode capabilities. - */ - if (IEEE80211_IS_CHAN_A(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_11A); - if (IEEE80211_IS_CHAN_B(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_11B); - if (IEEE80211_IS_CHAN_ANYG(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_11G); - if (IEEE80211_IS_CHAN_FHSS(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_FH); - if (IEEE80211_IS_CHAN_T(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); - if (IEEE80211_IS_CHAN_108G(c)) - setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); - if (ic->ic_curchan == NULL) { - /* arbitrarily pick the first channel */ - ic->ic_curchan = &ic->ic_channels[i]; - } - } + KASSERT(c->ic_flags != 0, ("channel with no flags")); + KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX, + ("channel with bogus ieee number %u", c->ic_ieee)); + setbit(ic->ic_chan_avail, c->ic_ieee); + /* + * Identify mode capabilities. + */ + if (IEEE80211_IS_CHAN_A(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11A); + if (IEEE80211_IS_CHAN_B(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11B); + if (IEEE80211_IS_CHAN_ANYG(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11G); + if (IEEE80211_IS_CHAN_FHSS(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_FH); + if (IEEE80211_IS_CHAN_108A(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); + if (IEEE80211_IS_CHAN_108G(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); + if (IEEE80211_IS_CHAN_ST(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); + if (IEEE80211_IS_CHAN_HTA(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); + if (IEEE80211_IS_CHAN_HTG(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); } + /* initialize candidate channels to all available */ + memcpy(ic->ic_chan_active, ic->ic_chan_avail, + sizeof(ic->ic_chan_avail)); + + ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + ic->ic_bsschan = IEEE80211_CHAN_ANYC; + /* arbitrarily pick the first channel */ + ic->ic_curchan = &ic->ic_channels[0]; /* fillin well-known rate sets if driver has not specified */ DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); @@ -208,35 +216,52 @@ ieee80211_ifattach(struct ieee80211com *ic) bpfattach2(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); - ieee80211_crypto_attach(ic); + /* override the 802.3 setting */ + ifp->if_hdrlen = ic->ic_headroom + + sizeof(struct ieee80211_qosframe_addr4) + + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN; + /* XXX no way to recalculate on ifdetach */ + if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { + /* XXX sanity check... */ + max_linkhdr = ALIGN(ifp->if_hdrlen); + max_hdr = max_linkhdr + max_protohdr; + max_datalen = MHLEN - max_hdr; + } - ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* * Fill in 802.11 available channel set, mark all * available channels as active, and pick a default * channel if not already specified. */ ieee80211_chan_init(ic); + + if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */ + ic->ic_flags |= IEEE80211_F_BGSCAN; #if 0 - /* - * Enable WME by default if we're capable. - */ - if (ic->ic_caps & IEEE80211_C_WME) + /* XXX not until WME+WPA issues resolved */ + if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */ ic->ic_flags |= IEEE80211_F_WME; #endif if (ic->ic_caps & IEEE80211_C_BURST) ic->ic_flags |= IEEE80211_F_BURST; + ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; + IEEE80211_LOCK_INIT(ic, "ieee80211com"); IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; + ieee80211_crypto_attach(ic); ieee80211_node_attach(ic); + ieee80211_power_attach(ic); ieee80211_proto_attach(ic); + ieee80211_ht_attach(ic); + ieee80211_scan_attach(ic); ieee80211_add_vap(ic); @@ -261,12 +286,16 @@ ieee80211_ifdetach(struct ieee80211com *ic) ieee80211_remove_vap(ic); ieee80211_sysctl_detach(ic); + ieee80211_scan_detach(ic); + ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ ieee80211_proto_detach(ic); ieee80211_crypto_detach(ic); + ieee80211_power_detach(ic); ieee80211_node_detach(ic); ifmedia_removeall(&ic->ic_media); + IEEE80211_LOCK_DESTROY(ic); IEEE80211_BEACON_LOCK_DESTROY(ic); bpfdetach(ifp); @@ -311,6 +340,7 @@ ieee80211_mhz2ieee(u_int freq, u_int flags) return 15 + ((freq - 2512) / 20); } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ if (freq <= 5000) { + /* XXX check regdomain? */ if (IS_FREQ_IN_PSB(freq)) return mappsb(freq, flags); return (freq - 4000) / 5; @@ -343,18 +373,11 @@ ieee80211_mhz2ieee(u_int freq, u_int flags) int ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) { - if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) - return c - ic->ic_channels; - else if (c == IEEE80211_CHAN_ANYC) - return IEEE80211_CHAN_ANY; - else if (c != NULL) { - if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", - c->ic_freq, c->ic_flags); - return 0; /* XXX */ - } else { + if (c == NULL) { if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); return 0; /* XXX */ } + return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); } /* @@ -391,6 +414,71 @@ ieee80211_ieee2mhz(u_int chan, u_int flags) } /* + * Locate a channel given a frequency+flags. We cache + * the previous lookup to optimize swithing between two + * channels--as happens with dynamic turbo. + */ +struct ieee80211_channel * +ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) +{ + struct ieee80211_channel *c; + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + c = ic->ic_prevchan; + if (c != NULL && c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + /* brute force search */ + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +static void +addmedia(struct ieee80211com *ic, int mode, int mword) +{ +#define TURBO(m) ((m) | IFM_IEEE80211_TURBO) +#define ADD(_ic, _s, _o) \ + ifmedia_add(&(_ic)->ic_media, \ + IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) + static const u_int mopts[IEEE80211_MODE_MAX] = { + IFM_AUTO, /* IEEE80211_MODE_AUTO */ + IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */ + IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */ + IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */ + IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */ + TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */ + TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */ + TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */ + IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */ + IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */ + }; + u_int mopt; + + KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); + mopt = mopts[mode]; + KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO, + ("no media mapping for mode %u", mode)); + + ADD(ic, mword, mopt); /* e.g. 11a auto */ + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); +#undef ADD +#undef TURBO +} + +/* * Setup the media data structures according to the channel and * rate tables. This must be called by the driver after * ieee80211_attach and before most anything else. @@ -399,13 +487,9 @@ void ieee80211_media_init(struct ieee80211com *ic, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { -#define ADD(_ic, _s, _o) \ - ifmedia_add(&(_ic)->ic_media, \ - IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) struct ifnet *ifp = ic->ic_ifp; - struct ifmediareq imr; - int i, j, mode, rate, maxrate, mword, mopt, r; - struct ieee80211_rateset *rs; + int i, j, mode, rate, maxrate, mword, r; + const struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; /* NB: this works because the structure is initialized to zero */ @@ -424,35 +508,21 @@ ieee80211_media_init(struct ieee80211com *ic, ifmedia_removeall(&ic->ic_media); ieee80211_chan_init(ic); } + ieee80211_power_lateattach(ic); /* * Fill in media characteristics. */ ifmedia_init(&ic->ic_media, 0, media_change, media_stat); maxrate = 0; + /* + * Add media for legacy operating modes. + */ memset(&allrates, 0, sizeof(allrates)); - for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { - static const u_int mopts[] = { - IFM_AUTO, - IFM_IEEE80211_11A, - IFM_IEEE80211_11B, - IFM_IEEE80211_11G, - IFM_IEEE80211_FH, - IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, - IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, - }; + for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; - mopt = mopts[mode]; - ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ - if (ic->ic_caps & IEEE80211_C_IBSS) - ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); - if (ic->ic_caps & IEEE80211_C_HOSTAP) - ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); - if (ic->ic_caps & IEEE80211_C_AHDEMO) - ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_caps & IEEE80211_C_MONITOR) - ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); + addmedia(ic, mode, IFM_AUTO); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; @@ -461,17 +531,9 @@ ieee80211_media_init(struct ieee80211com *ic, mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; - ADD(ic, mword, mopt); - if (ic->ic_caps & IEEE80211_C_IBSS) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); - if (ic->ic_caps & IEEE80211_C_HOSTAP) - ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); - if (ic->ic_caps & IEEE80211_C_AHDEMO) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_caps & IEEE80211_C_MONITOR) - ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); + addmedia(ic, mode, mword); /* - * Add rate to the collection of all rates. + * Add legacy rate to the collection of all rates. */ r = rate & IEEE80211_RATE_VAL; for (j = 0; j < allrates.rs_nrates; j++) @@ -492,35 +554,52 @@ ieee80211_media_init(struct ieee80211com *ic, IEEE80211_MODE_AUTO); if (mword == 0) continue; - mword = IFM_SUBTYPE(mword); /* remove media options */ - ADD(ic, mword, 0); - if (ic->ic_caps & IEEE80211_C_IBSS) - ADD(ic, mword, IFM_IEEE80211_ADHOC); - if (ic->ic_caps & IEEE80211_C_HOSTAP) - ADD(ic, mword, IFM_IEEE80211_HOSTAP); - if (ic->ic_caps & IEEE80211_C_AHDEMO) - ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_caps & IEEE80211_C_MONITOR) - ADD(ic, mword, IFM_IEEE80211_MONITOR); + /* NB: remove media options from mword */ + addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); + } + /* + * Add HT/11n media. Note that we do not have enough + * bits in the media subtype to express the MCS so we + * use a "placeholder" media subtype and any fixed MCS + * must be specified with a different mechanism. + */ + for (; mode < IEEE80211_MODE_MAX; mode++) { + if (isclr(ic->ic_modecaps, mode)) + continue; + addmedia(ic, mode, IFM_AUTO); + addmedia(ic, mode, IFM_IEEE80211_MCS); + } + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || + isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { + addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); + /* XXX could walk htrates */ + /* XXX known array size */ + if (ieee80211_htrates[15] > maxrate) + maxrate = ieee80211_htrates[15]; } - ieee80211_media_status(ifp, &imr); - ifmedia_set(&ic->ic_media, imr.ifm_active); + + /* NB: strip explicit mode; we're actually in autoselect */ + ifmedia_set(&ic->ic_media, + media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); -#undef ADD } const struct ieee80211_rateset * ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) { - enum ieee80211_phymode mode = ieee80211_chan2mode(ic, c); - if (IEEE80211_IS_CHAN_HALF(c)) return &ieee80211_rateset_half; if (IEEE80211_IS_CHAN_QUARTER(c)) return &ieee80211_rateset_quarter; - return &ic->ic_sup_rates[mode]; + if (IEEE80211_IS_CHAN_HTA(c)) + return &ic->ic_sup_rates[IEEE80211_MODE_11A]; + if (IEEE80211_IS_CHAN_HTG(c)) { + /* XXX does this work for basic rates? */ + return &ic->ic_sup_rates[IEEE80211_MODE_11G]; + } + return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; } void @@ -528,44 +607,77 @@ ieee80211_announce(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; int i, mode, rate, mword; - struct ieee80211_rateset *rs; + const struct ieee80211_rateset *rs; - for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { - rate = rs->rs_rates[i]; - mword = ieee80211_rate2media(ic, rate, mode); + mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); if (mword == 0) continue; + rate = ieee80211_media2rate(mword); printf("%s%d%sMbps", (i != 0 ? " " : ""), - (rate & IEEE80211_RATE_VAL) / 2, - ((rate & 0x1) != 0 ? ".5" : "")); + rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); } printf("\n"); } + ieee80211_ht_announce(ic); } -static int -findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +void +ieee80211_announce_channels(struct ieee80211com *ic) { -#define IEEERATE(_ic,_m,_i) \ - ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) - int i, nrates = ic->ic_sup_rates[mode].rs_nrates; - for (i = 0; i < nrates; i++) - if (IEEERATE(ic, mode, i) == rate) - return i; - return -1; -#undef IEEERATE + const struct ieee80211_channel *c; + char type; + int i, cw; + + printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (IEEE80211_IS_CHAN_ST(c)) + type = 'S'; + else if (IEEE80211_IS_CHAN_108A(c)) + type = 'T'; + else if (IEEE80211_IS_CHAN_108G(c)) + type = 'G'; + else if (IEEE80211_IS_CHAN_HT(c)) + type = 'n'; + else if (IEEE80211_IS_CHAN_A(c)) + type = 'a'; + else if (IEEE80211_IS_CHAN_ANYG(c)) + type = 'g'; + else if (IEEE80211_IS_CHAN_B(c)) + type = 'b'; + else + type = 'f'; + if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) + cw = 40; + else if (IEEE80211_IS_CHAN_HALF(c)) + cw = 10; + else if (IEEE80211_IS_CHAN_QUARTER(c)) + cw = 5; + else + cw = 20; + printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" + , c->ic_ieee, c->ic_freq, type + , cw + , IEEE80211_IS_CHAN_HT40U(c) ? '+' : + IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' + , c->ic_maxregpower + , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 + , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 + ); + } } /* * Find an instance by it's mac address. */ struct ieee80211com * -ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) +ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic; @@ -589,6 +701,50 @@ ieee80211_find_instance(struct ifnet *ifp) return NULL; } +static int +findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +{ +#define IEEERATE(_ic,_m,_i) \ + ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) + int i, nrates = ic->ic_sup_rates[mode].rs_nrates; + for (i = 0; i < nrates; i++) + if (IEEERATE(ic, mode, i) == rate) + return i; + return -1; +#undef IEEERATE +} + +/* + * Convert a media specification to a rate index and possibly a mode + * (if the rate is fixed and the mode is specified as ``auto'' then + * we need to lock down the mode so the index is meanginful). + */ +static int +checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +{ + + /* + * Check the rate table for the specified/current phy. + */ + if (mode == IEEE80211_MODE_AUTO) { + int i; + /* + * In autoselect mode search for the rate. + */ + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { + if (isset(ic->ic_modecaps, i) && + findrate(ic, i, rate) != -1) + return 1; + } + return 0; + } else { + /* + * Mode is fixed, check for rate. + */ + return (findrate(ic, mode, rate) != -1); + } +} + /* * Handle a media change request. */ @@ -599,7 +755,7 @@ ieee80211_media_change(struct ifnet *ifp) struct ifmedia_entry *ime; enum ieee80211_opmode newopmode; enum ieee80211_phymode newphymode; - int i, j, newrate, error = 0; + int newrate, error = 0; ic = ieee80211_find_instance(ifp); if (!ic) { @@ -623,6 +779,12 @@ ieee80211_media_change(struct ifnet *ifp) case IFM_IEEE80211_FH: newphymode = IEEE80211_MODE_FH; break; + case IFM_IEEE80211_11NA: + newphymode = IEEE80211_MODE_11NA; + break; + case IFM_IEEE80211_11NG: + newphymode = IEEE80211_MODE_11NG; + break; case IFM_AUTO: newphymode = IEEE80211_MODE_AUTO; break; @@ -634,55 +796,30 @@ ieee80211_media_change(struct ifnet *ifp) * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { - if (newphymode == IEEE80211_MODE_11A) - newphymode = IEEE80211_MODE_TURBO_A; - else if (newphymode == IEEE80211_MODE_11G) + if (newphymode == IEEE80211_MODE_11A) { + if (ic->ic_flags & IEEE80211_F_TURBOP) + newphymode = IEEE80211_MODE_TURBO_A; + else + newphymode = IEEE80211_MODE_STURBO_A; + } else if (newphymode == IEEE80211_MODE_11G) newphymode = IEEE80211_MODE_TURBO_G; else return EINVAL; } - /* - * Validate requested mode is available. - */ - if (isclr(ic->ic_modecaps, newphymode)) - return EINVAL; - + /* XXX HT40 +/- */ /* * Next, the fixed/variable rate. */ - i = -1; + newrate = ic->ic_fixed_rate; if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { /* * Convert media subtype to rate. */ newrate = ieee80211_media2rate(ime->ifm_media); - if (newrate == 0) + if (newrate == 0 || !checkrate(ic, newphymode, newrate)) return EINVAL; - /* - * Check the rate table for the specified/current phy. - */ - if (newphymode == IEEE80211_MODE_AUTO) { - /* - * In autoselect mode search for the rate. - */ - for (j = IEEE80211_MODE_11A; - j < IEEE80211_MODE_MAX; j++) { - if (isclr(ic->ic_modecaps, j)) - continue; - i = findrate(ic, j, newrate); - if (i != -1) { - /* lock mode too */ - newphymode = j; - break; - } - } - } else { - i = findrate(ic, newphymode, newrate); - } - if (i == -1) /* mode/rate mismatch */ - return EINVAL; - } - /* NB: defer rate setting to later */ + } else + newrate = IEEE80211_FIXED_RATE_NONE; /* * Deduce new operating mode but don't install it just yet. @@ -700,35 +837,18 @@ ieee80211_media_change(struct ifnet *ifp) newopmode = IEEE80211_M_STA; /* - * Autoselect doesn't make sense when operating as an AP. - * If no phy mode has been selected, pick one and lock it - * down so rate tables can be used in forming beacon frames - * and the like. - */ - if (newopmode == IEEE80211_M_HOSTAP && - newphymode == IEEE80211_MODE_AUTO) { - for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) - if (isset(ic->ic_modecaps, j)) { - newphymode = j; - break; - } - } - - /* * Handle phy mode change. */ - if (ic->ic_curmode != newphymode) { /* change phy mode */ - error = ieee80211_setmode(ic, newphymode); - if (error != 0) - return error; + if (ic->ic_des_mode != newphymode) { /* change phy mode */ + ic->ic_des_mode = newphymode; error = ENETRESET; } /* * Committed to changes, install the rate setting. */ - if (ic->ic_fixed_rate != i) { - ic->ic_fixed_rate = i; /* set fixed tx rate */ + if (ic->ic_fixed_rate != newrate) { + ic->ic_fixed_rate = newrate; /* set fixed tx rate */ error = ENETRESET; } @@ -742,6 +862,7 @@ ieee80211_media_change(struct ifnet *ifp) case IEEE80211_M_HOSTAP: case IEEE80211_M_STA: case IEEE80211_M_MONITOR: + case IEEE80211_M_WDS: ic->ic_flags &= ~IEEE80211_F_IBSSON; break; case IEEE80211_M_IBSS: @@ -764,10 +885,65 @@ ieee80211_media_change(struct ifnet *ifp) return error; } +/* + * Common code to calculate the media status word + * from the operating mode and channel state. + */ +static int +media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) +{ + int status; + + status = IFM_IEEE80211; + switch (opmode) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + status |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_HOSTAP: + status |= IFM_IEEE80211_HOSTAP; + break; + case IEEE80211_M_MONITOR: + status |= IFM_IEEE80211_MONITOR; + break; + case IEEE80211_M_AHDEMO: + status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; + break; + case IEEE80211_M_WDS: + /* should not come here */ + break; + } + if (IEEE80211_IS_CHAN_HTA(chan)) { + status |= IFM_IEEE80211_11NA; + } else if (IEEE80211_IS_CHAN_HTG(chan)) { + status |= IFM_IEEE80211_11NG; + } else if (IEEE80211_IS_CHAN_A(chan)) { + status |= IFM_IEEE80211_11A; + } else if (IEEE80211_IS_CHAN_B(chan)) { + status |= IFM_IEEE80211_11B; + } else if (IEEE80211_IS_CHAN_ANYG(chan)) { + status |= IFM_IEEE80211_11G; + } else if (IEEE80211_IS_CHAN_FHSS(chan)) { + status |= IFM_IEEE80211_FH; + } + /* XXX else complain? */ + + if (IEEE80211_IS_CHAN_TURBO(chan)) + status |= IFM_IEEE80211_TURBO; + if (IEEE80211_IS_CHAN_HT40U(chan)) + status |= IFM_IEEE80211_HT40PLUS; + if (IEEE80211_IS_CHAN_HT40D(chan)) + status |= IFM_IEEE80211_HT40MINUS; + + return status; +} + void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211com *ic; + enum ieee80211_phymode mode; const struct ieee80211_rateset *rs; ic = ieee80211_find_instance(ifp); @@ -776,9 +952,17 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) return; } imr->ifm_status = IFM_AVALID; - imr->ifm_active = IFM_IEEE80211; - if (ic->ic_state == IEEE80211_S_RUN) + /* + * NB: use the current channel's mode to lock down a xmit + * rate only when running; otherwise we may have a mismatch + * in which case the rate will not be convertible. + */ + if (ic->ic_state == IEEE80211_S_RUN) { imr->ifm_status |= IFM_ACTIVE; + mode = ieee80211_chan2mode(ic->ic_curchan); + } else + mode = IEEE80211_MODE_AUTO; + imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); /* * Calculate a current rate if possible. */ @@ -786,82 +970,18 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) /* * A fixed rate is set, report that. */ - rs = ieee80211_get_suprates(ic, ic->ic_curchan); imr->ifm_active |= ieee80211_rate2media(ic, - rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); + ic->ic_fixed_rate, mode); } else if (ic->ic_opmode == IEEE80211_M_STA) { /* * In station mode report the current transmit rate. + * XXX HT rate */ rs = &ic->ic_bss->ni_rates; imr->ifm_active |= ieee80211_rate2media(ic, - rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); + rs->rs_rates[ic->ic_bss->ni_txrate], mode); } else imr->ifm_active |= IFM_AUTO; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - break; - case IEEE80211_M_IBSS: - imr->ifm_active |= IFM_IEEE80211_ADHOC; - break; - case IEEE80211_M_AHDEMO: - /* should not come here */ - break; - case IEEE80211_M_HOSTAP: - imr->ifm_active |= IFM_IEEE80211_HOSTAP; - break; - case IEEE80211_M_MONITOR: - imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - } - switch (ic->ic_curmode) { - case IEEE80211_MODE_11A: - imr->ifm_active |= IFM_IEEE80211_11A; - break; - case IEEE80211_MODE_11B: - imr->ifm_active |= IFM_IEEE80211_11B; - break; - case IEEE80211_MODE_11G: - imr->ifm_active |= IFM_IEEE80211_11G; - break; - case IEEE80211_MODE_FH: - imr->ifm_active |= IFM_IEEE80211_FH; - break; - case IEEE80211_MODE_TURBO_A: - imr->ifm_active |= IFM_IEEE80211_11A - | IFM_IEEE80211_TURBO; - break; - case IEEE80211_MODE_TURBO_G: - imr->ifm_active |= IFM_IEEE80211_11G - | IFM_IEEE80211_TURBO; - break; - } -} - -void -ieee80211_watchdog(struct ieee80211com *ic) -{ - struct ieee80211_node_table *nt; - int need_inact_timer = 0; - - if (ic->ic_state != IEEE80211_S_INIT) { - if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); - nt = &ic->ic_scan; - if (nt->nt_inact_timer) { - if (--nt->nt_inact_timer == 0) - nt->nt_timeout(nt); - need_inact_timer += nt->nt_inact_timer; - } - nt = &ic->ic_sta; - if (nt->nt_inact_timer) { - if (--nt->nt_inact_timer == 0) - nt->nt_timeout(nt); - need_inact_timer += nt->nt_inact_timer; - } - } - if (ic->ic_mgt_timer != 0 || need_inact_timer) - ic->ic_ifp->if_timer = 1; } /* @@ -873,95 +993,6 @@ ieee80211_watchdog(struct ieee80211com *ic) int ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) { -#define N(a) (sizeof(a) / sizeof(a[0])) - static const u_int chanflags[] = { - 0, /* IEEE80211_MODE_AUTO */ - IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ - IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ - IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ - IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ - IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ - IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ - }; - struct ieee80211_channel *c; - u_int modeflags; - int i; - - /* validate new mode */ - if (isclr(ic->ic_modecaps, mode)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "%s: mode %u not supported (caps 0x%x)\n", - __func__, mode, ic->ic_modecaps); - return EINVAL; - } - - /* - * Verify at least one channel is present in the available - * channel list before committing to the new mode. - */ - KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); - modeflags = chanflags[mode]; - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - c = &ic->ic_channels[i]; - if (c->ic_flags == 0) - continue; - if (mode == IEEE80211_MODE_AUTO) { - /* ignore static turbo channels for autoselect */ - if (!IEEE80211_IS_CHAN_T(c)) - break; - } else { - if ((c->ic_flags & modeflags) == modeflags) - break; - } - } - if (i > IEEE80211_CHAN_MAX) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "%s: no channels found for mode %u\n", __func__, mode); - return EINVAL; - } - - /* - * Calculate the active channel set. - */ - memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - c = &ic->ic_channels[i]; - if (c->ic_flags == 0) - continue; - if (mode == IEEE80211_MODE_AUTO) { - /* take anything but static turbo channels */ - if (!IEEE80211_IS_CHAN_T(c)) - setbit(ic->ic_chan_active, i); - } else { - if ((c->ic_flags & modeflags) == modeflags) - setbit(ic->ic_chan_active, i); - } - } - /* - * If no current/default channel is setup or the current - * channel is wrong for the mode then pick the first - * available channel from the active list. This is likely - * not the right one. - */ - if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_curchan))) { - ic->ic_curchan = NULL; - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i)) { - ic->ic_curchan = &ic->ic_channels[i]; - break; - } - KASSERT(ic->ic_curchan != NULL, ("no current channel")); - } - if (ic->ic_ibss_chan == NULL || - isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) - ic->ic_ibss_chan = ic->ic_curchan; - /* - * If the desired channel is set but no longer valid then reset it. - */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) - ic->ic_des_chan = IEEE80211_CHAN_ANYC; - /* * Adjust basic rates in 11b/11g supported rate set. * Note that if operating on a hal/quarter rate channel @@ -971,64 +1002,71 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode); - /* - * Setup an initial rate set according to the - * current/default channel selected above. This - * will be changed when scanning but must exist - * now so driver have a consistent state of ic_ibss_chan. - */ - if (ic->ic_bss != NULL) /* NB: can be called before lateattach */ - ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; - ic->ic_curmode = mode; ieee80211_reset_erp(ic); /* reset ERP state */ ieee80211_wme_initparams(ic); /* reset WME stat */ return 0; -#undef N } /* - * Return the phy mode for with the specified channel so the - * caller can select a rate set. This is problematic for channels - * where multiple operating modes are possible (e.g. 11g+11b). - * In those cases we defer to the current operating mode when set. + * Return the phy mode for with the specified channel. */ enum ieee80211_phymode -ieee80211_chan2mode(struct ieee80211com *ic, const struct ieee80211_channel *chan) +ieee80211_chan2mode(const struct ieee80211_channel *chan) { - if (IEEE80211_IS_CHAN_T(chan)) { + + if (IEEE80211_IS_CHAN_HTA(chan)) + return IEEE80211_MODE_11NA; + else if (IEEE80211_IS_CHAN_HTG(chan)) + return IEEE80211_MODE_11NG; + else if (IEEE80211_IS_CHAN_108G(chan)) + return IEEE80211_MODE_TURBO_G; + else if (IEEE80211_IS_CHAN_ST(chan)) + return IEEE80211_MODE_STURBO_A; + else if (IEEE80211_IS_CHAN_TURBO(chan)) return IEEE80211_MODE_TURBO_A; - } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { + else if (IEEE80211_IS_CHAN_A(chan)) return IEEE80211_MODE_11A; - } else if (IEEE80211_IS_CHAN_FHSS(chan)) - return IEEE80211_MODE_FH; - else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { - /* - * This assumes all 11g channels are also usable - * for 11b, which is currently true. - */ - if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) - return IEEE80211_MODE_TURBO_G; - if (ic->ic_curmode == IEEE80211_MODE_11B) - return IEEE80211_MODE_11B; + else if (IEEE80211_IS_CHAN_ANYG(chan)) return IEEE80211_MODE_11G; - } else + else if (IEEE80211_IS_CHAN_B(chan)) return IEEE80211_MODE_11B; + else if (IEEE80211_IS_CHAN_FHSS(chan)) + return IEEE80211_MODE_FH; + + /* NB: should not get here */ + printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", + __func__, chan->ic_freq, chan->ic_flags); + return IEEE80211_MODE_11B; +} + +struct ratemedia { + u_int match; /* rate + mode */ + u_int media; /* if_media rate */ +}; + +static int +findmedia(const struct ratemedia rates[], int n, u_int match) +{ + int i; + + for (i = 0; i < n; i++) + if (rates[i].match == match) + return rates[i].media; + return IFM_AUTO; } /* - * convert IEEE80211 rate value to ifmedia subtype. - * ieee80211 rate is in unit of 0.5Mbps. + * Convert IEEE80211 rate value to ifmedia subtype. + * Rate is either a legacy rate in units of 0.5Mbps + * or an MCS index. */ int ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) { #define N(a) (sizeof(a) / sizeof(a[0])) - static const struct { - u_int m; /* rate + mode */ - u_int r; /* if_media rate */ - } rates[] = { + static const struct ratemedia rates[] = { { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, @@ -1061,36 +1099,66 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, /* NB: OFDM72 doesn't realy exist so we don't handle it */ }; - u_int mask, i; + static const struct ratemedia htrates[] = { + { 0, IFM_IEEE80211_MCS }, + { 1, IFM_IEEE80211_MCS }, + { 2, IFM_IEEE80211_MCS }, + { 3, IFM_IEEE80211_MCS }, + { 4, IFM_IEEE80211_MCS }, + { 5, IFM_IEEE80211_MCS }, + { 6, IFM_IEEE80211_MCS }, + { 7, IFM_IEEE80211_MCS }, + { 8, IFM_IEEE80211_MCS }, + { 9, IFM_IEEE80211_MCS }, + { 10, IFM_IEEE80211_MCS }, + { 11, IFM_IEEE80211_MCS }, + { 12, IFM_IEEE80211_MCS }, + { 13, IFM_IEEE80211_MCS }, + { 14, IFM_IEEE80211_MCS }, + { 15, IFM_IEEE80211_MCS }, + }; + int m; - mask = rate & IEEE80211_RATE_VAL; + /* + * Check 11n rates first for match as an MCS. + */ + if (mode == IEEE80211_MODE_11NA) { + if ((rate & IEEE80211_RATE_BASIC) == 0) { + m = findmedia(htrates, N(htrates), rate); + if (m != IFM_AUTO) + return m | IFM_IEEE80211_11NA; + } + } else if (mode == IEEE80211_MODE_11NG) { + /* NB: 12 is ambiguous, it will be treated as an MCS */ + if ((rate & IEEE80211_RATE_BASIC) == 0) { + m = findmedia(htrates, N(htrates), rate); + if (m != IFM_AUTO) + return m | IFM_IEEE80211_11NG; + } + } + rate &= IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: + case IEEE80211_MODE_11NA: case IEEE80211_MODE_TURBO_A: - mask |= IFM_IEEE80211_11A; - break; + case IEEE80211_MODE_STURBO_A: + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); case IEEE80211_MODE_11B: - mask |= IFM_IEEE80211_11B; - break; + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); case IEEE80211_MODE_FH: - mask |= IFM_IEEE80211_FH; - break; + return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ - if (ic && ic->ic_phytype == IEEE80211_T_FH) { - mask |= IFM_IEEE80211_FH; - break; - } + if (ic && ic->ic_phytype == IEEE80211_T_FH) + return findmedia(rates, N(rates), + rate | IFM_IEEE80211_FH); /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: + case IEEE80211_MODE_11NG: case IEEE80211_MODE_TURBO_G: - mask |= IFM_IEEE80211_11G; - break; + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); } - for (i = 0; i < N(rates); i++) - if (rates[i].m == mask) - return rates[i].r; return IFM_AUTO; #undef N } @@ -1124,6 +1192,7 @@ ieee80211_media2rate(int mword) 6, /* IFM_IEEE80211_OFDM3 */ 9, /* IFM_IEEE80211_OFDM4 */ 54, /* IFM_IEEE80211_OFDM27 */ + -1, /* IFM_IEEE80211_MCS */ }; return IFM_SUBTYPE(mword) < N(ieeerates) ? ieeerates[IFM_SUBTYPE(mword)] : 0; diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 6783a213a963..c697c5b700fc 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -38,11 +38,11 @@ /* IEEE 802.11 PLCP header */ struct ieee80211_plcp_hdr { - u_int16_t i_sfd; - u_int8_t i_signal; - u_int8_t i_service; - u_int16_t i_length; - u_int16_t i_crc; + uint16_t i_sfd; + uint8_t i_signal; + uint8_t i_service; + uint16_t i_length; + uint16_t i_crc; } __packed; #define IEEE80211_PLCP_SFD 0xF3A0 @@ -52,52 +52,52 @@ struct ieee80211_plcp_hdr { * generic definitions for IEEE 802.11 frames */ struct ieee80211_frame { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_addr1[IEEE80211_ADDR_LEN]; - u_int8_t i_addr2[IEEE80211_ADDR_LEN]; - u_int8_t i_addr3[IEEE80211_ADDR_LEN]; - u_int8_t i_seq[2]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ } __packed; struct ieee80211_qosframe { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_addr1[IEEE80211_ADDR_LEN]; - u_int8_t i_addr2[IEEE80211_ADDR_LEN]; - u_int8_t i_addr3[IEEE80211_ADDR_LEN]; - u_int8_t i_seq[2]; - u_int8_t i_qos[2]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_qos[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ } __packed; struct ieee80211_qoscntl { - u_int8_t i_qos[2]; + uint8_t i_qos[2]; }; struct ieee80211_frame_addr4 { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_addr1[IEEE80211_ADDR_LEN]; - u_int8_t i_addr2[IEEE80211_ADDR_LEN]; - u_int8_t i_addr3[IEEE80211_ADDR_LEN]; - u_int8_t i_seq[2]; - u_int8_t i_addr4[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_addr4[IEEE80211_ADDR_LEN]; } __packed; struct ieee80211_qosframe_addr4 { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_addr1[IEEE80211_ADDR_LEN]; - u_int8_t i_addr2[IEEE80211_ADDR_LEN]; - u_int8_t i_addr3[IEEE80211_ADDR_LEN]; - u_int8_t i_seq[2]; - u_int8_t i_addr4[IEEE80211_ADDR_LEN]; - u_int8_t i_qos[2]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_addr4[IEEE80211_ADDR_LEN]; + uint8_t i_qos[2]; } __packed; #define IEEE80211_FC0_VERSION_MASK 0x03 @@ -123,7 +123,9 @@ struct ieee80211_qosframe_addr4 { #define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0 #define IEEE80211_FC0_SUBTYPE_AUTH 0xb0 #define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0 +#define IEEE80211_FC0_SUBTYPE_ACTION 0xd0 /* for TYPE_CTL */ +#define IEEE80211_FC0_SUBTYPE_BAR 0x80 #define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0 #define IEEE80211_FC0_SUBTYPE_RTS 0xb0 #define IEEE80211_FC0_SUBTYPE_CTS 0xc0 @@ -159,13 +161,24 @@ struct ieee80211_qosframe_addr4 { #define IEEE80211_SEQ_FRAG_SHIFT 0 #define IEEE80211_SEQ_SEQ_MASK 0xfff0 #define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_RANGE 4096 + +#define IEEE80211_SEQ_ADD(seq, incr) \ + (((seq) + (incr)) & (IEEE80211_SEQ_RANGE-1)) +#define IEEE80211_SEQ_INC(seq) IEEE80211_SEQ_ADD(seq,1) +#define IEEE80211_SEQ_SUB(a, b) \ + (((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE-1)) #define IEEE80211_NWID_LEN 32 #define IEEE80211_QOS_TXOP 0x00ff /* bit 8 is reserved */ +#define IEEE80211_QOS_AMSDU 0x80 +#define IEEE80211_QOS_AMSDU_S 7 #define IEEE80211_QOS_ACKPOLICY 0x60 #define IEEE80211_QOS_ACKPOLICY_S 5 +#define IEEE80211_QOS_ACKPOLICY_NOACK 0x20 /* No ACK required */ +#define IEEE80211_QOS_ACKPOLICY_BA 0x60 /* Block ACK */ #define IEEE80211_QOS_ESOP 0x10 #define IEEE80211_QOS_ESOP_S 4 #define IEEE80211_QOS_TID 0x0f @@ -180,53 +193,54 @@ struct ieee80211_qosframe_addr4 { * WME/802.11e information element. */ struct ieee80211_wme_info { - u_int8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ - u_int8_t wme_len; /* length in bytes */ - u_int8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ - u_int8_t wme_type; /* OUI type */ - u_int8_t wme_subtype; /* OUI subtype */ - u_int8_t wme_version; /* spec revision */ - u_int8_t wme_info; /* QoS info */ + uint8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wme_len; /* length in bytes */ + uint8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ + uint8_t wme_type; /* OUI type */ + uint8_t wme_subtype; /* OUI subtype */ + uint8_t wme_version; /* spec revision */ + uint8_t wme_info; /* QoS info */ } __packed; /* * WME/802.11e Tspec Element */ struct ieee80211_wme_tspec { - u_int8_t ts_id; - u_int8_t ts_len; - u_int8_t ts_oui[3]; - u_int8_t ts_oui_type; - u_int8_t ts_oui_subtype; - u_int8_t ts_version; - u_int8_t ts_tsinfo[3]; - u_int8_t ts_nom_msdu[2]; - u_int8_t ts_max_msdu[2]; - u_int8_t ts_min_svc[4]; - u_int8_t ts_max_svc[4]; - u_int8_t ts_inactv_intv[4]; - u_int8_t ts_susp_intv[4]; - u_int8_t ts_start_svc[4]; - u_int8_t ts_min_rate[4]; - u_int8_t ts_mean_rate[4]; - u_int8_t ts_max_burst[4]; - u_int8_t ts_min_phy[4]; - u_int8_t ts_peak_rate[4]; - u_int8_t ts_delay[4]; - u_int8_t ts_surplus[2]; - u_int8_t ts_medium_time[2]; + uint8_t ts_id; + uint8_t ts_len; + uint8_t ts_oui[3]; + uint8_t ts_oui_type; + uint8_t ts_oui_subtype; + uint8_t ts_version; + uint8_t ts_tsinfo[3]; + uint8_t ts_nom_msdu[2]; + uint8_t ts_max_msdu[2]; + uint8_t ts_min_svc[4]; + uint8_t ts_max_svc[4]; + uint8_t ts_inactv_intv[4]; + uint8_t ts_susp_intv[4]; + uint8_t ts_start_svc[4]; + uint8_t ts_min_rate[4]; + uint8_t ts_mean_rate[4]; + uint8_t ts_max_burst[4]; + uint8_t ts_min_phy[4]; + uint8_t ts_peak_rate[4]; + uint8_t ts_delay[4]; + uint8_t ts_surplus[2]; + uint8_t ts_medium_time[2]; } __packed; /* * WME AC parameter field */ struct ieee80211_wme_acparams { - u_int8_t acp_aci_aifsn; - u_int8_t acp_logcwminmax; - u_int16_t acp_txop; + uint8_t acp_aci_aifsn; + uint8_t acp_logcwminmax; + uint16_t acp_txop; } __packed; #define WME_NUM_AC 4 /* 4 AC categories */ +#define WME_NUM_TID 16 /* 16 tids */ #define WME_PARAM_ACI 0x60 /* Mask for ACI field */ #define WME_PARAM_ACI_S 5 /* Shift for ACI field */ @@ -255,15 +269,15 @@ struct ieee80211_wme_acparams { * WME Parameter Element */ struct ieee80211_wme_param { - u_int8_t param_id; - u_int8_t param_len; - u_int8_t param_oui[3]; - u_int8_t param_oui_type; - u_int8_t param_oui_sybtype; - u_int8_t param_version; - u_int8_t param_qosInfo; + uint8_t param_id; + uint8_t param_len; + uint8_t param_oui[3]; + uint8_t param_oui_type; + uint8_t param_oui_sybtype; + uint8_t param_version; + uint8_t param_qosInfo; #define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */ - u_int8_t param_reserved; + uint8_t param_reserved; struct ieee80211_wme_acparams params_acParams[WME_NUM_AC]; } __packed; @@ -271,61 +285,168 @@ struct ieee80211_wme_param { * Management Notification Frame */ struct ieee80211_mnf { - u_int8_t mnf_category; - u_int8_t mnf_action; - u_int8_t mnf_dialog; - u_int8_t mnf_status; + uint8_t mnf_category; + uint8_t mnf_action; + uint8_t mnf_dialog; + uint8_t mnf_status; } __packed; #define MNF_SETUP_REQ 0 #define MNF_SETUP_RESP 1 #define MNF_TEARDOWN 2 +/* + * 802.11n Management Action Frames + */ +/* generic frame format */ +struct ieee80211_action { + uint8_t ia_category; + uint8_t ia_action; +} __packed; + +#define IEEE80211_ACTION_CAT_QOS 0 /* QoS */ +#define IEEE80211_ACTION_CAT_BA 3 /* BA */ +#define IEEE80211_ACTION_CAT_HT 5 /* HT */ + +#define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/ +#define IEEE80211_ACTION_HT_MIMOPWRSAVE 1 /* MIMO power save */ + +/* HT - recommended transmission channel width */ +struct ieee80211_action_ht_txchwidth { + struct ieee80211_action at_header; + uint8_t at_chwidth; +} __packed; + +#define IEEE80211_A_HT_TXCHWIDTH_20 0 +#define IEEE80211_A_HT_TXCHWIDTH_2040 1 + +/* HT - MIMO Power Save */ +struct ieee80211_action_ht_mimopowersave { + struct ieee80211_action am_header; + uint8_t am_enable; + uint8_t am_mode; +} __packed; + +/* Block Ack actions */ +#define IEEE80211_ACTION_BA_ADDBA_REQUEST 0 /* ADDBA request */ +#define IEEE80211_ACTION_BA_ADDBA_RESPONSE 1 /* ADDBA response */ +#define IEEE80211_ACTION_BA_DELBA 2 /* DELBA */ + +/* Block Ack Parameter Set */ +#define IEEE80211_BAPS_BUFSIZ 0xffc0 /* buffer size */ +#define IEEE80211_BAPS_BUFSIZ_S 6 +#define IEEE80211_BAPS_TID 0x003c /* TID */ +#define IEEE80211_BAPS_TID_S 2 +#define IEEE80211_BAPS_POLICY 0x0002 /* block ack policy */ +#define IEEE80211_BAPS_POLICY_S 1 + +#define IEEE80211_BAPS_POLICY_DELAYED (0<<IEEE80211_BAPS_POLICY_S) +#define IEEE80211_BAPS_POLICY_IMMEDIATE (1<<IEEE80211_BAPS_POLICY_S) + +/* Block Ack Sequence Control */ +#define IEEE80211_BASEQ_START 0xfff0 /* starting seqnum */ +#define IEEE80211_BASEQ_START_S 4 +#define IEEE80211_BASEQ_FRAG 0x000f /* fragment number */ +#define IEEE80211_BASEQ_FRAG_S 0 + +/* Delayed Block Ack Parameter Set */ +#define IEEE80211_DELBAPS_TID 0xf000 /* TID */ +#define IEEE80211_DELBAPS_TID_S 12 +#define IEEE80211_DELBAPS_INIT 0x0800 /* initiator */ +#define IEEE80211_DELBAPS_INIT_S 11 + +/* BA - ADDBA request */ +struct ieee80211_action_ba_addbarequest { + struct ieee80211_action rq_header; + uint8_t rq_dialogtoken; + uint16_t rq_baparamset; + uint16_t rq_batimeout; /* in TUs */ + uint16_t rq_baseqctl; +} __packed; + +/* BA - ADDBA response */ +struct ieee80211_action_ba_addbaresponse { + struct ieee80211_action rs_header; + uint8_t rs_dialogtoken; + uint16_t rs_statuscode; + uint16_t rs_baparamset; + uint16_t rs_batimeout; /* in TUs */ +} __packed; + +/* BA - DELBA */ +struct ieee80211_action_ba_delba { + struct ieee80211_action dl_header; + uint16_t dl_baparamset; + uint16_t dl_reasoncode; +} __packed; + +/* BAR Control */ +#define IEEE80211_BAR_TID 0xf000 /* TID */ +#define IEEE80211_BAR_TID_S 12 +#define IEEE80211_BAR_COMP 0x0004 /* compressed */ +#define IEEE80211_BAR_MTID 0x0002 +#define IEEE80211_BAR_NOACK 0x0001 /* no-ack policy */ + +struct ieee80211_ba_request { + uint16_t rq_barctl; + uint16_t rq_barseqctl; +} __packed; + /* * Control frames. */ struct ieee80211_frame_min { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_addr1[IEEE80211_ADDR_LEN]; - u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; /* FCS */ } __packed; struct ieee80211_frame_rts { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_ra[IEEE80211_ADDR_LEN]; - u_int8_t i_ta[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ } __packed; struct ieee80211_frame_cts { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ } __packed; struct ieee80211_frame_ack { - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; - u_int8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ } __packed; struct ieee80211_frame_pspoll { - u_int8_t i_fc[2]; - u_int8_t i_aid[2]; - u_int8_t i_bssid[IEEE80211_ADDR_LEN]; - u_int8_t i_ta[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_aid[2]; + uint8_t i_bssid[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ } __packed; struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ - u_int8_t i_fc[2]; - u_int8_t i_dur[2]; /* should be zero */ - u_int8_t i_ra[IEEE80211_ADDR_LEN]; - u_int8_t i_bssid[IEEE80211_ADDR_LEN]; + uint8_t i_fc[2]; + uint8_t i_dur[2]; /* should be zero */ + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_bssid[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_bar { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; + uint16_t i_ctl; + uint16_t i_seq; /* FCS */ } __packed; @@ -341,7 +462,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ * octet information[length] */ -typedef u_int8_t *ieee80211_mgt_beacon_t; +typedef uint8_t *ieee80211_mgt_beacon_t; #define IEEE80211_BEACON_INTERVAL(beacon) \ ((beacon)[8] | ((beacon)[9] << 8)) @@ -367,22 +488,153 @@ typedef u_int8_t *ieee80211_mgt_beacon_t; * 802.11i/WPA information element (maximally sized). */ struct ieee80211_ie_wpa { - u_int8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ - u_int8_t wpa_len; /* length in bytes */ - u_int8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ - u_int8_t wpa_type; /* OUI type */ - u_int16_t wpa_version; /* spec revision */ - u_int32_t wpa_mcipher[1]; /* multicast/group key cipher */ - u_int16_t wpa_uciphercnt; /* # pairwise key ciphers */ - u_int32_t wpa_uciphers[8];/* ciphers */ - u_int16_t wpa_authselcnt; /* authentication selector cnt*/ - u_int32_t wpa_authsels[8];/* selectors */ - u_int16_t wpa_caps; /* 802.11i capabilities */ - u_int16_t wpa_pmkidcnt; /* 802.11i pmkid count */ - u_int16_t wpa_pmkids[8]; /* 802.11i pmkids */ + uint8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wpa_len; /* length in bytes */ + uint8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ + uint8_t wpa_type; /* OUI type */ + uint16_t wpa_version; /* spec revision */ + uint32_t wpa_mcipher[1]; /* multicast/group key cipher */ + uint16_t wpa_uciphercnt; /* # pairwise key ciphers */ + uint32_t wpa_uciphers[8];/* ciphers */ + uint16_t wpa_authselcnt; /* authentication selector cnt*/ + uint32_t wpa_authsels[8];/* selectors */ + uint16_t wpa_caps; /* 802.11i capabilities */ + uint16_t wpa_pmkidcnt; /* 802.11i pmkid count */ + uint16_t wpa_pmkids[8]; /* 802.11i pmkids */ } __packed; /* + * 802.11n HT Capability IE + * NB: these reflect D1.10 + */ +struct ieee80211_ie_htcap { + uint8_t hc_id; /* element ID */ + uint8_t hc_len; /* length in bytes */ + uint16_t hc_cap; /* HT caps (see below) */ + uint8_t hc_param; /* HT params (see below) */ + uint8_t hc_mcsset[16]; /* supported MCS set */ + uint16_t hc_extcap; /* extended HT capabilities */ + uint32_t hc_txbf; /* txbf capabilities */ + uint8_t hc_antenna; /* antenna capabilities */ +} __packed; + +/* HT capability flags (ht_cap) */ +#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC supported */ +#define IEEE80211_HTCAP_CHWIDTH40 0x0002 /* 20/40 supported */ +#define IEEE80211_HTCAP_SMPS 0x000c /* SM Power Save mode */ +#define IEEE80211_HTCAP_SMPS_OFF 0x0000 /* none (static mode) */ +#define IEEE80211_HTCAP_SMPS_DYNAMIC 0x0004 /* send RTS first */ +/* NB: SMPS value 2 is reserved */ +#define IEEE80211_HTCAP_SMPS_ENA 0x000c /* enabled */ +#define IEEE80211_HTCAP_GREENFIELD 0x0010 /* Greenfield supported */ +#define IEEE80211_HTCAP_SHORTGI20 0x0020 /* Short GI in 20MHz */ +#define IEEE80211_HTCAP_SHORTGI40 0x0040 /* Short GI in 40MHz */ +#define IEEE80211_HTCAP_TXSTBC 0x0080 /* STBC tx ok */ +#define IEEE80211_HTCAP_RXSTBC 0x0300 /* STBC rx support */ +#define IEEE80211_HTCAP_RXSTBC_S 8 +#define IEEE80211_HTCAP_RXSTBC_1STREAM 0x0100 /* 1 spatial stream */ +#define IEEE80211_HTCAP_RXSTBC_2STREAM 0x0200 /* 1-2 spatial streams*/ +#define IEEE80211_HTCAP_RXSTBC_3STREAM 0x0300 /* 1-3 spatial streams*/ +#define IEEE80211_HTCAP_DELBA 0x0400 /* HT DELBA supported */ +#define IEEE80211_HTCAP_MAXAMSDU 0x0800 /* max A-MSDU length */ +#define IEEE80211_HTCAP_MAXAMSDU_7935 0x0800 /* 7935 octets */ +#define IEEE80211_HTCAP_MAXAMSDU_3839 0x0000 /* 3839 octets */ +#define IEEE80211_HTCAP_DSSSCCK40 0x1000 /* DSSS/CCK in 40MHz */ +#define IEEE80211_HTCAP_PSMP 0x2000 /* PSMP supported */ +#define IEEE80211_HTCAP_40INTOLERANT 0x4000 /* 40MHz intolerant */ +#define IEEE80211_HTCAP_LSIGTXOPPROT 0x8000 /* L-SIG TXOP prot */ + +/* HT parameters (hc_param) */ +#define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */ +#define IEEE80211_HTCAP_MAXRXAMPDU_S 0 +#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0x00 +#define IEEE80211_HTCAP_MAXRXAMPDU_16K 0x01 +#define IEEE80211_HTCAP_MAXRXAMPDU_32K 0x02 +#define IEEE80211_HTCAP_MAXRXAMPDU_64K 0x03 +#define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */ +#define IEEE80211_HTCAP_MPDUDENSITY_S 2 +#define IEEE80211_HTCAP_MPDUDENSITY_NA 0x00 /* no time restriction */ +#define IEEE80211_HTCAP_MPDUDENSITY_025 0x04 /* 1/4 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_05 0x08 /* 1/2 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_1 0x0c /* 1 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_2 0x10 /* 2 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_4 0x14 /* 4 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_8 0x18 /* 8 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_16 0x1c /* 16 us */ + +/* HT extended capabilities (hc_extcap) */ +#define IEEE80211_HTCAP_PCO 0x0001 /* PCO capable */ +#define IEEE80211_HTCAP_PCOTRANS 0x0006 /* PCO transition time */ +#define IEEE80211_HTCAP_PCOTRANS_S 1 +#define IEEE80211_HTCAP_PCOTRANS_04 0x0002 /* 400 us */ +#define IEEE80211_HTCAP_PCOTRANS_15 0x0004 /* 1.5 ms */ +#define IEEE80211_HTCAP_PCOTRANS_5 0x0006 /* 5 ms */ +/* bits 3-7 reserved */ +#define IEEE80211_HTCAP_MCSFBACK 0x0300 /* MCS feedback */ +#define IEEE80211_HTCAP_MCSFBACK_S 8 +#define IEEE80211_HTCAP_MCSFBACK_NONE 0x0000 /* nothing provided */ +#define IEEE80211_HTCAP_MCSFBACK_UNSOL 0x0200 /* unsolicited feedback */ +#define IEEE80211_HTCAP_MCSFBACK_MRQ 0x0300 /* " "+respond to MRQ */ +#define IEEE80211_HTCAP_HTC 0x0400 /* +HTC support */ +#define IEEE80211_HTCAP_RDR 0x0800 /* reverse direction responder*/ +/* bits 12-15 reserved */ + +/* + * 802.11n HT Information IE + */ +struct ieee80211_ie_htinfo { + uint8_t hi_id; /* element ID */ + uint8_t hi_len; /* length in bytes */ + uint8_t hi_ctrlchannel; /* primary channel */ + uint8_t hi_byte1; /* ht ie byte 1 */ + uint16_t hi_byte23; /* ht ie bytes 2+3 */ + uint16_t hi_byte45; /* ht ie bytes 4+5 */ + uint8_t hi_basicmcsset[16]; /* basic MCS set */ +} __packed; + +/* byte1 */ +#define IEEE80211_HTINFO_2NDCHAN 0x03 /* secondary/ext chan offset */ +#define IEEE80211_HTINFO_2NDCHAN_S 0 +#define IEEE80211_HTINFO_2NDCHAN_NONE 0x00 /* no secondary/ext channel */ +#define IEEE80211_HTINFO_2NDCHAN_ABOVE 0x01 /* above private channel */ +/* NB: 2 is reserved */ +#define IEEE80211_HTINFO_2NDCHAN_BELOW 0x03 /* below primary channel */ +#define IEEE80211_HTINFO_TXWIDTH 0x04 /* tx channel width */ +#define IEEE80211_HTINFO_TXWIDTH_20 0x00 /* 20MHz width */ +#define IEEE80211_HTINFO_TXWIDTH_2040 0x04 /* any supported width */ +#define IEEE80211_HTINFO_RIFSMODE 0x08 /* Reduced IFS (RIFS) use */ +#define IEEE80211_HTINFO_RIFSMODE_PROH 0x00 /* RIFS use prohibited */ +#define IEEE80211_HTINFO_RIFSMODE_PERM 0x08 /* RIFS use permitted */ +#define IEEE80211_HTINFO_PMSPONLY 0x10 /* PSMP required to associate */ +#define IEEE80211_HTINFO_SIGRAN 0xe0 /* shortest Service Interval */ +#define IEEE80211_HTINFO_SIGRAN_S 5 +#define IEEE80211_HTINFO_SIGRAN_5 0x00 /* 5 ms */ +/* XXX add rest */ + +/* bytes 2+3 */ +#define IEEE80211_HTINFO_OPMODE 0x03 /* operating mode */ +#define IEEE80211_HTINFO_OPMODE_S 0 +#define IEEE80211_HTINFO_OPMODE_PURE 0x00 /* no protection */ +#define IEEE80211_HTINFO_OPMODE_MIXED 0x01 /* protection required */ +#define IEEE80211_HTINFO_OPMODE_PROTOPT 0x02 /* protection optional */ +#define IEEE80211_HTINFO_OPMODE_TBD 0x03 +#define IEEE80211_HTINFO_NONGF_PRESENT 0x04 /* non-GF sta's present */ +#define IEEE80211_HTINFO_TXBL 0x08 /* transmit burst limit */ +#define IEEE80211_HTINFO_NONHT_PRESENT 0x10 /* non-HT sta's present */ +/* bits 5-15 reserved */ + +/* bytes 4+5 */ +#define IEEE80211_HTINFO_2NDARYBEACON 0x01 +#define IEEE80211_HTINFO_LSIGTXOPPROT 0x02 +#define IEEE80211_HTINFO_PCO_ACTIVE 0x04 +#define IEEE80211_HTINFO_40MHZPHASE 0x08 + +/* byte5 */ +#define IEEE80211_HTINFO_BASIC_STBCMCS 0x7f +#define IEEE80211_HTINFO_BASIC_STBCMCS_S 0 +#define IEEE80211_HTINFO_DUALPROTECTED 0x80 + +/* * Management information element payloads. */ @@ -397,34 +649,68 @@ enum { IEEE80211_ELEMID_COUNTRY = 7, IEEE80211_ELEMID_CHALLENGE = 16, /* 17-31 reserved for challenge text extension */ + IEEE80211_ELEMID_PWRCNSTR = 32, + IEEE80211_ELEMID_PWRCAP = 33, + IEEE80211_ELEMID_TPCREQ = 34, + IEEE80211_ELEMID_TPCREP = 35, + IEEE80211_ELEMID_SUPPCHAN = 36, + IEEE80211_ELEMID_CHANSWITCHANN = 37, + IEEE80211_ELEMID_MEASREQ = 38, + IEEE80211_ELEMID_MEASREP = 39, + IEEE80211_ELEMID_QUIET = 40, + IEEE80211_ELEMID_IBSSDFS = 41, IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_HTCAP = 45, IEEE80211_ELEMID_RSN = 48, IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_HTINFO = 61, IEEE80211_ELEMID_TPC = 150, IEEE80211_ELEMID_CCKM = 156, IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ }; struct ieee80211_tim_ie { - u_int8_t tim_ie; /* IEEE80211_ELEMID_TIM */ - u_int8_t tim_len; - u_int8_t tim_count; /* DTIM count */ - u_int8_t tim_period; /* DTIM period */ - u_int8_t tim_bitctl; /* bitmap control */ - u_int8_t tim_bitmap[1]; /* variable-length bitmap */ + uint8_t tim_ie; /* IEEE80211_ELEMID_TIM */ + uint8_t tim_len; + uint8_t tim_count; /* DTIM count */ + uint8_t tim_period; /* DTIM period */ + uint8_t tim_bitctl; /* bitmap control */ + uint8_t tim_bitmap[1]; /* variable-length bitmap */ } __packed; struct ieee80211_country_ie { - u_int8_t ie; /* IEEE80211_ELEMID_COUNTRY */ - u_int8_t len; - u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + uint8_t ie; /* IEEE80211_ELEMID_COUNTRY */ + uint8_t len; + uint8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ struct { - u_int8_t schan; /* starting channel */ - u_int8_t nchan; /* number channels */ - u_int8_t maxtxpwr; /* tx power cap */ + uint8_t schan; /* starting channel */ + uint8_t nchan; /* number channels */ + uint8_t maxtxpwr; /* tx power cap */ } __packed band[4]; /* up to 4 sub bands */ } __packed; +/* + * Atheros advanced capability information element. + */ +struct ieee80211_ath_ie { + uint8_t ath_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t ath_len; /* length in bytes */ + uint8_t ath_oui[3]; /* 0x00, 0x03, 0x7f */ + uint8_t ath_oui_type; /* OUI type */ + uint8_t ath_oui_subtype; /* OUI subtype */ + uint8_t ath_version; /* spec revision */ + uint8_t ath_capability; /* capability info */ +#define ATHEROS_CAP_TURBO_PRIME 0x01 /* dynamic turbo--aka Turbo' */ +#define ATHEROS_CAP_COMPRESSION 0x02 /* data compression */ +#define ATHEROS_CAP_FAST_FRAME 0x04 /* fast (jumbo) frames */ +#define ATHEROS_CAP_XR 0x08 /* Xtended Range support */ +#define ATHEROS_CAP_AR 0x10 /* Advanded Radar support */ +#define ATHEROS_CAP_BURST 0x20 /* Bursting - not negotiated */ +#define ATHEROS_CAP_WME 0x40 /* CWMin tuning */ +#define ATHEROS_CAP_BOOST 0x80 /* use turbo/!turbo mode */ + uint8_t ath_defkeyix[2]; +} __packed; + #define IEEE80211_CHALLENGE_LEN 128 #define IEEE80211_RATE_BASIC 0x80 @@ -435,16 +721,14 @@ struct ieee80211_country_ie { #define IEEE80211_ERP_USE_PROTECTION 0x02 #define IEEE80211_ERP_LONG_PREAMBLE 0x04 -/* Atheros private advanced capabilities info */ -#define ATHEROS_CAP_TURBO_PRIME 0x01 -#define ATHEROS_CAP_COMPRESSION 0x02 -#define ATHEROS_CAP_FAST_FRAME 0x04 -/* bits 3-6 reserved */ -#define ATHEROS_CAP_BOOST 0x80 - -#define ATH_OUI 0x7f0300 /* Atheros OUI */ +#define ATH_OUI 0x7f0300 /* Atheros OUI */ #define ATH_OUI_TYPE 0x01 -#define ATH_OUI_VERSION 0x01 +#define ATH_OUI_SUBTYPE 0x01 +#define ATH_OUI_VERSION 0x00 + +#define BCM_OUI 0x4c9000 /* Broadcom OUI */ +#define BCM_OUI_HTCAP 51 /* pre-draft HTCAP ie */ +#define BCM_OUI_HTINFO 52 /* pre-draft HTINFO ie */ #define WPA_OUI 0xf25000 #define WPA_OUI_TYPE 0x01 @@ -499,7 +783,7 @@ struct ieee80211_country_ie { * octet chal.text[253] */ -typedef u_int8_t *ieee80211_mgt_auth_t; +typedef uint8_t *ieee80211_mgt_auth_t; #define IEEE80211_AUTH_ALGORITHM(auth) \ ((auth)[0] | ((auth)[1] << 8)) @@ -654,4 +938,69 @@ enum { #define IEEE80211_HWBMISS_MIN 1 #define IEEE80211_HWBMISS_MAX 255 +/* + * 802.11 frame duration definitions. + */ + +struct ieee80211_duration { + uint16_t d_rts_dur; + uint16_t d_data_dur; + uint16_t d_plcp_len; + uint8_t d_residue; /* unused octets in time slot */ +}; + +/* One Time Unit (TU) is 1Kus = 1024 microseconds. */ +#define IEEE80211_DUR_TU 1024 + +/* IEEE 802.11b durations for DSSS PHY in microseconds */ +#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 +#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 + +#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 +#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 +#define IEEE80211_DUR_DS_SLOW_ACK 112 +#define IEEE80211_DUR_DS_FAST_ACK 56 +#define IEEE80211_DUR_DS_SLOW_CTS 112 +#define IEEE80211_DUR_DS_FAST_CTS 56 + +#define IEEE80211_DUR_DS_SLOT 20 +#define IEEE80211_DUR_DS_SIFS 10 +#define IEEE80211_DUR_DS_PIFS (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SLOT) +#define IEEE80211_DUR_DS_DIFS (IEEE80211_DUR_DS_SIFS + \ + 2 * IEEE80211_DUR_DS_SLOT) +#define IEEE80211_DUR_DS_EIFS (IEEE80211_DUR_DS_SIFS + \ + IEEE80211_DUR_DS_SLOW_ACK + \ + IEEE80211_DUR_DS_LONG_PREAMBLE + \ + IEEE80211_DUR_DS_SLOW_PLCPHDR + \ + IEEE80211_DUR_DIFS) + +/* + * Atheros fast-frame encapsulation format. + * FF max payload: + * 802.2 + FFHDR + HPAD + 802.3 + 802.2 + 1500 + SPAD + 802.3 + 802.2 + 1500: + * 8 + 4 + 4 + 14 + 8 + 1500 + 6 + 14 + 8 + 1500 + * = 3066 + */ +/* fast frame header is 32-bits */ +#define ATH_FF_PROTO 0x0000003f /* protocol */ +#define ATH_FF_PROTO_S 0 +#define ATH_FF_FTYPE 0x000000c0 /* frame type */ +#define ATH_FF_FTYPE_S 6 +#define ATH_FF_HLEN32 0x00000300 /* optional hdr length */ +#define ATH_FF_HLEN32_S 8 +#define ATH_FF_SEQNUM 0x001ffc00 /* sequence number */ +#define ATH_FF_SEQNUM_S 10 +#define ATH_FF_OFFSET 0xffe00000 /* offset to 2nd payload */ +#define ATH_FF_OFFSET_S 21 + +#define ATH_FF_MAX_HDR_PAD 4 +#define ATH_FF_MAX_SEP_PAD 6 +#define ATH_FF_MAX_HDR 30 + +#define ATH_FF_PROTO_L2TUNNEL 0 /* L2 tunnel protocol */ +#define ATH_FF_ETH_TYPE 0x88bd /* Ether type for encapsulated frames */ +#define ATH_FF_SNAP_ORGCODE_0 0x00 +#define ATH_FF_SNAP_ORGCODE_1 0x03 +#define ATH_FF_SNAP_ORGCODE_2 0x7f + #endif /* _NET80211_IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c index 9d771a5a402b..c5305d055e93 100644 --- a/sys/net80211/ieee80211_acl.c +++ b/sys/net80211/ieee80211_acl.c @@ -64,7 +64,7 @@ enum { struct acl { TAILQ_ENTRY(acl) acl_list; LIST_ENTRY(acl) acl_hash; - u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; + uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; }; struct aclstate { acl_lock_t as_lock; @@ -77,7 +77,7 @@ struct aclstate { /* simple hash is enough for variation of macaddr */ #define ACL_HASH(addr) \ - (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); @@ -112,7 +112,7 @@ acl_detach(struct ieee80211com *ic) } static __inline struct acl * -_find_acl(struct aclstate *as, const u_int8_t *macaddr) +_find_acl(struct aclstate *as, const uint8_t *macaddr) { struct acl *acl; int hash; @@ -137,7 +137,7 @@ _acl_free(struct aclstate *as, struct acl *acl) } static int -acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct aclstate *as = ic->ic_as; @@ -153,7 +153,7 @@ acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) } static int -acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct aclstate *as = ic->ic_as; struct acl *acl, *new; @@ -191,7 +191,7 @@ acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) } static int -acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct aclstate *as = ic->ic_as; struct acl *acl; diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index b54fd2a62928..5b34768b4e6b 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -64,11 +64,11 @@ __FBSDID("$FreeBSD$"); void ieee80211_amrr_init(struct ieee80211_amrr *amrr, - struct ieee80211com *ic, int min, int max) + struct ieee80211com *ic, int amin, int amax) { /* XXX bounds check? */ - amrr->amrr_min_success_threshold = min; - amrr->amrr_max_success_threshold = max; + amrr->amrr_min_success_threshold = amin; + amrr->amrr_max_success_threshold = amax; amrr->amrr_ic = ic; } diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 3d6d22f5f53d..83d7c3f968c1 100644 --- a/sys/net80211/ieee80211_crypto.c +++ b/sys/net80211/ieee80211_crypto.c @@ -84,7 +84,7 @@ null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) } static int null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, - const u_int8_t mac[IEEE80211_ADDR_LEN]) + const uint8_t mac[IEEE80211_ADDR_LEN]) { return 1; } @@ -125,7 +125,7 @@ dev_key_delete(struct ieee80211com *ic, static __inline int dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key, - const u_int8_t mac[IEEE80211_ADDR_LEN]) + const uint8_t mac[IEEE80211_ADDR_LEN]) { return ic->ic_crypto.cs_key_set(ic, key, mac); } @@ -461,7 +461,7 @@ ieee80211_crypto_delglobalkeys(struct ieee80211com *ic) */ int ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key, - const u_int8_t macaddr[IEEE80211_ADDR_LEN]) + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { const struct ieee80211_cipher *cip = key->wk_cipher; @@ -505,7 +505,7 @@ ieee80211_crypto_encap(struct ieee80211com *ic, struct ieee80211_key *k; struct ieee80211_frame *wh; const struct ieee80211_cipher *cip; - u_int8_t keyid; + uint8_t keyid; /* * Multicast traffic always uses the multicast key. @@ -549,8 +549,8 @@ ieee80211_crypto_decap(struct ieee80211com *ic, struct ieee80211_key *k; struct ieee80211_frame *wh; const struct ieee80211_cipher *cip; - const u_int8_t *ivp; - u_int8_t keyid; + const uint8_t *ivp; + uint8_t keyid; /* NB: this minimum size data frame could be bigger */ if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) { @@ -568,7 +568,7 @@ ieee80211_crypto_decap(struct ieee80211com *ic, * the key id in the header is meaningless (typically 0). */ wh = mtod(m, struct ieee80211_frame *); - ivp = mtod(m, const u_int8_t *) + hdrlen; /* XXX contig */ + ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */ keyid = ivp[IEEE80211_WEP_IVLEN]; if (IEEE80211_IS_MULTICAST(wh->i_addr1) || IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) @@ -586,7 +586,7 @@ ieee80211_crypto_decap(struct ieee80211com *ic, "[%s] unable to pullup %s header\n", ether_sprintf(wh->i_addr2), cip->ic_name); ic->ic_stats.is_rx_wepfail++; /* XXX */ - return 0; + return NULL; } return (cip->ic_decap(k, m, hdrlen) ? k : NULL); diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h index ed12b06912ff..ee99caa19000 100644 --- a/sys/net80211/ieee80211_crypto.h +++ b/sys/net80211/ieee80211_crypto.h @@ -39,7 +39,7 @@ */ struct ieee80211_wepkey { u_int wk_len; /* key length in bytes */ - u_int8_t wk_key[IEEE80211_KEYBUF_SIZE]; + uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; }; struct ieee80211_cipher; @@ -60,12 +60,12 @@ struct ieee80211_cipher; * Ciphers such as TKIP may also support mixed hardware/software * encrypt/decrypt and MIC processing. */ -typedef u_int16_t ieee80211_keyix; /* h/w key index */ +typedef uint16_t ieee80211_keyix; /* h/w key index */ struct ieee80211_key { - u_int8_t wk_keylen; /* key length in bytes */ - u_int8_t wk_pad; - u_int16_t wk_flags; + uint8_t wk_keylen; /* key length in bytes */ + uint8_t wk_pad; + uint16_t wk_flags; #define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */ #define IEEE80211_KEY_RECV 0x02 /* key used for recv */ #define IEEE80211_KEY_GROUP 0x04 /* key used for WPA group operation */ @@ -73,11 +73,11 @@ struct ieee80211_key { #define IEEE80211_KEY_SWMIC 0x20 /* host-based enmic/demic */ ieee80211_keyix wk_keyix; /* h/w key index */ ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ - u_int8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; + uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; #define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */ #define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */ - u_int64_t wk_keyrsc; /* key receive sequence counter */ - u_int64_t wk_keytsc; /* key transmit sequence counter */ + uint64_t wk_keyrsc; /* key receive sequence counter */ + uint64_t wk_keytsc; /* key transmit sequence counter */ const struct ieee80211_cipher *wk_cipher; void *wk_private; /* private cipher state */ }; @@ -116,7 +116,7 @@ struct mbuf; struct ieee80211_crypto_state { struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID]; ieee80211_keyix cs_def_txkey; /* default/group tx key index */ - u_int16_t cs_max_keyix; /* max h/w key index */ + uint16_t cs_max_keyix; /* max h/w key index */ int (*cs_key_alloc)(struct ieee80211com *, const struct ieee80211_key *, @@ -125,7 +125,7 @@ struct ieee80211_crypto_state { const struct ieee80211_key *); int (*cs_key_set)(struct ieee80211com *, const struct ieee80211_key *, - const u_int8_t mac[IEEE80211_ADDR_LEN]); + const uint8_t mac[IEEE80211_ADDR_LEN]); void (*cs_key_update_begin)(struct ieee80211com *); void (*cs_key_update_end)(struct ieee80211com *); }; @@ -137,7 +137,7 @@ int ieee80211_crypto_newkey(struct ieee80211com *, int ieee80211_crypto_delkey(struct ieee80211com *, struct ieee80211_key *); int ieee80211_crypto_setkey(struct ieee80211com *, - struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]); + struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); void ieee80211_crypto_delglobalkeys(struct ieee80211com *); /* @@ -156,7 +156,7 @@ struct ieee80211_cipher { void (*ic_detach)(struct ieee80211_key *); int (*ic_setkey)(struct ieee80211_key *); int (*ic_encap)(struct ieee80211_key *, struct mbuf *, - u_int8_t keyid); + uint8_t keyid); int (*ic_decap)(struct ieee80211_key *, struct mbuf *, int); int (*ic_enmic)(struct ieee80211_key *, struct mbuf *, int); int (*ic_demic)(struct ieee80211_key *, struct mbuf *, int); diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c index 0965de8e8e2d..525fe9adcaa0 100644 --- a/sys/net80211/ieee80211_crypto_ccmp.c +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -60,7 +60,7 @@ struct ccmp_ctx { static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *); static void ccmp_detach(struct ieee80211_key *); static int ccmp_setkey(struct ieee80211_key *); -static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, u_int8_t keyid); +static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid); static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int); static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int); static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int); @@ -134,11 +134,11 @@ ccmp_setkey(struct ieee80211_key *k) * Add privacy headers appropriate for the specified key. */ static int -ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) { struct ccmp_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->cc_ic; - u_int8_t *ivp; + uint8_t *ivp; int hdrlen; hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); @@ -149,7 +149,7 @@ ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) M_PREPEND(m, ccmp.ic_header, M_NOWAIT); if (m == NULL) return 0; - ivp = mtod(m, u_int8_t *); + ivp = mtod(m, uint8_t *); ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen); ivp += hdrlen; @@ -244,7 +244,7 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) /* * Copy up 802.11 header and strip crypto bits. */ - ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen); + ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen); m_adj(m, ccmp.ic_header); m_adj(m, -ccmp.ic_trailer); @@ -350,7 +350,7 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, b0[1] = aad[30]; aad[1] = 22 + IEEE80211_ADDR_LEN + 2; } else { - *(u_int16_t *)&aad[30] = 0; + *(uint16_t *)&aad[30] = 0; b0[1] = 0; aad[1] = 22 + IEEE80211_ADDR_LEN; } @@ -363,12 +363,12 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, b0[1] = aad[24]; aad[1] = 22 + 2; } else { - *(u_int16_t *)&aad[24] = 0; + *(uint16_t *)&aad[24] = 0; b0[1] = 0; aad[1] = 22; } - *(u_int16_t *)&aad[26] = 0; - *(u_int32_t *)&aad[28] = 0; + *(uint16_t *)&aad[26] = 0; + *(uint32_t *)&aad[28] = 0; } /* Start with the first block and AAD */ @@ -629,32 +629,4 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen /* * Module glue. */ -static int -ccmp_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - ieee80211_crypto_register(&ccmp); - return 0; - case MOD_UNLOAD: - case MOD_QUIESCE: - if (nrefs) { - printf("wlan_ccmp: still in use (%u dynamic refs)\n", - nrefs); - return EBUSY; - } - if (type == MOD_UNLOAD) - ieee80211_crypto_unregister(&ccmp); - return 0; - } - return EINVAL; -} - -static moduledata_t ccmp_mod = { - "wlan_ccmp", - ccmp_modevent, - 0 -}; -DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_ccmp, 1); -MODULE_DEPEND(wlan_ccmp, wlan, 1, 1, 1); +IEEE80211_CRYPTO_MODULE(ccmp, 1); diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c index c3be2e46b942..7fbb53d9dee3 100644 --- a/sys/net80211/ieee80211_crypto_none.c +++ b/sys/net80211/ieee80211_crypto_none.c @@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$"); static void *none_attach(struct ieee80211com *, struct ieee80211_key *); static void none_detach(struct ieee80211_key *); static int none_setkey(struct ieee80211_key *); -static int none_encap(struct ieee80211_key *, struct mbuf *, u_int8_t); +static int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t); static int none_decap(struct ieee80211_key *, struct mbuf *, int); static int none_enmic(struct ieee80211_key *, struct mbuf *, int); static int none_demic(struct ieee80211_key *, struct mbuf *, int); @@ -85,7 +85,7 @@ none_setkey(struct ieee80211_key *k) } static int -none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) { struct ieee80211com *ic = k->wk_private; #ifdef IEEE80211_DEBUG @@ -109,7 +109,7 @@ none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) struct ieee80211com *ic = k->wk_private; #ifdef IEEE80211_DEBUG struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); - const u_int8_t *ivp = (const u_int8_t *)&wh[1]; + const uint8_t *ivp = (const uint8_t *)&wh[1]; #endif /* diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c index 05c1532d54f3..327306a298c1 100644 --- a/sys/net80211/ieee80211_crypto_tkip.c +++ b/sys/net80211/ieee80211_crypto_tkip.c @@ -52,7 +52,7 @@ __FBSDID("$FreeBSD$"); static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *); static void tkip_detach(struct ieee80211_key *); static int tkip_setkey(struct ieee80211_key *); -static int tkip_encap(struct ieee80211_key *, struct mbuf *m, u_int8_t keyid); +static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid); static int tkip_enmic(struct ieee80211_key *, struct mbuf *, int); static int tkip_decap(struct ieee80211_key *, struct mbuf *, int); static int tkip_demic(struct ieee80211_key *, struct mbuf *, int); @@ -150,11 +150,11 @@ tkip_setkey(struct ieee80211_key *k) * Add privacy headers and do any s/w encryption required. */ static int -tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) { struct tkip_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->tc_ic; - u_int8_t *ivp; + uint8_t *ivp; int hdrlen; /* @@ -179,7 +179,7 @@ tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) M_PREPEND(m, tkip.ic_header, M_NOWAIT); if (m == NULL) return 0; - ivp = mtod(m, u_int8_t *); + ivp = mtod(m, uint8_t *); memmove(ivp, ivp + tkip.ic_header, hdrlen); ivp += hdrlen; @@ -970,32 +970,4 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, /* * Module glue. */ -static int -tkip_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - ieee80211_crypto_register(&tkip); - return 0; - case MOD_UNLOAD: - case MOD_QUIESCE: - if (nrefs) { - printf("wlan_tkip: still in use (%u dynamic refs)\n", - nrefs); - return EBUSY; - } - if (type == MOD_UNLOAD) - ieee80211_crypto_unregister(&tkip); - return 0; - } - return EINVAL; -} - -static moduledata_t tkip_mod = { - "wlan_tkip", - tkip_modevent, - 0 -}; -DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_tkip, 1); -MODULE_DEPEND(wlan_tkip, wlan, 1, 1, 1); +IEEE80211_CRYPTO_MODULE(tkip, 1); diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c index f6888b28b856..81d15cc20326 100644 --- a/sys/net80211/ieee80211_crypto_wep.c +++ b/sys/net80211/ieee80211_crypto_wep.c @@ -48,7 +48,7 @@ __FBSDID("$FreeBSD$"); static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); static void wep_detach(struct ieee80211_key *); static int wep_setkey(struct ieee80211_key *); -static int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid); +static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid); static int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen); static int wep_enmic(struct ieee80211_key *, struct mbuf *, int); static int wep_demic(struct ieee80211_key *, struct mbuf *, int); @@ -73,7 +73,7 @@ static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); struct wep_ctx { struct ieee80211com *wc_ic; /* for diagnostics */ - u_int32_t wc_iv; /* initial vector for crypto */ + uint32_t wc_iv; /* initial vector for crypto */ }; /* number of references from net80211 layer */ @@ -117,12 +117,12 @@ wep_setkey(struct ieee80211_key *k) * Add privacy headers appropriate for the specified key. */ static int -wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) { struct wep_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->wc_ic; - u_int32_t iv; - u_int8_t *ivp; + uint32_t iv; + uint8_t *ivp; int hdrlen; hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); @@ -133,7 +133,7 @@ wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) M_PREPEND(m, wep.ic_header, M_NOWAIT); if (m == NULL) return 0; - ivp = mtod(m, u_int8_t *); + ivp = mtod(m, uint8_t *); ovbcopy(ivp + wep.ic_header, ivp, hdrlen); ivp += hdrlen; @@ -229,7 +229,7 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) /* * Copy up 802.11 header and strip crypto bits. */ - ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen); + ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen); m_adj(m, wep.ic_header); m_adj(m, -wep.ic_trailer); @@ -306,7 +306,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) struct wep_ctx *ctx = key->wk_private; struct mbuf *m = m0; - u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; uint8_t icv[IEEE80211_WEP_CRCLEN]; uint32_t i, j, k, crc; size_t buflen, data_len; @@ -317,7 +317,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) ctx->wc_ic->ic_stats.is_crypto_wep++; /* NB: this assumes the header was pulled up */ - memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); /* Setup RC4 state */ @@ -388,7 +388,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) struct wep_ctx *ctx = key->wk_private; struct mbuf *m = m0; - u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; uint8_t icv[IEEE80211_WEP_CRCLEN]; uint32_t i, j, k, crc; size_t buflen, data_len; @@ -399,7 +399,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) ctx->wc_ic->ic_stats.is_crypto_wep++; /* NB: this assumes the header was pulled up */ - memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); /* Setup RC4 state */ @@ -473,32 +473,4 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) /* * Module glue. */ -static int -wep_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - ieee80211_crypto_register(&wep); - return 0; - case MOD_UNLOAD: - case MOD_QUIESCE: - if (nrefs) { - printf("wlan_wep: still in use (%u dynamic refs)\n", - nrefs); - return EBUSY; - } - if (type == MOD_UNLOAD) - ieee80211_crypto_unregister(&wep); - return 0; - } - return EINVAL; -} - -static moduledata_t wep_mod = { - "wlan_wep", - wep_modevent, - 0 -}; -DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_wep, 1); -MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); +IEEE80211_CRYPTO_MODULE(wep, 1); diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index c51bb699966d..d75b2d284982 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -169,6 +169,15 @@ ieee80211_drain_ifq(struct ifqueue *ifq) } /* + * As above, for mbufs allocated with m_gethdr/MGETHDR + * or initialized by M_COPY_PKTHDR. + */ +#define MC_ALIGN(m, len) \ +do { \ + (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \ +} while (/* CONSTCOND */ 0) + +/* * Allocate and setup a management frame of the specified * size. We return the mbuf and a pointer to the start * of the contiguous data area that's been reserved based @@ -178,7 +187,7 @@ ieee80211_drain_ifq(struct ifqueue *ifq) * can use this interface too. */ struct mbuf * -ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) +ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) { struct mbuf *m; u_int len; @@ -187,8 +196,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) * NB: we know the mbuf routines will align the data area * so we don't need to do anything special. */ - /* XXX 4-address frame? */ - len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); + len = roundup2(headroom + pktlen, 4); KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); if (len < MINCLSIZE) { m = m_gethdr(M_NOWAIT, MT_DATA); @@ -200,8 +208,11 @@ ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) */ if (m != NULL) MH_ALIGN(m, len); - } else + } else { m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m != NULL) + MC_ALIGN(m, len); + } if (m != NULL) { m->m_data += sizeof(struct ieee80211_frame); *frm = m->m_data; @@ -209,18 +220,51 @@ ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) return m; } +int +ieee80211_add_callback(struct mbuf *m, + void (*func)(struct ieee80211_node *, void *, int), void *arg) +{ + struct m_tag *mtag; + struct ieee80211_cb *cb; + + mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, + sizeof(struct ieee80211_cb), M_NOWAIT); + if (mtag == NULL) + return 0; + + cb = (struct ieee80211_cb *)(mtag+1); + cb->func = func; + cb->arg = arg; + m_tag_prepend(m, mtag); + m->m_flags |= M_TXCB; + return 1; +} + +void +ieee80211_process_callback(struct ieee80211_node *ni, + struct mbuf *m, int status) +{ + struct m_tag *mtag; + + mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL); + if (mtag != NULL) { + struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1); + cb->func(ni, cb->arg, status); + } +} + #include <sys/libkern.h> void get_random_bytes(void *p, size_t n) { - u_int8_t *dp = p; + uint8_t *dp = p; while (n > 0) { - u_int32_t v = arc4random(); - size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n; - bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n); - dp += sizeof(u_int32_t), n -= nb; + uint32_t v = arc4random(); + size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n; + bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n); + dp += sizeof(uint32_t), n -= nb; } } diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index dc4cd9502252..4b9fc7057d41 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -29,6 +29,18 @@ #ifdef _KERNEL /* + * Common state locking definitions. + */ +typedef struct mtx ieee80211_com_lock_t; +#define IEEE80211_LOCK_INIT(_ic, _name) \ + mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF) +#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock) +#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock) +#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock) +#define IEEE80211_LOCK_ASSERT(_ic) \ + mtx_assert(&(_ic)->ic_comlock, MA_OWNED) + +/* * Beacon locking definitions. */ typedef struct mtx ieee80211_beacon_lock_t; @@ -60,7 +72,7 @@ typedef struct mtx ieee80211_node_lock_t; */ typedef struct mtx ieee80211_scan_lock_t; #define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \ - mtx_init(&(_nt)->nt_scanlock, _name, "802.11 scangen", MTX_DEF) + mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF) #define IEEE80211_SCAN_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_scanlock) #define IEEE80211_SCAN_LOCK(_nt) mtx_lock(&(_nt)->nt_scanlock) #define IEEE80211_SCAN_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_scanlock) @@ -165,10 +177,21 @@ int ieee80211_node_dectestref(struct ieee80211_node *ni); struct ifqueue; void ieee80211_drain_ifq(struct ifqueue *); -struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen); +#define msecs_to_ticks(ms) ((ms)*1000/hz) +#define time_after(a,b) ((long)(b) - (long)(a) < 0) +#define time_before(a,b) time_after(b,a) +#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) +#define time_before_eq(a,b) time_after_eq(b,a) + +struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); +/* tx path usage */ #define M_LINK0 M_PROTO1 /* WEP requested */ #define M_PWR_SAV M_PROTO4 /* bypass PS handling */ #define M_MORE_DATA M_PROTO5 /* more data frames to follow */ +#define M_FF 0x20000 /* fast frame */ +#define M_TXCB 0x40000 /* do tx complete callback */ +/* rx path usage */ +#define M_AMPDU M_PROTO1 /* A-MPDU processing done */ /* * Encode WME access control bits in the PROTO flags. * This is safe since it's passed directly in to the @@ -193,6 +216,17 @@ struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen); #define M_AGE_GET(m) (m->m_pkthdr.csum_data) #define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj) +#define MTAG_ABI_NET80211 1132948340 /* net80211 ABI */ + +struct ieee80211_cb { + void (*func)(struct ieee80211_node *, void *, int status); + void *arg; +}; +#define NET80211_TAG_CALLBACK 0 /* xmit complete callback */ +int ieee80211_add_callback(struct mbuf *m, + void (*func)(struct ieee80211_node *, void *, int), void *arg); +void ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int); + void get_random_bytes(void *, size_t); struct ieee80211com; @@ -201,6 +235,36 @@ void ieee80211_sysctl_attach(struct ieee80211com *); void ieee80211_sysctl_detach(struct ieee80211com *); void ieee80211_load_module(const char *); + +#define IEEE80211_CRYPTO_MODULE(name, version) \ +static int \ +name##_modevent(module_t mod, int type, void *unused) \ +{ \ + switch (type) { \ + case MOD_LOAD: \ + ieee80211_crypto_register(&name); \ + return 0; \ + case MOD_UNLOAD: \ + case MOD_QUIESCE: \ + if (nrefs) { \ + printf("wlan_##name: still in use (%u dynamic refs)\n",\ + nrefs); \ + return EBUSY; \ + } \ + if (type == MOD_UNLOAD) \ + ieee80211_crypto_unregister(&name); \ + return 0; \ + } \ + return EINVAL; \ +} \ +static moduledata_t name##_mod = { \ + "wlan_" #name, \ + name##_modevent, \ + 0 \ +}; \ +DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\ +MODULE_VERSION(wlan_##name, version); \ +MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1) #endif /* _KERNEL */ /* XXX this stuff belongs elsewhere */ diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c new file mode 100644 index 000000000000..9b6223cf2b24 --- /dev/null +++ b/sys/net80211/ieee80211_ht.c @@ -0,0 +1,1472 @@ +/*- + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11n protocol support. + */ + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/endian.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +/* define here, used throughout file */ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) +#define SM(_v, _f) (((_v) << _f##_S) & _f) + +/* XXX need max array size */ +const int ieee80211_htrates[16] = { + 13, /* IFM_IEEE80211_MCS0 */ + 26, /* IFM_IEEE80211_MCS1 */ + 39, /* IFM_IEEE80211_MCS2 */ + 52, /* IFM_IEEE80211_MCS3 */ + 78, /* IFM_IEEE80211_MCS4 */ + 104, /* IFM_IEEE80211_MCS5 */ + 117, /* IFM_IEEE80211_MCS6 */ + 130, /* IFM_IEEE80211_MCS7 */ + 26, /* IFM_IEEE80211_MCS8 */ + 52, /* IFM_IEEE80211_MCS9 */ + 78, /* IFM_IEEE80211_MCS10 */ + 104, /* IFM_IEEE80211_MCS11 */ + 156, /* IFM_IEEE80211_MCS12 */ + 208, /* IFM_IEEE80211_MCS13 */ + 234, /* IFM_IEEE80211_MCS14 */ + 260, /* IFM_IEEE80211_MCS15 */ +}; + +static const struct ieee80211_htrateset ieee80211_rateset_11n = + { 16, { + /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + /* 39 52 78 104 117, 130 */ + 10, 11, 12, 13, 14, 15 } + }; + +#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250) +#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000) +#define IEEE80211_AGGR_MAXTRIES 3 + +static int ieee80211_addba_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout); +static int ieee80211_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int code, int baparamset, int batimeout); +static void ieee80211_addba_stop(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); +static void ieee80211_aggr_recv_action(struct ieee80211_node *ni, + const uint8_t *frm, const uint8_t *efrm); + +void +ieee80211_ht_attach(struct ieee80211com *ic) +{ + + ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; + ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; + ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; + + ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; + + /* setup default aggregation policy */ + ic->ic_recv_action = ieee80211_aggr_recv_action; + ic->ic_send_action = ieee80211_send_action; + ic->ic_addba_request = ieee80211_addba_request; + ic->ic_addba_response = ieee80211_addba_response; + ic->ic_addba_stop = ieee80211_addba_stop; + + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || + isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { + /* + * There are HT channels in the channel list; enable + * all HT-related facilities by default. + * XXX these choices may be too aggressive. + */ + ic->ic_flags_ext |= IEEE80211_FEXT_HT + | IEEE80211_FEXT_HTCOMPAT + ; + if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; + /* XXX infer from channel list */ + if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; + if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; + } + /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; + if (ic->ic_htcaps & IEEE80211_HTC_AMPDU) + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; + if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; + } +} + +void +ieee80211_ht_detach(struct ieee80211com *ic) +{ +} + +static void +ht_announce(struct ieee80211com *ic, int mode, + const struct ieee80211_htrateset *rs) +{ + struct ifnet *ifp = ic->ic_ifp; + int i, rate, mword; + + if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); + for (i = 0; i < rs->rs_nrates; i++) { + mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); + if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) + continue; + rate = ieee80211_htrates[rs->rs_rates[i]]; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); +} + +void +ieee80211_ht_announce(struct ieee80211com *ic) +{ + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) + ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) + ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); +} + +const struct ieee80211_htrateset * +ieee80211_get_suphtrates(struct ieee80211com *ic, + const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_HT(c)) + return &ieee80211_rateset_11n; + /* XXX what's the right thing to do here? */ + return (const struct ieee80211_htrateset *) + ieee80211_get_suprates(ic, c); +} + +/* + * Receive processing. + */ + +/* + * Decap the encapsulated A-MSDU frames and dispatch all but + * the last for delivery. The last frame is returned for + * delivery via the normal path. + */ +struct mbuf * +ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211com *ic = ni->ni_ic; + int totallen, framelen; + struct mbuf *n; + + /* discard 802.3 header inserted by ieee80211_decap */ + m_adj(m, sizeof(struct ether_header)); + + ic->ic_stats.is_amsdu_decap++; + + totallen = m->m_pkthdr.len; + for (;;) { + /* + * Decap the first frame, bust it apart from the + * remainder and deliver. We leave the last frame + * delivery to the caller (for consistency with other + * code paths, could also do it here). + */ + m = ieee80211_decap1(m, &framelen); + if (m == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "a-msdu", "%s", "first decap failed"); + ic->ic_stats.is_amsdu_tooshort++; + return NULL; + } + if (framelen == totallen) + break; + n = m_split(m, framelen, M_NOWAIT); + if (n == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "a-msdu", + "%s", "unable to split encapsulated frames"); + ic->ic_stats.is_amsdu_split++; + m_freem(m); /* NB: must reclaim */ + return NULL; + } + ieee80211_deliver_data(ic, ni, m); + + /* + * Remove frame contents; each intermediate frame + * is required to be aligned to a 4-byte boundary. + */ + m = n; + m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ + } + return m; /* last delivered by caller */ +} + +/* + * Start A-MPDU rx/re-order processing for the specified TID. + */ +static void +ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) +{ + memset(rap, 0, sizeof(*rap)); + rap->rxa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + rap->rxa_start = start; + rap->rxa_nxt = rap->rxa_start; + rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; +} + +/* + * Purge all frames in the A-MPDU re-order queue. + */ +static void +ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) +{ + struct mbuf *m; + int i; + + for (i = 0; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m != NULL) { + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + m_freem(m); + if (--rap->rxa_qframes == 0) + break; + } + } + KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, + ("lost %u data, %u frames on ampdu rx q", + rap->rxa_qbytes, rap->rxa_qframes)); +} + +/* + * Stop A-MPDU rx processing for the specified TID. + */ +static void +ampdu_rx_stop(struct ieee80211_rx_ampdu *rap) +{ + rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND; + ampdu_rx_purge(rap); +} + +/* + * Dispatch a frame from the A-MPDU reorder queue. The + * frame is fed back into ieee80211_input marked with an + * M_AMPDU flag so it doesn't come back to us (it also + * permits ieee80211_input to optimize re-processing). + */ +static __inline void +ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) +{ + m->m_flags |= M_AMPDU; /* bypass normal processing */ + /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */ + (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0); +} + +/* + * Dispatch as many frames as possible from the re-order queue. + * Frames will always be "at the front"; we process all frames + * up to the first empty slot in the window. On completion we + * cleanup state if there are still pending frames in the current + * BA window. We assume the frame at slot 0 is already handled + * by the caller; we always start at slot 1. + */ +static void +ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + int i; + + /* flush run of frames */ + for (i = 1; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m == NULL) + break; + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + + ampdu_dispatch(ni, m); + } + /* + * Adjust the start of the BA window to + * reflect the frames just dispatched. + */ + rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); + rap->rxa_nxt = rap->rxa_start; + ic->ic_stats.is_ampdu_rx_oor += i; + /* + * If frames remain, copy the mbuf pointers down so + * they correspond to the offsets in the new window. + */ + if (rap->rxa_qframes != 0) { + int n = rap->rxa_qframes, j; + for (j = i+1; j < rap->rxa_wnd; j++) { + if (rap->rxa_m[j] != NULL) { + rap->rxa_m[j-i] = rap->rxa_m[j]; + rap->rxa_m[j] = NULL; + if (--n == 0) + break; + } + } + KASSERT(n == 0, ("lost %d frames", n)); + ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + } +} + +/* + * Dispatch all frames in the A-MPDU + * re-order queue up to the specified slot. + */ +static void +ampdu_rx_flush(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, int limit) +{ + struct mbuf *m; + int i; + + for (i = 0; i < limit; i++) { + m = rap->rxa_m[i]; + if (m == NULL) + continue; + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + ampdu_dispatch(ni, m); + if (--rap->rxa_qframes == 0) + break; + } +} + +/* + * Process a received QoS data frame for an HT station. Handle + * A-MPDU reordering: if this frame is received out of order + * and falls within the BA window hold onto it. Otherwise if + * this frame completes a run flush any pending frames. We + * return 1 if the frame is consumed. A 0 is returned if + * the frame should be processed normally by the caller. + */ +int +ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) +{ +#define IEEE80211_FC0_QOSDATA \ + (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_qosframe *wh; + struct ieee80211_rx_ampdu *rap; + ieee80211_seq rxseq; + uint8_t tid; + int off; + + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); + + /* NB: m_len known to be sufficient */ + wh = mtod(m, struct ieee80211_qosframe *); + KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data")); + + /* XXX 4-address frame */ + tid = wh->i_qos[0] & IEEE80211_QOS_TID; + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { + /* + * No ADDBA request yet, don't touch. + */ + return 0; + } + rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + if (rxseq == rap->rxa_start) { + /* + * First frame in window. + */ + if (rap->rxa_qframes != 0) { + /* + * Dispatch as many packets as we can. + */ + KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); + ampdu_dispatch(ni, m); + ampdu_rx_dispatch(rap, ni); + return 1; /* NB: consumed */ + } else { + /* + * In order; advance window and notify + * caller to dispatch directly. + */ + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + rap->rxa_nxt = rap->rxa_start; + return 0; /* NB: process packet */ + } + } + /* + * This packet is out of order; store it + * if it's in the BA window. + */ + /* calculate offset in BA window */ + off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); + if (off >= rap->rxa_wnd) { + /* + * Outside the window, clear the q and start over. + * + * NB: this handles the case where rxseq is before + * rxa_start because our max BA window is 64 + * and the sequence number range is 4096. + */ + IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + "flush BA win <%u:%u> (%u frames) rxseq %u tid %u", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), + rap->rxa_qframes, rxseq, tid); + + if (rap->rxa_qframes != 0) { + ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; + ampdu_rx_flush(ni, rap, rap->rxa_wnd); + KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, + ("lost %u data, %u frames on ampdu rx q", + rap->rxa_qbytes, rap->rxa_qframes)); + } + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + rap->rxa_nxt = rap->rxa_start; + return 0; /* NB: process packet */ + } + if (rap->rxa_qframes != 0) { +#if 0 + /* XXX honor batimeout? */ + if (ticks - mn->mn_age[tid] > 50) { + /* + * Too long since we received the first frame; flush. + */ + if (rap->rxa_qframes != 0) { + ic->ic_stats.is_ampdu_rx_oor += + rap->rxa_qframes; + ampdu_rx_flush(ni, rap, rap->rxa_wnd); + } + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + rap->rxa_nxt = rap->rxa_start; + return 0; /* NB: process packet */ + } +#endif + rap->rxa_nxt = rxseq; + } else { + /* + * First frame, start aging timer. + */ +#if 0 + mn->mn_age[tid] = ticks; +#endif + } + /* save packet */ + if (rap->rxa_m[off] == NULL) { + rap->rxa_m[off] = m; + rap->rxa_qframes++; + rap->rxa_qbytes += m->m_pkthdr.len; + } else { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, + ni->ni_macaddr, "a-mpdu duplicate", + "seqno %u tid %u BA win <%u:%u>", + rxseq, tid, rap->rxa_start, rap->rxa_wnd); + ic->ic_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + m_freem(m); + } + return 1; /* NB: consumed */ +#undef IEEE80211_FC0_QOSDATA +} + +/* + * Process a BAR ctl frame. Dispatch all frames up to + * the sequence number of the frame. If this frame is + * out of the window it's discarded. + */ +void +ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame_bar *wh; + struct ieee80211_rx_ampdu *rap; + ieee80211_seq rxseq; + int tid, off; + + wh = mtod(m0, struct ieee80211_frame_bar *); + /* XXX check basic BAR */ + tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { + /* + * No ADDBA request yet, don't touch. + */ + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, + ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); + ic->ic_stats.is_ampdu_bar_bad++; + return; + } + ic->ic_stats.is_ampdu_bar_rx++; + rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + /* calculate offset in BA window */ + off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); + if (off >= rap->rxa_wnd) { + /* + * Outside the window, flush the reorder q if + * not pulling the sequence # backward. The + * latter is typically caused by a dropped BA. + */ + IEEE80211_NOTE(ic, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni, + "recv BAR outside BA win <%u:%u> rxseq %u tid %u", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), + rxseq, tid); + ic->ic_stats.is_ampdu_bar_oow++; + if (rxseq < rap->rxa_start) { + /* XXX stat? */ + return; + } + if (rap->rxa_qframes != 0) { + ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; + ampdu_rx_flush(ni, rap, rap->rxa_wnd); + KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, + ("lost %u data, %u frames on ampdu rx q", + rap->rxa_qbytes, rap->rxa_qframes)); + } + } else if (rap->rxa_qframes != 0) { + /* + * Dispatch packets up to rxseq. + */ + ampdu_rx_flush(ni, rap, off); + ic->ic_stats.is_ampdu_rx_oor += off; + + /* + * If frames remain, copy the mbuf pointers down so + * they correspond to the offsets in the new window. + */ + if (rap->rxa_qframes != 0) { + int n = rap->rxa_qframes, j; + for (j = off+1; j < rap->rxa_wnd; j++) { + if (rap->rxa_m[j] != NULL) { + rap->rxa_m[j-off] = rap->rxa_m[j]; + rap->rxa_m[j] = NULL; + if (--n == 0) + break; + } + } + KASSERT(n == 0, ("lost %d frames", n)); + ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + } + } + rap->rxa_start = rxseq; + rap->rxa_nxt = rap->rxa_start; +} + +/* + * Setup HT-specific state in a node. Called only + * when HT use is negotiated so we don't do extra + * work for temporary and/or legacy sta's. + */ +void +ieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap) +{ + struct ieee80211_tx_ampdu *tap; + int ac; + + ieee80211_parse_htcap(ni, htcap); + for (ac = 0; ac < WME_NUM_AC; ac++) { + tap = &ni->ni_tx_ampdu[ac]; + tap->txa_ac = ac; + } + ni->ni_flags |= IEEE80211_NODE_HT; +} + +/* + * Cleanup HT-specific state in a node. Called only + * when HT use has been marked. + */ +void +ieee80211_ht_node_cleanup(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + int i; + + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); + + /* XXX optimize this */ + for (i = 0; i < WME_NUM_AC; i++) { + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; + if (IEEE80211_AMPDU_REQUESTED(tap)) + ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]); + } + for (i = 0; i < WME_NUM_TID; i++) + ampdu_rx_stop(&ni->ni_rx_ampdu[i]); + + ni->ni_htcap = 0; + ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT); +} + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) + +/* + * Process an 802.11n HT capabilities ie. + */ +void +ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) +{ + struct ieee80211com *ic = ni->ni_ic; + + if (ie[0] == IEEE80211_ELEMID_VENDOR) { + /* + * Station used Vendor OUI ie to associate; + * mark the node so when we respond we'll use + * the Vendor OUI's and not the standard ie's. + */ + ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; + ie += 4; + } else + ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; + + ni->ni_htcap = LE_READ_2(ie + + __offsetof(struct ieee80211_ie_htcap, hc_cap)); + if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0) + ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI40; + if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) + ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI20; + ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) ? 40 : 20; + ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; +#if 0 + ni->ni_maxampdu = + (8*1024) << MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); + ni->ni_mpdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); +#endif +} + +/* + * Process an 802.11n HT info ie. + */ +void +ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) +{ + const struct ieee80211_ie_htinfo *htinfo; + uint16_t w; + int chw; + + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htinfo = (const struct ieee80211_ie_htinfo *) ie; + ni->ni_htctlchan = htinfo->hi_ctrlchannel; + ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); + w = LE_READ_2(&htinfo->hi_byte23); + ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); + w = LE_READ_2(&htinfo->hi_byte45); + ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); + /* update node's recommended tx channel width */ + chw = (htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) ? 40 : 20; + if (chw != ni->ni_chw) { + ni->ni_chw = chw; + ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; + } +} + +/* + * Install received HT rate set by parsing the HT cap ie. + */ +int +ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_ie_htcap *htcap; + struct ieee80211_htrateset *rs; + int i; + + rs = &ni->ni_htrates; + memset(rs, 0, sizeof(*rs)); + if (ie != NULL) { + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htcap = (const struct ieee80211_ie_htcap *) ie; + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { + if (isclr(htcap->hc_mcsset, i)) + continue; + if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { + IEEE80211_NOTE(ic, + IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "WARNING, HT rate set too large; only " + "using %u rates", IEEE80211_HTRATE_MAXSIZE); + ic->ic_stats.is_rx_rstoobig++; + break; + } + rs->rs_rates[rs->rs_nrates++] = i; + } + } + return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); +} + +/* + * Mark rates in a node's HT rate set as basic according + * to the information in the supplied HT info ie. + */ +void +ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) +{ + const struct ieee80211_ie_htinfo *htinfo; + struct ieee80211_htrateset *rs; + int i, j; + + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htinfo = (const struct ieee80211_ie_htinfo *) ie; + rs = &ni->ni_htrates; + if (rs->rs_nrates == 0) { + IEEE80211_NOTE(ni->ni_ic, + IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "%s", "WARNING, empty HT rate set"); + return; + } + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { + if (isclr(htinfo->hi_basicmcsset, i)) + continue; + for (j = 0; j < rs->rs_nrates; j++) + if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) + rs->rs_rates[j] |= IEEE80211_RATE_BASIC; + } +} + +static void +addba_timeout(void *arg) +{ + struct ieee80211_tx_ampdu *tap = arg; + + /* XXX ? */ + tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; + tap->txa_attempts++; +} + +static void +addba_start_timeout(struct ieee80211_tx_ampdu *tap) +{ + /* XXX use CALLOUT_PENDING instead? */ + callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT, + addba_timeout, tap); + tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; + tap->txa_lastrequest = ticks; +} + +static void +addba_stop_timeout(struct ieee80211_tx_ampdu *tap) +{ + /* XXX use CALLOUT_PENDING instead? */ + if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { + callout_stop(&tap->txa_timer); + tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; + } +} + +/* + * Default method for requesting A-MPDU tx aggregation. + * We setup the specified state block and start a timer + * to wait for an ADDBA response frame. + */ +static int +ieee80211_addba_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout) +{ + int bufsiz; + + /* XXX locking */ + tap->txa_token = dialogtoken; + tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; + tap->txa_start = tap->txa_seqstart = 0; + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + tap->txa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + addba_start_timeout(tap); + return 1; +} + +/* + * Default method for processing an A-MPDU tx aggregation + * response. We shutdown any pending timer and update the + * state block according to the reply. + */ +static int +ieee80211_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status, int baparamset, int batimeout) +{ + int bufsiz; + + /* XXX locking */ + addba_stop_timeout(tap); + if (status == IEEE80211_STATUS_SUCCESS) { + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + /* XXX override our request? */ + tap->txa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + tap->txa_flags |= IEEE80211_AGGR_RUNNING; + } + return 1; +} + +/* + * Default method for stopping A-MPDU tx aggregation. + * Any timer is cleared and we drain any pending frames. + */ +static void +ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + /* XXX locking */ + addba_stop_timeout(tap); + if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { + /* clear aggregation queue */ + ieee80211_drain_ifq(&tap->txa_q); + tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; + } + tap->txa_attempts = 0; +} + +/* + * Process a received action frame using the default aggregation + * policy. We intercept ADDBA-related frames and use them to + * update our aggregation state. All other frames are passed up + * for processing by ieee80211_recv_action. + */ +static void +ieee80211_aggr_recv_action(struct ieee80211_node *ni, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_action *ia; + struct ieee80211_rx_ampdu *rap; + struct ieee80211_tx_ampdu *tap; + uint8_t dialogtoken; + uint16_t baparamset, batimeout, baseqctl, code; + uint16_t args[4]; + int tid, ac, bufsiz; + + ia = (const struct ieee80211_action *) frm; + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + switch (ia->ia_action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + dialogtoken = frm[2]; + baparamset = LE_READ_2(frm+3); + batimeout = LE_READ_2(frm+5); + baseqctl = LE_READ_2(frm+7); + + tid = MS(baparamset, IEEE80211_BAPS_TID); + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv ADDBA request: dialogtoken %u " + "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " + "baseqctl %d", + dialogtoken, baparamset, tid, bufsiz, + batimeout, baseqctl); + + rap = &ni->ni_rx_ampdu[tid]; + + /* Send ADDBA response */ + args[0] = dialogtoken; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) { + ampdu_rx_start(rap, bufsiz, + MS(baseqctl, IEEE80211_BASEQ_START)); + + args[1] = IEEE80211_STATUS_SUCCESS; + } else + args[1] = IEEE80211_STATUS_UNSPECIFIED; + /* XXX honor rap flags? */ + args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE + | SM(tid, IEEE80211_BAPS_TID) + | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) + ; + args[3] = 0; + ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); + return; + + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + dialogtoken = frm[2]; + code = LE_READ_2(frm+3); + baparamset = LE_READ_2(frm+5); + tid = MS(baparamset, IEEE80211_BAPS_TID); + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + batimeout = LE_READ_2(frm+7); + + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv ADDBA response: dialogtoken %u code %d " + "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", + dialogtoken, code, baparamset, tid, bufsiz, + batimeout); + + ac = TID_TO_WME_AC(tid); + tap = &ni->ni_tx_ampdu[ac]; + + ic->ic_addba_response(ni, tap, + code, baparamset, batimeout); + return; + + case IEEE80211_ACTION_BA_DELBA: + baparamset = LE_READ_2(frm+2); + code = LE_READ_2(frm+4); + + tid = MS(baparamset, IEEE80211_DELBAPS_TID); + + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv DELBA: baparamset 0x%x (tid %d initiator %d) " + "code %d", baparamset, tid, + MS(baparamset, IEEE80211_DELBAPS_INIT), code); + + if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { + ac = TID_TO_WME_AC(tid); + tap = &ni->ni_tx_ampdu[ac]; + ic->ic_addba_stop(ni, tap); + } else { + rap = &ni->ni_rx_ampdu[tid]; + ampdu_rx_stop(rap); + } + return; + } + break; + } + return ieee80211_recv_action(ni, frm, efrm); +} + +/* + * Process a received 802.11n action frame. + * Aggregation-related frames are assumed to be handled + * already; we handle any other frames we can, otherwise + * complain about being unsupported (with debugging). + */ +void +ieee80211_recv_action(struct ieee80211_node *ni, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_action *ia; + int chw; + + ia = (const struct ieee80211_action *) frm; + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: BA action %d not implemented", __func__, + ia->ia_action); + ic->ic_stats.is_rx_mgtdiscard++; + break; + case IEEE80211_ACTION_CAT_HT: + switch (ia->ia_action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20; + if (chw != ni->ni_chw) { + ni->ni_chw = chw; + ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; + } + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: HT txchwidth. width %d (%s)", + __func__, chw, + ni->ni_flags & IEEE80211_NODE_CHWUPDATE ? + "new" : "no change"); + break; + default: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: HT action %d not implemented", __func__, + ia->ia_action); + ic->ic_stats.is_rx_mgtdiscard++; + break; + } + break; + default: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: category %d not implemented", __func__, + ia->ia_category); + ic->ic_stats.is_rx_mgtdiscard++; + break; + } +} + +/* + * Transmit processing. + */ + +/* + * Request A-MPDU tx aggregation. Setup local state and + * issue an ADDBA request. BA use will only happen after + * the other end replies with ADDBA response. + */ +int +ieee80211_ampdu_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t args[4]; + int tid, dialogtoken; + static int tokens = 0; /* XXX */ + + /* XXX locking */ + if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { + /* do deferred setup of state */ + /* XXX tap->txa_q */ + callout_init(&tap->txa_timer, CALLOUT_MPSAFE); + tap->txa_flags |= IEEE80211_AGGR_SETUP; + } + if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES && + (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) { + /* + * Don't retry too often; IEEE80211_AGGR_MINRETRY + * defines the minimum interval we'll retry after + * IEEE80211_AGGR_MAXTRIES failed attempts to + * negotiate use. + */ + return 0; + } + dialogtoken = (tokens+1) % 63; /* XXX */ + + tid = WME_AC_TO_TID(tap->txa_ac); + args[0] = dialogtoken; + args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE + | SM(tid, IEEE80211_BAPS_TID) + | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) + ; + args[2] = 0; /* batimeout */ + args[3] = SM(0, IEEE80211_BASEQ_START) + | SM(0, IEEE80211_BASEQ_FRAG) + ; + /* NB: do first so there's no race against reply */ + if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { + /* unable to setup state, don't make request */ + return 0; + } + tokens = dialogtoken; /* allocate token */ + return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_REQUEST, args); +} + +/* + * Transmit a BAR frame to the specified node. The + * BAR contents are drawn from the supplied aggregation + * state associated with the node. + */ +int +ieee80211_send_bar(struct ieee80211_node *ni, + const struct ieee80211_tx_ampdu *tap) +{ +#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_frame_min *wh; + struct mbuf *m; + uint8_t *frm; + uint16_t barctl, barseqctl; + int tid, ret; + + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame_min), + sizeof(struct ieee80211_ba_request) + ); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + wh = mtod(m, struct ieee80211_frame_min *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; + wh->i_fc[1] = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + + tid = WME_AC_TO_TID(tap->txa_ac); + barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? + IEEE80211_BAPS_POLICY_IMMEDIATE : + IEEE80211_BAPS_POLICY_DELAYED) + | SM(tid, IEEE80211_BAPS_TID) + | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ) + ; + barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START) + | SM(0, IEEE80211_BASEQ_FRAG) + ; + ADDSHORT(frm, barctl); + ADDSHORT(frm, barseqctl); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "[%s] send bar frame (tid %u start %u) on channel %u\n", + ether_sprintf(ni->ni_macaddr), tid, tap->txa_start, + ieee80211_chan2ieee(ic, ic->ic_curchan)); + + m->m_pkthdr.rcvif = (void *)ni; + IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ + (*ifp->if_start)(ifp); + + return 0; +bad: + ieee80211_free_node(ni); + return ret; +#undef ADDSHORT +#undef senderr +} + +/* + * Send an action management frame. The arguments are stuff + * into a frame without inspection; the caller is assumed to + * prepare them carefully (e.g. based on the aggregation state). + */ +int +ieee80211_send_action(struct ieee80211_node *ni, + int category, int action, uint16_t args[4]) +{ +#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + uint8_t *frm; + uint16_t baparamset; + int ret; + + KASSERT(ni != NULL, ("null node")); + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + /* XXX may action payload */ + + sizeof(struct ieee80211_action_ba_addbaresponse) + ); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + *frm++ = category; + *frm++ = action; + switch (category) { + case IEEE80211_ACTION_CAT_BA: + switch (action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send ADDBA request: tid %d, baparamset 0x%x", + args[0], args[1]); + + *frm++ = args[0]; /* dialog token */ + ADDSHORT(frm, args[1]); /* baparamset */ + ADDSHORT(frm, args[2]); /* batimeout */ + ADDSHORT(frm, args[3]); /* baseqctl */ + break; + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send ADDBA response: dialogtoken %d status %d " + "baparamset 0x%x (tid %d) batimeout %d", + args[0], args[1], args[2], + MS(args[2], IEEE80211_BAPS_TID), args[3]); + + *frm++ = args[0]; /* dialog token */ + ADDSHORT(frm, args[1]); /* statuscode */ + ADDSHORT(frm, args[2]); /* baparamset */ + ADDSHORT(frm, args[3]); /* batimeout */ + break; + case IEEE80211_ACTION_BA_DELBA: + /* XXX */ + baparamset = SM(args[0], IEEE80211_DELBAPS_TID) + | SM(args[1], IEEE80211_DELBAPS_INIT) + ; + ADDSHORT(frm, baparamset); + ADDSHORT(frm, args[2]); /* reason code */ + + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send DELBA action: tid %d, initiator %d reason %d", + args[0], args[1], args[2]); + break; + default: + goto badaction; + } + break; + case IEEE80211_ACTION_CAT_HT: + switch (action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "send HT txchwidth: width %d", + IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 + ); + *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? + IEEE80211_A_HT_TXCHWIDTH_2040 : + IEEE80211_A_HT_TXCHWIDTH_20; + break; + default: + goto badaction; + } + break; + default: + badaction: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: unsupported category %d action %d", __func__, + category, action); + senderr(EINVAL, is_tx_unknownmgt); + /* NOTREACHED */ + } + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION); + if (ret != 0) + goto bad; + return 0; +bad: + ieee80211_free_node(ni); + return ret; +#undef ADDSHORT +#undef senderr +} + +/* + * Construct the MCS bit mask for inclusion + * in an HT information element. + */ +static void +ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) +{ + int i; + + for (i = 0; i < rs->rs_nrates; i++) { + int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ + /* NB: this assumes a particular implementation */ + setbit(frm, r); + } + } +} + +/* + * Add body of an HTCAP information element. + */ +static uint8_t * +ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) +{ +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + struct ieee80211com *ic = ni->ni_ic; + uint16_t caps; + + /* HT capabilities */ + caps = ic->ic_htcaps & 0xffff; + /* override 20/40 use based on channel and config */ + if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && + (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + /* adjust short GI based on channel and config */ + if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) + caps &= ~IEEE80211_HTCAP_SHORTGI20; + if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || + (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) + caps &= ~IEEE80211_HTCAP_SHORTGI40; + ADDSHORT(frm, caps); + + /* HT parameters */ + switch (ic->ic_ampdu_rxmax / 1024) { + case 8: *frm = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; + case 16: *frm = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; + case 32: *frm = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; + default: *frm = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; + } + *frm |= SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY); + frm++; + + /* pre-zero remainder of ie */ + memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); + + /* supported MCS set */ + ieee80211_set_htrates(frm, &ni->ni_htrates); + + frm += sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset); + return frm; +#undef ADDSHORT +} + +/* + * Add 802.11n HT capabilities information element + */ +uint8_t * +ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_HTCAP; + frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; + return ieee80211_add_htcap_body(frm + 2, ni); +} + +/* + * Add Broadcom OUI wrapped standard HTCAP ie; this is + * used for compatibility w/ pre-draft implementations. + */ +uint8_t * +ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_VENDOR; + frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; + frm[2] = (BCM_OUI >> 0) & 0xff; + frm[3] = (BCM_OUI >> 8) & 0xff; + frm[4] = (BCM_OUI >> 16) & 0xff; + frm[5] = BCM_OUI_HTCAP; + return ieee80211_add_htcap_body(frm + 6, ni); +} + +/* + * Construct the MCS bit mask of basic rates + * for inclusion in an HT information element. + */ +static void +ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) +{ + int i; + + for (i = 0; i < rs->rs_nrates; i++) { + int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && + r < IEEE80211_HTRATE_MAXSIZE) { + /* NB: this assumes a particular implementation */ + setbit(frm, r); + } + } +} + +/* + * Add body of an HTINFO information element. + */ +static uint8_t * +ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + /* pre-zero remainder of ie */ + memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); + + /* primary/control channel center */ + *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); + + frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; + if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) + frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; + else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) + frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; + else + frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; + if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; + + frm[1] = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) ? + IEEE80211_HTINFO_OPMODE_PURE : IEEE80211_HTINFO_OPMODE_MIXED; + /* XXX IEEE80211_HTINFO_NONHT_PRESENT */ + + frm += 5; + + /* basic MCS set */ + ieee80211_set_basic_htrates(frm, &ni->ni_htrates); + frm += sizeof(struct ieee80211_ie_htinfo) - + __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); + return frm; +} + +/* + * Add 802.11n HT information information element. + */ +uint8_t * +ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_HTINFO; + frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; + return ieee80211_add_htinfo_body(frm + 2, ni); +} + +/* + * Add Broadcom OUI wrapped standard HTINFO ie; this is + * used for compatibility w/ pre-draft implementations. + */ +uint8_t * +ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_VENDOR; + frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; + frm[2] = (BCM_OUI >> 0) & 0xff; + frm[3] = (BCM_OUI >> 8) & 0xff; + frm[4] = (BCM_OUI >> 16) & 0xff; + frm[5] = BCM_OUI_HTINFO; + return ieee80211_add_htinfo_body(frm + 6, ni); +} diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h new file mode 100644 index 000000000000..fc87f1f1abaf --- /dev/null +++ b/sys/net80211/ieee80211_ht.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_HT_H_ +#define _NET80211_IEEE80211_HT_H_ + +/* + * 802.11n protocol implementation definitions. + */ + +#define IEEE80211_SEND_ACTION(_ni,_cat, _act, _args) \ + ((*(_ic)->ic_send_action)(_ni, _cat, _act, _args)) + +#define IEEE80211_AGGR_BAWMAX 64 /* max block ack window size */ + +typedef uint16_t ieee80211_seq; + +struct ieee80211_tx_ampdu { + u_short txa_flags; +#define IEEE80211_AGGR_IMMEDIATE 0x0001 /* BA policy */ +#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */ +#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */ +#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */ + uint8_t txa_ac; + uint8_t txa_token; /* dialog token */ + int txa_qbytes; /* data queued (bytes) */ + short txa_qframes; /* data queued (frames) */ + ieee80211_seq txa_seqstart; + ieee80211_seq txa_start; + uint16_t txa_wnd; /* BA window size */ + uint8_t txa_attempts; /* # setup attempts */ + int txa_lastrequest;/* time of last ADDBA request */ + struct ifqueue txa_q; /* packet queue */ + struct callout txa_timer; +}; + +/* return non-zero if AMPDU tx for the TID is running */ +#define IEEE80211_AMPDU_RUNNING(tap) \ + (((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0) + +/* return non-zero if AMPDU tx for the TID is running or started */ +#define IEEE80211_AMPDU_REQUESTED(tap) \ + (((tap)->txa_flags & \ + (IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND)) != 0) + +struct ieee80211_rx_ampdu { + int rxa_flags; + int rxa_qbytes; /* data queued (bytes) */ + short rxa_qframes; /* data queued (frames) */ + ieee80211_seq rxa_seqstart; + ieee80211_seq rxa_start; /* start of current BA window */ + ieee80211_seq rxa_nxt; /* next seq# in BA window */ + uint16_t rxa_wnd; /* BA window size */ + struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX]; +}; + +void ieee80211_ht_attach(struct ieee80211com *); +void ieee80211_ht_detach(struct ieee80211com *); + +void ieee80211_ht_announce(struct ieee80211com *); + +extern const int ieee80211_htrates[16]; +const struct ieee80211_htrateset *ieee80211_get_suphtrates( + struct ieee80211com *, const struct ieee80211_channel *); + +struct ieee80211_node; +int ieee80211_setup_htrates(struct ieee80211_node *, + const uint8_t *htcap, int flags); +void ieee80211_setup_basic_htrates(struct ieee80211_node *, + const uint8_t *htinfo); +struct mbuf *ieee80211_decap_amsdu(struct ieee80211_node *, struct mbuf *); +int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *); +void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *); +void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *); +void ieee80211_ht_node_cleanup(struct ieee80211_node *); +void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *); +void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *); +void ieee80211_recv_action(struct ieee80211_node *, + const uint8_t *, const uint8_t *); +int ieee80211_ampdu_request(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); +int ieee80211_send_bar(struct ieee80211_node *, + const struct ieee80211_tx_ampdu *); +int ieee80211_send_action(struct ieee80211_node *, + int, int, uint16_t [4]); +uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *); +uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *); +uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *); +uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *); +#endif /* _NET80211_IEEE80211_HT_H_ */ diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 721966898180..a5b53410b359 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -67,7 +67,7 @@ doprint(struct ieee80211com *ic, int subtype) return 1; } -static const u_int8_t *ieee80211_getbssid(struct ieee80211com *, +static const uint8_t *ieee80211_getbssid(struct ieee80211com *, const struct ieee80211_frame *); #endif /* IEEE80211_DEBUG */ @@ -75,10 +75,9 @@ static struct mbuf *ieee80211_defrag(struct ieee80211com *, struct ieee80211_node *, struct mbuf *, int); static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int); static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *, - const u_int8_t *mac, int subtype, int arg); -static void ieee80211_deliver_data(struct ieee80211com *, + const uint8_t *mac, int subtype, int arg); +static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); -static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); static void ieee80211_recv_pspoll(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); @@ -94,7 +93,7 @@ static void ieee80211_recv_pspoll(struct ieee80211com *, */ int ieee80211_input(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni, int rssi, u_int32_t rstamp) + struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp) { #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) #define HAS_SEQ(type) ((type & 0x4) == 0) @@ -102,19 +101,33 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_frame *wh; struct ieee80211_key *key; struct ether_header *eh; - int hdrspace; - u_int8_t dir, type, subtype; - u_int8_t *bssid; - u_int16_t rxseq; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint8_t *bssid; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + need_tap = 0; + goto resubmit_ampdu; + } KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; - /* trim CRC here so WEP can find its own CRC at the end of packet. */ - if (m->m_flags & M_HASFCS) { - m_adj(m, -IEEE80211_CRC_LEN); - m->m_flags &= ~M_HASFCS; - } + need_tap = 1; /* mbuf need to be tapped. */ type = -1; /* undefined */ /* * In monitor mode, send everything directly to bpf. @@ -215,9 +228,10 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, goto out; } ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; if (HAS_SEQ(type)) { - u_int8_t tid; + uint8_t tid; if (IEEE80211_QOS_HAS_SEQ(wh)) { tid = ((struct ieee80211_qosframe *)wh)-> i_qos[0] & IEEE80211_QOS_TID; @@ -226,8 +240,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, tid++; } else tid = IEEE80211_NONQOS_TID; - rxseq = le16toh(*(u_int16_t *)wh->i_seq); - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, @@ -263,7 +278,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_STA: if (dir != IEEE80211_FC1_DIR_FROMDS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -286,7 +301,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -295,7 +310,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -321,6 +336,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, /* * Check for power save state change. + * XXX out-of-order A-MPDU frames? */ if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) @@ -333,6 +349,23 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + + /* * Handle privacy requirements. Note that we * must not be preempted from here until after * we (potentially) call ieee80211_crypto_demic; @@ -364,6 +397,16 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* * Next up, any fragmentation. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { @@ -389,6 +432,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, /* copy to listener after decrypt */ if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m); + need_tap = 0; /* * Finally, strip the 802.11 header. @@ -396,7 +440,8 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, m = ieee80211_decap(ic, m, hdrspace); if (m == NULL) { /* don't count Null data frames as errors */ - if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) goto out; IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, ni->ni_macaddr, "data", "%s", "decap error"); @@ -439,6 +484,37 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, goto out; } } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + ic->ic_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ic, ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE ieee80211_deliver_data(ic, ni, m); return IEEE80211_FC0_TYPE_DATA; @@ -447,7 +523,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto err; } @@ -500,9 +576,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m); - (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); + (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp); m_freem(m); - return type; + return IEEE80211_FC0_TYPE_MGT; case IEEE80211_FC0_TYPE_CTL: ic->ic_stats.is_rx_ctl++; @@ -512,6 +588,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_FC0_SUBTYPE_PS_POLL: ieee80211_recv_pspoll(ic, ni, m); break; + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; } } goto out; @@ -525,7 +604,7 @@ err: ifp->if_ierrors++; out: if (m != NULL) { - if (bpf_peers_present(ic->ic_rawbpf)) + if (bpf_peers_present(ic->ic_rawbpf) && need_tap) bpf_mtap(ic->ic_rawbpf, m); m_freem(m); } @@ -542,14 +621,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, { struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct ieee80211_frame *lwh; - u_int16_t rxseq; - u_int8_t fragno; - u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + uint16_t rxseq; + uint8_t fragno; + uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; struct mbuf *mfrag; KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); - rxseq = le16toh(*(u_int16_t *)wh->i_seq); + rxseq = le16toh(*(uint16_t *)wh->i_seq); fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; /* Quick way out, if there's nothing to defragment */ @@ -582,10 +661,10 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, * related to the previous ones. */ if (mfrag != NULL) { - u_int16_t last_rxseq; + uint16_t last_rxseq; lwh = mtod(mfrag, struct ieee80211_frame *); - last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq); + last_rxseq = le16toh(*(uint16_t *)lwh->i_seq); /* NB: check seq # and frag together */ if (rxseq != last_rxseq+1 || !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || @@ -614,7 +693,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, mfrag->m_pkthdr.len += m->m_pkthdr.len; /* track last seqnum and fragno */ lwh = mtod(mfrag, struct ieee80211_frame *); - *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; + *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq; } if (more_frag) { /* more to come, save */ ni->ni_rxfragstamp = ticks; @@ -624,7 +703,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, return mfrag; } -static void +void ieee80211_deliver_data(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m) { @@ -735,7 +814,7 @@ ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) break; } #ifdef ALIGNED_POINTER - if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { + if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { struct mbuf *n, *n0, **np; caddr_t newdata; int off, pktlen; @@ -793,11 +872,109 @@ ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) } /* + * Decap a frame encapsulated in a fast-frame/A-MSDU. + */ +struct mbuf * +ieee80211_decap1(struct mbuf *m, int *framelen) +{ +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + struct ether_header *eh; + struct llc *llc; + + /* + * The frame has an 802.3 header followed by an 802.2 + * LLC header. The encapsulated frame length is in the + * first header type field; save that and overwrite it + * with the true type field found in the second. Then + * copy the 802.3 header up to where it belongs and + * adjust the mbuf contents to remove the void. + */ + if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL) + return NULL; + eh = mtod(m, struct ether_header *); /* 802.3 header is first */ + llc = (struct llc *)&eh[1]; /* 802.2 header follows */ + *framelen = ntohs(eh->ether_type) /* encap'd frame size */ + + sizeof(struct ether_header) - sizeof(struct llc); + eh->ether_type = llc->llc_un.type_snap.ether_type; + ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc), + sizeof(struct ether_header)); + m_adj(m, sizeof(struct llc)); + return m; +#undef FF_LLC_SIZE +} + +/* + * Decap the encapsulated frame pair and dispatch the first + * for delivery. The second frame is returned for delivery + * via the normal path. + */ +static struct mbuf * +ieee80211_decap_fastframe(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) +{ +#define MS(x,f) (((x) & f) >> f##_S) + uint32_t ath; + struct mbuf *n; + int framelen; + + m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath); + if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "unsupport tunnel protocol, header 0x%x", ath); + ic->ic_stats.is_ff_badhdr++; + m_freem(m); + return NULL; + } + /* NB: skip header and alignment padding */ + m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2); + + ic->ic_stats.is_ff_decap++; + + /* + * Decap the first frame, bust it apart from the + * second and deliver; then decap the second frame + * and return it to the caller for normal delivery. + */ + m = ieee80211_decap1(m, &framelen); + if (m == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", "%s", "first decap failed"); + ic->ic_stats.is_ff_tooshort++; + return NULL; + } + n = m_split(m, framelen, M_NOWAIT); + if (n == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "unable to split encapsulated frames"); + ic->ic_stats.is_ff_split++; + m_freem(m); /* NB: must reclaim */ + return NULL; + } + ieee80211_deliver_data(ic, ni, m); /* 1st of pair */ + + /* + * Decap second frame. + */ + m_adj(n, roundup2(framelen, 4) - framelen); /* padding */ + n = ieee80211_decap1(n, &framelen); + if (n == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", "%s", "second decap failed"); + ic->ic_stats.is_ff_tooshort++; + } + /* XXX verify framelen against mbuf contents */ + return n; /* 2nd delivered by caller */ +#undef MS +} + +/* * Install received rate set information in the node's state block. */ int ieee80211_setup_rates(struct ieee80211_node *ni, - const u_int8_t *rates, const u_int8_t *xrates, int flags) + const uint8_t *rates, const uint8_t *xrates, int flags) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rateset *rs = &ni->ni_rates; @@ -806,7 +983,7 @@ ieee80211_setup_rates(struct ieee80211_node *ni, rs->rs_nrates = rates[1]; memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); if (xrates != NULL) { - u_int8_t nxrates; + uint8_t nxrates; /* * Tack on 11g extended supported rate element. */ @@ -827,8 +1004,8 @@ ieee80211_setup_rates(struct ieee80211_node *ni, static void ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, - struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, - u_int16_t status) + struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp, + uint16_t seq, uint16_t status) { if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { @@ -843,7 +1020,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, * open auth is attempted. */ if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } /* XXX hack to workaround calling convention */ @@ -857,6 +1034,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_MONITOR: + case IEEE80211_M_WDS: /* should not come here */ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "open auth", @@ -911,10 +1089,10 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, if (ni != ic->ic_bss) ni->ni_fails++; ic->ic_stats.is_rx_auth_fail++; - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); } else - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; } } @@ -928,7 +1106,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, */ static void ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, - const u_int8_t *mac, int subtype, int arg) + const uint8_t *mac, int subtype, int arg) { int istmp; @@ -950,8 +1128,8 @@ static int alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) { if (ni->ni_challenge == NULL) - MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, - M_DEVBUF, M_NOWAIT); + MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN, + M_80211_NODE, M_NOWAIT); if (ni->ni_challenge == NULL) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] shared key challenge alloc failed\n", @@ -964,10 +1142,10 @@ alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) /* XXX TODO: add statistics */ static void ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, - u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, - u_int32_t rstamp, u_int16_t seq, u_int16_t status) + uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni, + int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) { - u_int8_t *challenge; + uint8_t *challenge; int allocbs, estatus; /* @@ -1041,6 +1219,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, case IEEE80211_M_MONITOR: case IEEE80211_M_AHDEMO: case IEEE80211_M_IBSS: + case IEEE80211_M_WDS: IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "shared key auth", "bad operating mode %u", ic->ic_opmode); @@ -1074,6 +1253,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, */ ni->ni_flags |= IEEE80211_NODE_AREF; ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; if (!alloc_challenge(ic, ni)) { /* NB: don't return error so they rexmit */ @@ -1136,7 +1316,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, switch (seq) { case IEEE80211_AUTH_SHARED_PASS: if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } if (status != 0) { @@ -1151,8 +1331,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, ic->ic_stats.is_rx_auth_fail++; return; } - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; case IEEE80211_AUTH_SHARED_CHALLENGE: if (!alloc_challenge(ic, ni)) @@ -1187,7 +1366,8 @@ bad: * state transition. */ if (ic->ic_state == IEEE80211_S_AUTH) - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); } } @@ -1211,7 +1391,7 @@ bad: } \ } while (0) -#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ +#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \ if ((_len) < (_minlen)) { \ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ wh, ieee80211_mgt_subtype_name[subtype >> \ @@ -1219,14 +1399,14 @@ bad: "ie too short, got %d, expected %d", \ (_len), (_minlen)); \ ic->ic_stats.is_rx_elem_toosmall++; \ - return; \ + _action; \ } \ } while (0) #ifdef IEEE80211_DEBUG static void ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, - u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid) + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) { printf("[%s] discard %s frame, ssid mismatch: ", ether_sprintf(mac), tag); @@ -1260,58 +1440,70 @@ ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, /* unalligned little endian access */ #define LE_READ_2(p) \ - ((u_int16_t) \ - ((((const u_int8_t *)(p))[0] ) | \ - (((const u_int8_t *)(p))[1] << 8))) + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_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))) + ((uint32_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8) | \ + (((const uint8_t *)(p))[2] << 16) | \ + (((const uint8_t *)(p))[3] << 24))) static __inline int -iswpaoui(const u_int8_t *frm) +iswpaoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); } static __inline int -iswmeoui(const u_int8_t *frm) +iswmeoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); } static __inline int -iswmeparam(const u_int8_t *frm) +iswmeparam(const uint8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_PARAM_OUI_SUBTYPE; } static __inline int -iswmeinfo(const u_int8_t *frm) +iswmeinfo(const uint8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_INFO_OUI_SUBTYPE; } static __inline int -isatherosoui(const u_int8_t *frm) +isatherosoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); } +static __inline int +ishtcapoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI); +} + +static __inline int +ishtinfooui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); +} + /* * Convert a WPA cipher selector OUI to an internal * cipher algorithm. Where appropriate we also * record any key length. */ static int -wpa_cipher(u_int8_t *sel, u_int8_t *keylen) +wpa_cipher(uint8_t *sel, uint8_t *keylen) { #define WPA_SEL(x) (((x)<<24)|WPA_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case WPA_SEL(WPA_CSE_NULL): @@ -1338,10 +1530,10 @@ wpa_cipher(u_int8_t *sel, u_int8_t *keylen) * to an internal code. */ static int -wpa_keymgmt(u_int8_t *sel) +wpa_keymgmt(uint8_t *sel) { #define WPA_SEL(x) (((x)<<24)|WPA_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case WPA_SEL(WPA_ASE_8021X_UNSPEC): @@ -1361,11 +1553,11 @@ wpa_keymgmt(u_int8_t *sel) * configured for the system. */ static int -ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm, struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { - u_int8_t len = frm[1]; - u_int32_t w; + uint8_t len = frm[1]; + uint32_t w; int n; /* @@ -1473,10 +1665,10 @@ ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, * record any key length. */ static int -rsn_cipher(u_int8_t *sel, u_int8_t *keylen) +rsn_cipher(uint8_t *sel, uint8_t *keylen) { #define RSN_SEL(x) (((x)<<24)|RSN_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case RSN_SEL(RSN_CSE_NULL): @@ -1505,10 +1697,10 @@ rsn_cipher(u_int8_t *sel, u_int8_t *keylen) * to an internal code. */ static int -rsn_keymgmt(u_int8_t *sel) +rsn_keymgmt(uint8_t *sel) { #define RSN_SEL(x) (((x)<<24)|RSN_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case RSN_SEL(RSN_ASE_8021X_UNSPEC): @@ -1528,11 +1720,11 @@ rsn_keymgmt(u_int8_t *sel) * configured for the system. */ static int -ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm, struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { - u_int8_t len = frm[1]; - u_int32_t w; + uint8_t len = frm[1]; + uint32_t w; int n; /* @@ -1636,7 +1828,7 @@ ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, } static int -ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm, const struct ieee80211_frame *wh) { #define MS(_v, _f) (((_v) & _f) >> _f##_S) @@ -1672,8 +1864,65 @@ ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, #undef MS } +static int +ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm, + const struct ieee80211_frame *wh) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_ath_ie *ath; + u_int len = frm[1]; + int capschanged; + uint16_t defkeyix; + + if (len < sizeof(struct ieee80211_ath_ie)-2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG, + wh, "Atheros", "too short, len %u", len); + return -1; + } + ath = (const struct ieee80211_ath_ie *)frm; + capschanged = (ni->ni_ath_flags != ath->ath_capability); + defkeyix = LE_READ_2(ath->ath_defkeyix); + if (capschanged || defkeyix != ni->ni_ath_defkeyix) { + ni->ni_ath_flags = ath->ath_capability; + ni->ni_ath_defkeyix = defkeyix; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n", + ether_sprintf(ni->ni_macaddr), + ni->ni_ath_flags, ni->ni_ath_defkeyix); + } + if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) { + uint16_t curflags, newflags; + + /* + * Check for turbo mode switch. Calculate flags + * for the new mode and effect the switch. + */ + newflags = curflags = ic->ic_bsschan->ic_flags; + /* NB: BOOST is not in ic_flags, so get it from the ie */ + if (ath->ath_capability & ATHEROS_CAP_BOOST) + newflags |= IEEE80211_CHAN_TURBO; + else + newflags &= ~IEEE80211_CHAN_TURBO; + if (newflags != curflags) + ieee80211_dturbo_switch(ic, newflags); + } + return capschanged; +} + void -ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) +ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie) +{ + const struct ieee80211_ath_ie *ath = + (const struct ieee80211_ath_ie *) ie; + + ni->ni_ath_flags = ath->ath_capability; + ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix); + ieee80211_saveie(&ni->ni_ath_ie, ie); +} + +void +ieee80211_saveie(uint8_t **iep, const uint8_t *ie) { u_int ielen = ie[1]+2; /* @@ -1681,8 +1930,8 @@ ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) */ if (*iep == NULL || (*iep)[1] != ie[1]) { if (*iep != NULL) - FREE(*iep, M_DEVBUF); - MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); + FREE(*iep, M_80211_NODE); + MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT); } if (*iep != NULL) memcpy(*iep, ie, ielen); @@ -1692,10 +1941,10 @@ ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) /* XXX find a better place for definition */ struct l2_update_frame { struct ether_header eh; - u_int8_t dsap; - u_int8_t ssap; - u_int8_t control; - u_int8_t xid[3]; + uint8_t dsap; + uint8_t ssap; + uint8_t control; + uint8_t xid[3]; } __packed; /* @@ -1738,6 +1987,22 @@ ieee80211_deliver_l2uf(struct ieee80211_node *ni) ieee80211_deliver_data(ic, ni, m); } +static __inline int +contbgscan(struct ieee80211com *ic) +{ + return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && + time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); +} + +static __inline int +startbgscan(struct ieee80211com *ic) +{ + return ((ic->ic_flags & IEEE80211_F_BGSCAN) && + !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && + time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) && + time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); +} + static void ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, int reassoc, int resp, const char *tag, int rate) @@ -1745,7 +2010,7 @@ ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, struct ieee80211com *ic = ni->ni_ic; IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, %srate set mismatch, rate 0x%x\n", + "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n", ether_sprintf(wh->i_addr2), reassoc ? "reassoc" : "assoc", tag, rate); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); @@ -1771,19 +2036,19 @@ capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, void ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, struct ieee80211_node *ni, - int subtype, int rssi, u_int32_t rstamp) + int subtype, int rssi, int noise, uint32_t rstamp) { #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) struct ieee80211_frame *wh; - u_int8_t *frm, *efrm; - u_int8_t *ssid, *rates, *xrates, *wpa, *wme; + uint8_t *frm, *efrm; + uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; int reassoc, resp, allocbs; - u_int8_t rate; + uint8_t rate; wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; + frm = (uint8_t *)&wh[1]; + efrm = mtod(m0, uint8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { @@ -1816,17 +2081,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] extended supported rates * [tlv] WME * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Atheros capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 12); + IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return); memset(&scan, 0, sizeof(scan)); scan.tstamp = frm; frm += 8; - scan.bintval = le16toh(*(u_int16_t *)frm); frm += 2; - scan.capinfo = le16toh(*(u_int16_t *)frm); frm += 2; + scan.bintval = le16toh(*(uint16_t *)frm); frm += 2; + scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2; scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan); scan.chan = scan.bchan; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: scan.ssid = frm; @@ -1855,7 +2123,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_TIM: /* XXX ATIM? */ scan.tim = frm; - scan.timoff = frm - mtod(m0, u_int8_t *); + scan.timoff = frm - mtod(m0, uint8_t *); break; case IEEE80211_ELEMID_IBSSPARMS: break; @@ -1872,15 +2140,35 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } scan.erp = frm[2]; break; + case IEEE80211_ELEMID_HTCAP: + scan.htcap = frm; + break; case IEEE80211_ELEMID_RSN: - scan.wpa = frm; + scan.rsn = frm; + break; + case IEEE80211_ELEMID_HTINFO: + scan.htinfo = frm; break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) scan.wpa = frm; else if (iswmeparam(frm) || iswmeinfo(frm)) scan.wme = frm; - /* XXX Atheros OUI support */ + else if (isatherosoui(frm)) + scan.ath = frm; + else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + /* + * Accept pre-draft HT ie's if the + * standard ones have not been seen. + */ + if (ishtcapoui(frm)) { + if (scan.htcap == NULL) + scan.htcap = frm; + } else if (ishtinfooui(frm)) { + if (scan.htinfo == NULL) + scan.htcap = frm; + } + } break; default: IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, @@ -1892,20 +2180,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE); + if (scan.xrates != NULL) + IEEE80211_VERIFY_ELEMENT(scan.xrates, + IEEE80211_RATE_MAXSIZE - scan.rates[1]); IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN); - if ( #if IEEE80211_CHAN_MAX < 255 - scan.chan > IEEE80211_CHAN_MAX || -#endif - isclr(ic->ic_chan_active, scan.chan)) { - IEEE80211_DISCARD(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + if (scan.chan > IEEE80211_CHAN_MAX) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, wh, ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], "invalid channel %u", scan.chan); ic->ic_stats.is_rx_badchan++; return; } +#endif if (scan.chan != scan.bchan && ic->ic_phytype != IEEE80211_T_FH) { /* @@ -1936,6 +2224,25 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_badbintval++; return; } + /* + * Process HT ie's. This is complicated by our + * accepting both the standard ie's and the pre-draft + * vendor OUI ie's that some vendors still use/require. + */ + if (scan.htcap != NULL) { + IEEE80211_VERIFY_LENGTH(scan.htcap[1], + scan.htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + scan.htcap = NULL); + } + if (scan.htinfo != NULL) { + IEEE80211_VERIFY_LENGTH(scan.htinfo[1], + scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htinfo)-2 : + sizeof(struct ieee80211_ie_htinfo)-2, + scan.htinfo = NULL); + } /* * Count frame now that we know it's to be processed. @@ -1966,7 +2273,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, "[%s] erp change: was 0x%x, now 0x%x\n", ether_sprintf(wh->i_addr2), ni->ni_erp, scan.erp); - if (ic->ic_curmode == IEEE80211_MODE_11G && + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) ic->ic_flags |= IEEE80211_F_USEPROT; else @@ -1985,25 +2292,70 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * change dynamically */ ieee80211_set_shortslottime(ic, - ic->ic_curmode == IEEE80211_MODE_11A || + IEEE80211_IS_CHAN_A(ic->ic_bsschan) || (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); - ni->ni_capinfo = scan.capinfo; + ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) + | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); /* XXX statistic */ } if (scan.wme != NULL && (ni->ni_flags & IEEE80211_NODE_QOS) && ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) ieee80211_wme_updateparams(ic); + if (scan.ath != NULL) + ieee80211_parse_athparams(ni, scan.ath, wh); + if (scan.htcap != NULL) + ieee80211_parse_htcap(ni, scan.htcap); + if (scan.htinfo != NULL) + ieee80211_parse_htinfo(ni, scan.htinfo); if (scan.tim != NULL) { - struct ieee80211_tim_ie *ie = + struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) scan.tim; - - ni->ni_dtim_count = ie->tim_count; - ni->ni_dtim_period = ie->tim_period; +#if 0 + int aid = IEEE80211_AID(ni->ni_associd); + int ix = aid / NBBY; + int min = tim->tim_bitctl &~ 1; + int max = tim->tim_len + min - 4; + if ((tim->tim_bitctl&1) || + (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid))) { + /* + * XXX Do not let bg scan kick off + * we are expecting data. + */ + ic->ic_lastdata = ticks; + ieee80211_sta_pwrsave(ic, 0); + } +#endif + ni->ni_dtim_count = tim->tim_count; + ni->ni_dtim_period = tim->tim_period; } - if (ic->ic_flags & IEEE80211_F_SCAN) + /* + * If scanning, pass the info to the scan module. + * Otherwise, check if it's the right time to do + * a background scan. Background scanning must + * be enabled and we must not be operating in the + * turbo phase of dynamic turbo mode. Then, + * it's been a while since the last background + * scan and if no data frames have come through + * recently, kick off a scan. Note that this + * is the mechanism by which a background scan + * is started _and_ continued each time we + * return on-channel to receive a beacon from + * our ap. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, rstamp); + subtype, rssi, noise, rstamp); + } else if (contbgscan(ic)) { + ieee80211_bg_scan(ic); + } else if (startbgscan(ic)) { +#if 0 + /* wakeup if we are sleeing */ + ieee80211_set_pwrsave(ic, 0); +#endif + ieee80211_bg_scan(ic); + } return; } /* @@ -2023,7 +2375,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, rstamp); + subtype, rssi, noise, rstamp); return; } if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { @@ -2047,6 +2399,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } if (ni != NULL) { ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; } } @@ -2070,10 +2423,11 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] Atheros capabilities */ - ssid = rates = xrates = NULL; + ssid = rates = xrates = ath = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2084,10 +2438,17 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_VENDOR: + if (isatherosoui(frm)) + ath = frm; + break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { @@ -2139,11 +2500,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * response, reclaim immediately. */ ieee80211_free_node(ni); - } + } else if (ath != NULL) + ieee80211_saveath(ni, ath); break; case IEEE80211_FC0_SUBTYPE_AUTH: { - u_int16_t algo, seq, status; + uint16_t algo, seq, status; /* * auth frame format * [2] algorithm @@ -2151,10 +2513,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [2] status * [tlv*] challenge */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6); - algo = le16toh(*(u_int16_t *)frm); - seq = le16toh(*(u_int16_t *)(frm + 2)); - status = le16toh(*(u_int16_t *)(frm + 4)); + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, "[%s] recv auth frame with algorithm %d seq %d\n", ether_sprintf(wh->i_addr2), algo, seq); @@ -2187,10 +2549,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } if (algo == IEEE80211_AUTH_ALG_SHARED) ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, - rstamp, seq, status); + noise, rstamp, seq, status); else if (algo == IEEE80211_AUTH_ALG_OPEN) - ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, - status); + ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp, + seq, status); else { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, "auth", "unsupported alg %d", algo); @@ -2208,9 +2570,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { - u_int16_t capinfo, lintval; - struct ieee80211_rsnparms rsn; - u_int8_t reason; + uint16_t capinfo, lintval; + struct ieee80211_rsnparms rsnparms; + uint8_t reason; + int badwparsn; if (ic->ic_opmode != IEEE80211_M_HOSTAP || ic->ic_state != IEEE80211_S_RUN) { @@ -2234,8 +2597,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] supported rates * [tlv] extended supported rates * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] Atheros capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, ieee80211_mgt_subtype_name[subtype >> @@ -2244,13 +2609,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_assoc_bss++; return; } - capinfo = le16toh(*(u_int16_t *)frm); frm += 2; - lintval = le16toh(*(u_int16_t *)frm); frm += 2; + capinfo = le16toh(*(uint16_t *)frm); frm += 2; + lintval = le16toh(*(uint16_t *)frm); frm += 2; if (reassoc) frm += 6; /* ignore current AP info */ - ssid = rates = xrates = wpa = wme = NULL; + ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2263,21 +2628,39 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; /* XXX verify only one of RSN and WPA ie's? */ case IEEE80211_ELEMID_RSN: - wpa = frm; + rsn = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) wpa = frm; else if (iswmeinfo(frm)) wme = frm; - /* XXX Atheros OUI support */ + else if (isatherosoui(frm)) + ath = frm; + else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + if (ishtcapoui(frm) && htcap == NULL) + htcap = frm; + } break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); + if (htcap != NULL) { + IEEE80211_VERIFY_LENGTH(htcap[1], + htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + return); /* XXX just NULL out? */ + } if (ni == ic->ic_bss) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, @@ -2290,8 +2673,23 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_assoc_notauth++; return; } - /* assert right associstion security credentials */ - if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) { + /* assert right association security credentials */ + badwparsn = 0; + switch (ic->ic_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + if (wpa == NULL) + badwparsn = 1; + break; + case IEEE80211_F_WPA2: + if (rsn == NULL) + badwparsn = 1; + break; + case IEEE80211_F_WPA1|IEEE80211_F_WPA2: + if (wpa == NULL && rsn == NULL) + badwparsn = 1; + break; + } + if (badwparsn) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] no WPA/RSN IE in association request\n", @@ -2300,23 +2698,22 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_RSN_REQUIRED); ieee80211_node_leave(ic, ni); - /* XXX distinguish WPA/RSN? */ ic->ic_stats.is_rx_assoc_badwpaie++; - return; + return; } - if (wpa != NULL) { + if (wpa != NULL || rsn != NULL) { /* - * Parse WPA information element. Note that + * Parse WPA/RSN information element. Note that * we initialize the param block from the node * state so that information in the IE overrides * our defaults. The resulting parameters are * installed below after the association is assured. */ - rsn = ni->ni_rsn; - if (wpa[0] != IEEE80211_ELEMID_RSN) - reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); + rsnparms = ni->ni_rsn; + if (wpa != NULL) + reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh); else - reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); + reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh); if (reason != 0) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); @@ -2329,14 +2726,14 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", ether_sprintf(wh->i_addr2), - wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", - rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, - rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, - rsn.rsn_keymgmt, rsn.rsn_caps); + wpa != NULL ? "WPA" : "RSN", + rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen, + rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen, + rsnparms.rsn_keymgmt, rsnparms.rsn_caps); } /* discard challenge after association */ if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } /* NB: 802.11 spec says to ignore station's privacy bit */ @@ -2349,7 +2746,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * Disallow re-associate w/ invalid slot time setting. */ if (ni->ni_associd != 0 && - ic->ic_curmode == IEEE80211_MODE_11G && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { capinfomismatch(ni, wh, reassoc, resp, "slot time", capinfo); @@ -2372,28 +2769,59 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); return; } + /* XXX enforce PUREN */ + /* 802.11n-specific rateset handling */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) { + rate = ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | + IEEE80211_F_DOBRS); + if (rate & IEEE80211_RATE_BASIC) { + /* XXX 11n-specific stat */ + ratesetmismatch(ni, wh, reassoc, resp, + "HT", rate); + return; + } + ieee80211_ht_node_init(ni, htcap); + } else if (ni->ni_flags & IEEE80211_NODE_HT) + ieee80211_ht_node_cleanup(ni); ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; ni->ni_intval = lintval; ni->ni_capinfo = capinfo; - ni->ni_chan = ic->ic_bss->ni_chan; + ni->ni_chan = ic->ic_bsschan; ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; ni->ni_fhindex = ic->ic_bss->ni_fhindex; if (wpa != NULL) { /* - * Record WPA/RSN parameters for station, mark + * Record WPA parameters for station, mark * node as using WPA and record information element * for applications that require it. */ - ni->ni_rsn = rsn; + ni->ni_rsn = rsnparms; ieee80211_saveie(&ni->ni_wpa_ie, wpa); } else if (ni->ni_wpa_ie != NULL) { /* * Flush any state from a previous association. */ - FREE(ni->ni_wpa_ie, M_DEVBUF); + FREE(ni->ni_wpa_ie, M_80211_NODE); ni->ni_wpa_ie = NULL; } + if (rsn != NULL) { + /* + * Record RSN parameters for station, mark + * node as using WPA and record information element + * for applications that require it. + */ + ni->ni_rsn = rsnparms; + ieee80211_saveie(&ni->ni_rsn_ie, rsn); + } else if (ni->ni_rsn_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_rsn_ie, M_80211_NODE); + ni->ni_rsn_ie = NULL; + } if (wme != NULL) { /* * Record WME parameters for station, mark node @@ -2406,19 +2834,35 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, /* * Flush any state from a previous association. */ - FREE(ni->ni_wme_ie, M_DEVBUF); + FREE(ni->ni_wme_ie, M_80211_NODE); ni->ni_wme_ie = NULL; ni->ni_flags &= ~IEEE80211_NODE_QOS; } - ieee80211_deliver_l2uf(ni); + if (ath != NULL) { + /* + * Record ATH parameters for station, mark + * node with appropriate capabilities, and + * record the information element for + * applications that require it. + */ + ieee80211_saveath(ni, ath); + } else if (ni->ni_ath_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_ath_ie, M_80211_NODE); + ni->ni_ath_ie = NULL; + ni->ni_ath_flags = 0; + } ieee80211_node_join(ic, ni, resp); + ieee80211_deliver_l2uf(ni); break; } case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { - u_int16_t capinfo, associd; - u_int16_t status; + uint16_t capinfo, associd; + uint16_t status; if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_ASSOC) { @@ -2434,12 +2878,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] supported rates * [tlv] extended supported rates * [tlv] WME + * [tlv] HT capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); ni = ic->ic_bss; - capinfo = le16toh(*(u_int16_t *)frm); + capinfo = le16toh(*(uint16_t *)frm); frm += 2; - status = le16toh(*(u_int16_t *)frm); + status = le16toh(*(uint16_t *)frm); frm += 2; if (status != 0) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, @@ -2451,12 +2896,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_auth_fail++; /* XXX */ return; } - associd = le16toh(*(u_int16_t *)frm); + associd = le16toh(*(uint16_t *)frm); frm += 2; - rates = xrates = wpa = wme = NULL; + rates = xrates = wme = htcap = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; @@ -2464,6 +2909,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; case IEEE80211_ELEMID_VENDOR: if (iswmeoui(frm)) wme = frm; @@ -2474,6 +2922,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); rate = ieee80211_setup_rates(ni, rates, xrates, IEEE80211_F_JOIN | IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | @@ -2486,7 +2937,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, if (ni != ic->ic_bss) /* XXX never true? */ ni->ni_fails++; ic->ic_stats.is_rx_assoc_norate++; - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); return; } @@ -2503,7 +2955,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * * XXX may need different/additional driver callbacks? */ - if (ic->ic_curmode == IEEE80211_MODE_11A || + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; @@ -2512,34 +2964,37 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_flags |= IEEE80211_F_USEBARKER; } ieee80211_set_shortslottime(ic, - ic->ic_curmode == IEEE80211_MODE_11A || + IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); /* * Honor ERP protection. * * NB: ni_erp should zero for non-11g operation. - * XXX check ic_curmode anyway? */ - if (ic->ic_curmode == IEEE80211_MODE_11G && + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) ic->ic_flags |= IEEE80211_F_USEPROT; else ic->ic_flags &= ~IEEE80211_F_USEPROT; IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc success: %s preamble, %s slot time%s%s\n", + "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n", ether_sprintf(wh->i_addr2), ISREASSOC(subtype) ? "re" : "", ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", - ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" ); ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); break; } case IEEE80211_FC0_SUBTYPE_DEAUTH: { - u_int16_t reason; + uint16_t reason; if (ic->ic_state == IEEE80211_S_SCAN) { ic->ic_stats.is_rx_mgtdiscard++; @@ -2549,8 +3004,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * deauth frame format * [2] reason */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2); - reason = le16toh(*(u_int16_t *)frm); + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_deauth++; IEEE80211_NODE_STAT(ni, rx_deauth); @@ -2565,7 +3020,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) @@ -2579,7 +3034,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } case IEEE80211_FC0_SUBTYPE_DISASSOC: { - u_int16_t reason; + uint16_t reason; if (ic->ic_state != IEEE80211_S_RUN && ic->ic_state != IEEE80211_S_ASSOC && @@ -2591,8 +3046,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * disassoc frame format * [2] reason */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2); - reason = le16toh(*(u_int16_t *)frm); + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_disassoc++; IEEE80211_NODE_STAT(ni, rx_disassoc); @@ -2606,8 +3061,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ether_sprintf(ni->ni_macaddr), reason); switch (ic->ic_opmode) { case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) @@ -2619,6 +3073,65 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } break; } + + case IEEE80211_FC0_SUBTYPE_ACTION: { + const struct ieee80211_action *ia; + + if (ic->ic_state != IEEE80211_S_RUN && + ic->ic_state != IEEE80211_S_ASSOC && + ic->ic_state != IEEE80211_S_AUTH) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * action frame format: + * [1] category + * [1] action + * [tlv] parameters + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action), return); + ia = (const struct ieee80211_action *) frm; + + ic->ic_stats.is_rx_action++; + IEEE80211_NODE_STAT(ni, rx_action); + + /* verify frame payloads but defer processing */ + /* XXX maybe push this to method */ + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + switch (ia->ia_action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbarequest), + return); + break; + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbaresponse), + return); + break; + case IEEE80211_ACTION_BA_DELBA: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_delba), + return); + break; + } + break; + case IEEE80211_ACTION_CAT_HT: + switch (ia->ia_action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_txchwidth), + return); + break; + } + break; + } + ic->ic_recv_action(ni, frm, efrm); + break; + } + default: IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); @@ -2632,76 +3145,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, #undef IEEE80211_VERIFY_ELEMENT /* - * Handle station power-save state change. - */ -static void -ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) -{ - struct ieee80211com *ic = ni->ni_ic; - struct mbuf *m, *mhead, *mtail; - int mcount; - - if (enable) { - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) - ic->ic_ps_sta++; - ni->ni_flags |= IEEE80211_NODE_PWR_MGT; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] power save mode on, %u sta's in ps mode\n", - ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); - return; - } - - if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) - ic->ic_ps_sta--; - ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] power save mode off, %u sta's in ps mode\n", - ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); - /* XXX if no stations in ps mode, flush mc frames */ - - /* - * Flush queued unicast frames. - */ - if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); /* just in case */ - return; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] flush ps queue, %u packets queued\n", - ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); - /* - * Unload the frames from the ps q but don't send them - * to the driver yet. We do this in two stages to minimize - * locking but also because there's no easy way to preserve - * ordering given the existing ifnet access mechanisms. - * XXX could be optimized - */ - IEEE80211_NODE_SAVEQ_LOCK(ni); - mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); - mhead = mtail = NULL; - for (;;) { - _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); - if (m == NULL) - break; - if (mhead == NULL) { - mhead = m; - m->m_nextpkt = NULL; - } else - mtail->m_nextpkt = m; - mtail = m; - } - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - if (mhead != NULL) { - /* XXX need different driver interface */ - /* XXX bypasses q max */ - IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); - } - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); -} - -/* * Process a received ps-poll frame. */ static void @@ -2710,7 +3153,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, { struct ieee80211_frame_min *wh; struct mbuf *m; - u_int16_t aid; + uint16_t aid; int qlen; wh = mtod(m0, struct ieee80211_frame_min *); @@ -2724,7 +3167,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, return; } - aid = le16toh(*(u_int16_t *)wh->i_dur); + aid = le16toh(*(uint16_t *)wh->i_dur); if (aid != ni->ni_associd) { IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, (struct ieee80211_frame *) wh, "ps-poll", @@ -2777,7 +3220,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, /* * Return the bssid of a frame. */ -static const u_int8_t * +static const uint8_t * ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) { if (ic->ic_opmode == IEEE80211_M_STA) @@ -2819,7 +3262,7 @@ ieee80211_note_frame(struct ieee80211com *ic, void ieee80211_note_mac(struct ieee80211com *ic, - const u_int8_t mac[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...) { char buf[128]; /* XXX */ @@ -2871,7 +3314,7 @@ ieee80211_discard_ie(struct ieee80211com *ic, void ieee80211_discard_mac(struct ieee80211com *ic, - const u_int8_t mac[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...) { va_list ap; diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 17befd23e1f2..5929eddad5a2 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -62,752 +62,15 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ioctl.h> -#include <dev/wi/if_wavelan_ieee.h> - #define IS_UP(_ic) \ (((_ic)->ic_ifp->if_flags & IFF_UP) && \ ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) #define IS_UP_AUTO(_ic) \ (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) +#define RESCAN 1 -/* - * XXX - * Wireless LAN specific configuration interface, which is compatible - * with wicontrol(8). - */ - -struct wi_read_ap_args { - int i; /* result count */ - struct wi_apinfo *ap; /* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_ap_result(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_ap_args *sa = arg; - struct wi_apinfo *ap = sa->ap; - struct ieee80211_rateset *rs; - int j; - - if ((caddr_t)(ap + 1) > sa->max) - return; - memset(ap, 0, sizeof(struct wi_apinfo)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); - ap->namelen = ic->ic_des_esslen; - if (ic->ic_des_esslen) - memcpy(ap->name, ic->ic_des_essid, - ic->ic_des_esslen); - } else { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); - ap->namelen = ni->ni_esslen; - if (ni->ni_esslen) - memcpy(ap->name, ni->ni_essid, - ni->ni_esslen); - } - ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); - ap->signal = ic->ic_node_getrssi(ni); - ap->capinfo = ni->ni_capinfo; - ap->interval = ni->ni_intval; - rs = &ni->ni_rates; - for (j = 0; j < rs->rs_nrates; j++) { - if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { - ap->rate = (rs->rs_rates[j] & - IEEE80211_RATE_VAL) * 5; /* XXX */ - } - } - sa->i++; - sa->ap++; -} - -struct wi_read_prism2_args { - int i; /* result count */ - struct wi_scan_res *res;/* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_prism2_result(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_prism2_args *sa = arg; - struct wi_scan_res *res = sa->res; - - if ((caddr_t)(res + 1) > sa->max) - return; - res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); - res->wi_noise = 0; - res->wi_signal = ic->ic_node_getrssi(ni); - IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); - res->wi_interval = ni->ni_intval; - res->wi_capinfo = ni->ni_capinfo; - res->wi_ssid_len = ni->ni_esslen; - memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); - /* NB: assumes wi_srates holds <= ni->ni_rates */ - memcpy(res->wi_srates, ni->ni_rates.rs_rates, - sizeof(res->wi_srates)); - if (ni->ni_rates.rs_nrates < 10) - res->wi_srates[ni->ni_rates.rs_nrates] = 0; - res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; - res->wi_rsvd = 0; - - sa->i++; - sa->res++; -} - -struct wi_read_sigcache_args { - int i; /* result count */ - struct wi_sigcache *wsc;/* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_sigcache(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_sigcache_args *sa = arg; - struct wi_sigcache *wsc = sa->wsc; - - if ((caddr_t)(wsc + 1) > sa->max) - return; - memset(wsc, 0, sizeof(struct wi_sigcache)); - IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); - wsc->signal = ic->ic_node_getrssi(ni); - - sa->wsc++; - sa->i++; -} - -int -ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) -{ - struct ifnet *ifp = ic->ic_ifp; - int i, j, error; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_req wreq; - struct wi_ltv_keys *keys; - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return error; - wreq.wi_len = 0; - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - /* nothing appropriate */ - break; - case WI_RID_NODENAME: - strcpy((char *)&wreq.wi_val[1], hostname); - wreq.wi_val[0] = htole16(strlen(hostname)); - wreq.wi_len = (1 + strlen(hostname) + 1) / 2; - break; - case WI_RID_CURRENT_SSID: - if (ic->ic_state != IEEE80211_S_RUN) { - wreq.wi_val[0] = 0; - wreq.wi_len = 1; - break; - } - wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); - memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, - ic->ic_bss->ni_esslen); - wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; - break; - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - wreq.wi_val[0] = htole16(ic->ic_des_esslen); - memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); - wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; - break; - case WI_RID_CURRENT_BSSID: - if (ic->ic_state == IEEE80211_S_RUN) - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); - else - memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_CHANNEL_LIST: - memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) - if (isset(ic->ic_chan_active, i)) { - setbit((u_int8_t *)wreq.wi_val, j); - wreq.wi_len = j / 16 + 1; - } - break; - case WI_RID_OWN_CHNL: - wreq.wi_val[0] = htole16( - ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); - wreq.wi_len = 1; - break; - case WI_RID_CURRENT_CHAN: - wreq.wi_val[0] = htole16( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - wreq.wi_len = 1; - break; - case WI_RID_COMMS_QUALITY: - wreq.wi_val[0] = 0; /* quality */ - wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); - wreq.wi_val[2] = 0; /* noise */ - wreq.wi_len = 3; - break; - case WI_RID_PROMISC: - wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_PORTTYPE: - wreq.wi_val[0] = htole16(ic->ic_opmode); - wreq.wi_len = 1; - break; - case WI_RID_MAC_NODE: - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_TX_RATE: - if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) - wreq.wi_val[0] = 0; /* auto */ - else - wreq.wi_val[0] = htole16( - (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_CUR_TX_RATE: - wreq.wi_val[0] = htole16( - (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_RTS_THRESH: - wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); - wreq.wi_len = 1; - break; - case WI_RID_CREATE_IBSS: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MICROWAVE_OVEN: - wreq.wi_val[0] = 0; /* no ... not supported */ - wreq.wi_len = 1; - break; - case WI_RID_ROAMING_MODE: - wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ - wreq.wi_len = 1; - break; - case WI_RID_SYSTEM_SCALE: - wreq.wi_val[0] = htole16(1); /* low density ... not supp */ - wreq.wi_len = 1; - break; - case WI_RID_PM_ENABLED: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MAX_SLEEP: - wreq.wi_val[0] = htole16(ic->ic_lintval); - wreq.wi_len = 1; - break; - case WI_RID_CUR_BEACON_INT: - wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); - wreq.wi_len = 1; - break; - case WI_RID_WEP_AVAIL: - wreq.wi_val[0] = htole16(1); /* always available */ - wreq.wi_len = 1; - break; - case WI_RID_CNFAUTHMODE: - wreq.wi_val[0] = htole16(1); /* TODO: open system only */ - wreq.wi_len = 1; - break; - case WI_RID_ENCRYPTION: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_TX_CRYPT_KEY: - wreq.wi_val[0] = htole16(ic->ic_def_txkey); - wreq.wi_len = 1; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - keys = (struct wi_ltv_keys *)&wreq; - /* do not show keys to non-root user */ - error = priv_check(curthread, PRIV_NET80211_GETKEY); - if (error) { - memset(keys, 0, sizeof(*keys)); - error = 0; - break; - } - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keys->wi_keys[i].wi_keylen = - htole16(ic->ic_nw_keys[i].wk_keylen); - memcpy(keys->wi_keys[i].wi_keydat, - ic->ic_nw_keys[i].wk_key, - ic->ic_nw_keys[i].wk_keylen); - } - wreq.wi_len = sizeof(*keys) / 2; - break; - case WI_RID_MAX_DATALEN: - wreq.wi_val[0] = htole16(ic->ic_fragthreshold); - wreq.wi_len = 1; - break; - case WI_RID_IFACE_STATS: - /* XXX: should be implemented in lower drivers */ - break; - case WI_RID_READ_APS: - /* - * Don't return results until active scan completes. - */ - if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { - struct wi_read_ap_args args; - - args.i = 0; - args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, - wi_read_ap_result, &args); - memcpy(wreq.wi_val, &args.i, sizeof(args.i)); - wreq.wi_len = (sizeof(int) + - sizeof(struct wi_apinfo) * args.i) / 2; - } else - error = EINPROGRESS; - break; - case WI_RID_PRISM2: - /* NB: we lie so WI_RID_SCAN_RES can include rates */ - wreq.wi_val[0] = 1; - wreq.wi_len = sizeof(u_int16_t) / 2; - break; - case WI_RID_SCAN_RES: /* compatibility interface */ - if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { - struct wi_read_prism2_args args; - struct wi_scan_p2_hdr *p2; - - /* NB: use Prism2 format so we can include rate info */ - p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; - args.i = 0; - args.res = (void *)&p2[1]; - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, - wi_read_prism2_result, &args); - p2->wi_rsvd = 0; - p2->wi_reason = args.i; - wreq.wi_len = (sizeof(*p2) + - sizeof(struct wi_scan_res) * args.i) / 2; - } else - error = EINPROGRESS; - break; - case WI_RID_READ_CACHE: { - struct wi_read_sigcache_args args; - args.i = 0; - args.wsc = (struct wi_sigcache *) wreq.wi_val; - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); - wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; - break; - } - default: - error = EINVAL; - break; - } - if (error == 0) { - wreq.wi_len++; - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); - } - return error; -} - -static int -findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) -{ -#define IEEERATE(_ic,_m,_i) \ - ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) - int i, nrates = ic->ic_sup_rates[mode].rs_nrates; - for (i = 0; i < nrates; i++) - if (IEEERATE(ic, mode, i) == rate) - return i; - return -1; -#undef IEEERATE -} - -/* - * Prepare to do a user-initiated scan for AP's. If no - * current/default channel is setup or the current channel - * is invalid then pick the first available channel from - * the active list as the place to start the scan. - */ -static int -ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) -{ - - /* - * XXX don't permit a scan to be started unless we - * know the device is ready. For the moment this means - * the device is marked up as this is the required to - * initialize the hardware. It would be better to permit - * scanning prior to being up but that'll require some - * changes to the infrastructure. - */ - if (!IS_UP(ic)) - return EINVAL; - memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); - /* - * We force the state to INIT before calling ieee80211_new_state - * to get ieee80211_begin_scan called. We really want to scan w/o - * altering the current state but that's not possible right now. - */ - /* XXX handle proberequest case */ - ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ - return 0; -} - -int -ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) -{ - struct ifnet *ifp = ic->ic_ifp; - int i, j, len, error, rate; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_ltv_keys *keys; - struct wi_req wreq; - u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return error; - len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - case WI_RID_NODENAME: - return EPERM; - case WI_RID_CURRENT_SSID: - return EPERM; - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - if (le16toh(wreq.wi_val[0]) * 2 > len || - le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { - error = ENOSPC; - break; - } - memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); - ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; - memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); - error = ENETRESET; - break; - case WI_RID_CURRENT_BSSID: - return EPERM; - case WI_RID_OWN_CHNL: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i < 0 || - i > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, i)) - return EINVAL; - ic->ic_ibss_chan = &ic->ic_channels[i]; - if (ic->ic_opmode == IEEE80211_M_MONITOR) - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - else - error = ENETRESET; - break; - case WI_RID_CURRENT_CHAN: - return EPERM; - case WI_RID_COMMS_QUALITY: - return EPERM; - case WI_RID_PROMISC: - if (len != 2) - return EINVAL; - if (ifp->if_flags & IFF_PROMISC) { - if (wreq.wi_val[0] == 0) { - ifp->if_flags &= ~IFF_PROMISC; - error = ENETRESET; - } - } else { - if (wreq.wi_val[0] != 0) { - ifp->if_flags |= IFF_PROMISC; - error = ENETRESET; - } - } - break; - case WI_RID_PORTTYPE: - if (len != 2) - return EINVAL; - switch (le16toh(wreq.wi_val[0])) { - case IEEE80211_M_STA: - break; - case IEEE80211_M_IBSS: - if (!(ic->ic_caps & IEEE80211_C_IBSS)) - return EINVAL; - break; - case IEEE80211_M_AHDEMO: - if (ic->ic_phytype != IEEE80211_T_DS || - !(ic->ic_caps & IEEE80211_C_AHDEMO)) - return EINVAL; - break; - case IEEE80211_M_HOSTAP: - if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) - return EINVAL; - break; - default: - return EINVAL; - } - if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { - ic->ic_opmode = le16toh(wreq.wi_val[0]); - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - break; -#if 0 - case WI_RID_MAC_NODE: - if (len != IEEE80211_ADDR_LEN) - return EINVAL; - IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); - /* if_init will copy lladdr into ic_myaddr */ - error = ENETRESET; - break; -#endif - case WI_RID_TX_RATE: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] == 0) { - /* auto */ - ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; - break; - } - rate = 2 * le16toh(wreq.wi_val[0]); - if (ic->ic_curmode == IEEE80211_MODE_AUTO) { - /* - * In autoselect mode search for the rate. We take - * the first instance which may not be right, but we - * are limited by the interface. Note that we also - * lock the mode to insure the rate is meaningful - * when it is used. - */ - for (j = IEEE80211_MODE_11A; - j < IEEE80211_MODE_MAX; j++) { - if (isclr(ic->ic_modecaps, j)) - continue; - i = findrate(ic, j, rate); - if (i != -1) { - /* lock mode too */ - ic->ic_curmode = j; - goto setrate; - } - } - } else { - i = findrate(ic, ic->ic_curmode, rate); - if (i != -1) - goto setrate; - } - return EINVAL; - setrate: - ic->ic_fixed_rate = i; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_CUR_TX_RATE: - return EPERM; - case WI_RID_RTS_THRESH: - if (len != 2) - return EINVAL; - if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) - return EINVAL; /* TODO: RTS */ - break; - case WI_RID_CREATE_IBSS: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { - ic->ic_flags |= IEEE80211_F_IBSSON; - if (ic->ic_opmode == IEEE80211_M_IBSS && - ic->ic_state == IEEE80211_S_SCAN) - error = IS_UP_AUTO(ic) ? ENETRESET : 0; - } - } else { - if (ic->ic_flags & IEEE80211_F_IBSSON) { - ic->ic_flags &= ~IEEE80211_F_IBSSON; - if (ic->ic_flags & IEEE80211_F_SIBSS) { - ic->ic_flags &= ~IEEE80211_F_SIBSS; - error = IS_UP_AUTO(ic) ? ENETRESET : 0; - } - } - } - break; - case WI_RID_MICROWAVE_OVEN: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) - return EINVAL; /* not supported */ - break; - case WI_RID_ROAMING_MODE: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i > IEEE80211_ROAMING_MANUAL) - return EINVAL; /* not supported */ - ic->ic_roaming = i; - break; - case WI_RID_SYSTEM_SCALE: - if (len != 2) - return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) - return EINVAL; /* not supported */ - break; - case WI_RID_PM_ENABLED: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - } else { - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - } - break; - case WI_RID_MAX_SLEEP: - if (len != 2) - return EINVAL; - ic->ic_lintval = le16toh(wreq.wi_val[0]); - if (ic->ic_flags & IEEE80211_F_PMGTON) - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_CUR_BEACON_INT: - return EPERM; - case WI_RID_WEP_AVAIL: - return EPERM; - case WI_RID_CNFAUTHMODE: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i > IEEE80211_AUTH_WPA) - return EINVAL; - ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ - error = ENETRESET; - break; - case WI_RID_ENCRYPTION: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { - ic->ic_flags |= IEEE80211_F_PRIVACY; - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_PRIVACY) { - ic->ic_flags &= ~IEEE80211_F_PRIVACY; - error = ENETRESET; - } - } - break; - case WI_RID_TX_CRYPT_KEY: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i >= IEEE80211_WEP_NKID) - return EINVAL; - ic->ic_def_txkey = i; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - if (len != sizeof(struct wi_ltv_keys)) - return EINVAL; - keys = (struct wi_ltv_keys *)&wreq; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - len = le16toh(keys->wi_keys[i].wi_keylen); - if (len != 0 && len < IEEE80211_WEP_KEYLEN) - return EINVAL; - if (len > IEEE80211_KEYBUF_SIZE) - return EINVAL; - } - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - struct ieee80211_key *k = &ic->ic_nw_keys[i]; - - len = le16toh(keys->wi_keys[i].wi_keylen); - k->wk_keylen = len; - k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; - memset(k->wk_key, 0, sizeof(k->wk_key)); - memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); -#if 0 - k->wk_type = IEEE80211_CIPHER_WEP; -#endif - } - error = ENETRESET; - break; - case WI_RID_MAX_DATALEN: - if (len != 2) - return EINVAL; - len = le16toh(wreq.wi_val[0]); - if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) - return EINVAL; - ic->ic_fragthreshold = len; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_IFACE_STATS: - error = EPERM; - break; - case WI_RID_SCAN_REQ: /* XXX wicontrol */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - error = ieee80211_setupscan(ic, ic->ic_chan_avail); - if (error == 0) - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - break; - case WI_RID_SCAN_APS: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - len--; /* XXX: tx rate? */ - /* FALLTHRU */ - case WI_RID_CHANNEL_LIST: - memset(chanlist, 0, sizeof(chanlist)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { - if ((j / 8) >= len) - break; - if (isclr((u_int8_t *)wreq.wi_val, j)) - continue; - if (isclr(ic->ic_chan_active, i)) { - if (wreq.wi_type != WI_RID_CHANNEL_LIST) - continue; - if (isclr(ic->ic_chan_avail, i)) - return EPERM; - } - setbit(chanlist, i); - } - error = ieee80211_setupscan(ic, chanlist); - if (wreq.wi_type == WI_RID_CHANNEL_LIST) { - /* NB: ignore error from ieee80211_setupscan */ - error = ENETRESET; - } else if (error == 0) - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - break; - default: - error = EINVAL; - break; - } - if (error == ENETRESET && !IS_UP_AUTO(ic)) - error = 0; - return error; -} +static struct ieee80211_channel *findchannel(struct ieee80211com *, + int ieee, int mode); static int cap2cipher(int flag) @@ -889,37 +152,21 @@ ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) static int ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) { - struct ieee80211req_chaninfo chans; /* XXX off stack? */ - int i, space; + int space; - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - memset(&chans, 0, sizeof(chans)); - for (; i <= IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_avail, i)) { - struct ieee80211_channel *c = &ic->ic_channels[i]; - chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; - chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; - chans.ic_nchans++; - } space = __offsetof(struct ieee80211req_chaninfo, - ic_chans[chans.ic_nchans]); + ic_chans[ic->ic_nchans]); if (space > ireq->i_len) space = ireq->i_len; - return copyout(&chans, ireq->i_data, space); + /* XXX assumes compatible layout */ + return copyout(&ic->ic_nchans, ireq->i_data, space); } static int -ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) +ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req) { struct ieee80211_node *ni; - struct ieee80211req_wpaie wpaie; + struct ieee80211req_wpaie2 wpaie; int error; if (ireq->i_len < IEEE80211_ADDR_LEN) @@ -929,7 +176,7 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) return error; ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; /* XXX */ memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); if (ni->ni_wpa_ie != NULL) { int ielen = ni->ni_wpa_ie[1] + 2; @@ -937,9 +184,29 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) ielen = sizeof(wpaie.wpa_ie); memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); } + if (req == IEEE80211_IOC_WPAIE2) { + memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie)); + if (ni->ni_rsn_ie != NULL) { + int ielen = ni->ni_rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.rsn_ie)) + ielen = sizeof(wpaie.rsn_ie); + memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie2)) + ireq->i_len = sizeof(struct ieee80211req_wpaie2); + } else { + /* compatibility op, may overwrite wpa ie */ + /* XXX check ic_flags? */ + if (ni->ni_rsn_ie != NULL) { + int ielen = ni->ni_rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie)) + ireq->i_len = sizeof(struct ieee80211req_wpaie); + } ieee80211_free_node(ni); - if (ireq->i_len > sizeof(wpaie)) - ireq->i_len = sizeof(wpaie); return copyout(&wpaie, ireq->i_data, ireq->i_len); } @@ -947,7 +214,7 @@ static int ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); int error; @@ -957,213 +224,250 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, macaddr); - if (ni == NULL) { - /* XXX special-case sta-mode until bss is node in ic_sta */ - if (ic->ic_opmode != IEEE80211_M_STA) - return ENOENT; - ni = ieee80211_ref_node(ic->ic_bss); - } + if (ni == NULL) + return EINVAL; if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) ireq->i_len = sizeof(struct ieee80211req_sta_stats); /* NB: copy out only the statistics */ - error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, + error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off, ireq->i_len - off); ieee80211_free_node(ni); return error; } +static __inline uint8_t * +copyie(uint8_t *cp, const uint8_t *ie) +{ + if (ie != NULL) { + memcpy(cp, ie, 2+ie[1]); + cp += 2+ie[1]; + } + return cp; +} + #ifdef COMPAT_FREEBSD6 #define IEEE80211_IOC_SCAN_RESULTS_OLD 24 struct scan_result_old { - u_int16_t isr_len; /* length (mult of 4) */ - u_int16_t isr_freq; /* MHz */ - u_int16_t isr_flags; /* channel flags */ - u_int8_t isr_noise; - u_int8_t isr_rssi; - u_int8_t isr_intval; /* beacon interval */ - u_int8_t isr_capinfo; /* capabilities */ - u_int8_t isr_erp; /* ERP element */ - u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; - u_int8_t isr_nrates; - u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; - u_int8_t isr_ssid_len; /* SSID length */ - u_int8_t isr_ie_len; /* IE length */ - u_int8_t isr_pad[5]; + uint16_t isr_len; /* length (mult of 4) */ + uint16_t isr_freq; /* MHz */ + uint16_t isr_flags; /* channel flags */ + uint8_t isr_noise; + uint8_t isr_rssi; + uint8_t isr_intval; /* beacon interval */ + uint8_t isr_capinfo; /* capabilities */ + uint8_t isr_erp; /* ERP element */ + uint8_t isr_bssid[IEEE80211_ADDR_LEN]; + uint8_t isr_nrates; + uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isr_ssid_len; /* SSID length */ + uint8_t isr_ie_len; /* IE length */ + uint8_t isr_pad[5]; /* variable length SSID followed by IE data */ }; +struct oscanreq { + struct scan_result_old *sr; + size_t space; +}; + +static size_t +old_scan_space(const struct ieee80211_scan_entry *se, int *ielen) +{ + size_t len; + + *ielen = 0; + if (se->se_wpa_ie != NULL) + *ielen += 2+se->se_wpa_ie[1]; + if (se->se_wme_ie != NULL) + *ielen += 2+se->se_wme_ie[1]; + /* + * NB: ie's can be no more than 255 bytes and the max 802.11 + * packet is <3Kbytes so we are sure this doesn't overflow + * 16-bits; if this is a concern we can drop the ie's. + */ + len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen; + return roundup(len, sizeof(uint32_t)); +} + static void -old_get_scan_result(struct scan_result_old *sr, - const struct ieee80211_node *ni) +old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se) { - struct ieee80211com *ic = ni->ni_ic; - u_int ielen; + struct oscanreq *req = arg; + int ielen; + + req->space += old_scan_space(se, &ielen); +} + +static void +old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se) +{ + struct oscanreq *req = arg; + struct scan_result_old *sr; + int ielen, len, nr, nxr; + uint8_t *cp; + + len = old_scan_space(se, &ielen); + if (len > req->space) + return; + sr = req->sr; memset(sr, 0, sizeof(*sr)); - sr->isr_ssid_len = ni->ni_esslen; - ielen = 0; - if (ni->ni_wpa_ie != NULL) - ielen += 2+ni->ni_wpa_ie[1]; - if (ni->ni_wme_ie != NULL) - ielen += 2+ni->ni_wme_ie[1]; + sr->isr_ssid_len = se->se_ssid[1]; /* NB: beware of overflow, isr_ie_len is 8 bits */ sr->isr_ie_len = (ielen > 255 ? 0 : ielen); - sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; - sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); - if (ni->ni_chan != IEEE80211_CHAN_ANYC) { - sr->isr_freq = ni->ni_chan->ic_freq; - sr->isr_flags = ni->ni_chan->ic_flags; + sr->isr_len = len; + sr->isr_freq = se->se_chan->ic_freq; + sr->isr_flags = se->se_chan->ic_flags; + sr->isr_rssi = se->se_rssi; + sr->isr_noise = se->se_noise; + sr->isr_intval = se->se_intval; + sr->isr_capinfo = se->se_capinfo; + sr->isr_erp = se->se_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); + nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); + memcpy(sr->isr_rates, se->se_rates+2, nr); + nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); + memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); + sr->isr_nrates = nr + nxr; + + cp = (uint8_t *)(sr+1); + memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); + cp += sr->isr_ssid_len; + if (sr->isr_ie_len) { + cp = copyie(cp, se->se_wpa_ie); + cp = copyie(cp, se->se_wme_ie); } - sr->isr_rssi = ic->ic_node_getrssi(ni); - sr->isr_intval = ni->ni_intval; - sr->isr_capinfo = ni->ni_capinfo; - sr->isr_erp = ni->ni_erp; - IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); - sr->isr_nrates = ni->ni_rates.rs_nrates; - if (sr->isr_nrates > 15) - sr->isr_nrates = 15; - memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); + + req->space -= len; + req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len); } static int old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) { - union { - struct scan_result_old res; - char data[512]; /* XXX shrink? */ - } u; - struct scan_result_old *sr = &u.res; - struct ieee80211_node_table *nt; - struct ieee80211_node *ni; - int error, space; - u_int8_t *p, *cp; + struct oscanreq req; + int error; + + if (ireq->i_len < sizeof(struct scan_result_old)) + return EFAULT; - p = ireq->i_data; - space = ireq->i_len; error = 0; - /* XXX locking */ - nt = &ic->ic_scan; - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { - /* NB: skip pre-scan node state */ - if (ni->ni_chan == IEEE80211_CHAN_ANYC) - continue; - old_get_scan_result(sr, ni); - if (sr->isr_len > sizeof(u)) - continue; /* XXX */ - if (space < sr->isr_len) - break; - cp = (u_int8_t *)(sr+1); - memcpy(cp, ni->ni_essid, ni->ni_esslen); - cp += ni->ni_esslen; - if (sr->isr_ie_len) { - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; - } - } - error = copyout(sr, p, sr->isr_len); - if (error) - break; - p += sr->isr_len; - space -= sr->isr_len; - } - ireq->i_len -= space; + req.space = 0; + ieee80211_scan_iterate(ic, old_get_scan_space, &req); + if (req.space > ireq->i_len) + req.space = ireq->i_len; + if (req.space > 0) { + size_t space; + void *p; + + space = req.space; + /* XXX M_WAITOK after driver lock released */ + MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); + if (p == NULL) + return ENOMEM; + req.sr = p; + ieee80211_scan_iterate(ic, old_get_scan_result, &req); + ireq->i_len = space - req.space; + error = copyout(p, ireq->i_data, ireq->i_len); + FREE(p, M_TEMP); + } else + ireq->i_len = 0; + return error; } #endif /* COMPAT_FREEBSD6 */ -struct scanresultsreq { +struct scanreq { struct ieee80211req_scan_result *sr; - size_t space; + size_t space; }; static size_t -scan_space(const struct ieee80211_node *ni, size_t *ielen) +scan_space(const struct ieee80211_scan_entry *se, int *ielen) { size_t len; *ielen = 0; - if (ni->ni_wpa_ie != NULL) - *ielen += 2+ni->ni_wpa_ie[1]; - if (ni->ni_wme_ie != NULL) - *ielen += 2+ni->ni_wme_ie[1]; + if (se->se_wpa_ie != NULL) + *ielen += 2+se->se_wpa_ie[1]; + if (se->se_rsn_ie != NULL) + *ielen += 2+se->se_rsn_ie[1]; + if (se->se_wme_ie != NULL) + *ielen += 2+se->se_wme_ie[1]; + if (se->se_ath_ie != NULL) + *ielen += 2+se->se_ath_ie[1]; /* * NB: ie's can be no more than 255 bytes and the max 802.11 * packet is <3Kbytes so we are sure this doesn't overflow * 16-bits; if this is a concern we can drop the ie's. */ - len = sizeof(struct ieee80211req_scan_result) + ni->ni_esslen + *ielen; - return roundup(len, sizeof(u_int32_t)); + len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen; + return roundup(len, sizeof(uint32_t)); } static void -get_scan_space(void *arg, struct ieee80211_node *ni) +get_scan_space(void *arg, const struct ieee80211_scan_entry *se) { - struct scanresultsreq *req = arg; - size_t ielen; + struct scanreq *req = arg; + int ielen; - req->space += scan_space(ni, &ielen); + req->space += scan_space(se, &ielen); } static void -get_scan_result(void *arg, struct ieee80211_node *ni) +get_scan_result(void *arg, const struct ieee80211_scan_entry *se) { - struct scanresultsreq *req = arg; - struct ieee80211com *ic = ni->ni_ic; + struct scanreq *req = arg; struct ieee80211req_scan_result *sr; - size_t ielen, len; - u_int8_t *cp; + int ielen, len, nr, nxr; + uint8_t *cp; - len = scan_space(ni, &ielen); + len = scan_space(se, &ielen); if (len > req->space) return; + sr = req->sr; KASSERT(len <= 65535 && ielen <= 65535, - ("len %zu ssid %u ie %zu", len, ni->ni_esslen, ielen)); - sr->isr_len = len; - sr->isr_ssid_len = ni->ni_esslen; + ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen)); + sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); sr->isr_ie_len = ielen; - if (ni->ni_chan != IEEE80211_CHAN_ANYC) { - sr->isr_freq = ni->ni_chan->ic_freq; - sr->isr_flags = ni->ni_chan->ic_flags; - } - /* XXX need to rev driver apis for signal data */ - sr->isr_rssi = (int8_t) ic->ic_node_getrssi(ni); - sr->isr_intval = ni->ni_intval; - sr->isr_capinfo = ni->ni_capinfo; - sr->isr_erp = ni->ni_erp; - IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); - sr->isr_nrates = ni->ni_rates.rs_nrates; - if (sr->isr_nrates > 15) - sr->isr_nrates = 15; - memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); - cp = (u_int8_t *)(sr+1); - memcpy(cp, ni->ni_essid, ni->ni_esslen); - cp += ni->ni_esslen; - if (sr->isr_ie_len) { - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; - } + sr->isr_len = len; + sr->isr_freq = se->se_chan->ic_freq; + sr->isr_flags = se->se_chan->ic_flags; + sr->isr_rssi = se->se_rssi; + sr->isr_noise = se->se_noise; + sr->isr_intval = se->se_intval; + sr->isr_capinfo = se->se_capinfo; + sr->isr_erp = se->se_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); + nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); + memcpy(sr->isr_rates, se->se_rates+2, nr); + nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); + memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); + sr->isr_nrates = nr + nxr; + + sr->isr_ssid_len = se->se_ssid[1]; + cp = ((uint8_t *)sr) + sr->isr_ie_off; + memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); + + if (ielen) { + cp += sr->isr_ssid_len; + cp = copyie(cp, se->se_wpa_ie); + cp = copyie(cp, se->se_rsn_ie); + cp = copyie(cp, se->se_wme_ie); + cp = copyie(cp, se->se_ath_ie); + cp = copyie(cp, se->se_htcap_ie); } - req->sr = (struct ieee80211req_scan_result *)(((u_int8_t *)sr) + len); req->space -= len; + req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len); } static int ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) { - struct scanresultsreq req; + struct scanreq req; int error; if (ireq->i_len < sizeof(struct ieee80211req_scan_result)) @@ -1171,7 +475,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire error = 0; req.space = 0; - ieee80211_iterate_nodes(&ic->ic_scan, get_scan_space, &req); + ieee80211_scan_iterate(ic, get_scan_space, &req); if (req.space > ireq->i_len) req.space = ireq->i_len; if (req.space > 0) { @@ -1184,7 +488,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire if (p == NULL) return ENOMEM; req.sr = p; - ieee80211_iterate_nodes(&ic->ic_scan, get_scan_result, &req); + ieee80211_scan_iterate(ic, get_scan_result, &req); ireq->i_len = space - req.space; error = copyout(p, ireq->i_data, ireq->i_len); FREE(p, M_TEMP); @@ -1206,10 +510,14 @@ sta_space(const struct ieee80211_node *ni, size_t *ielen) *ielen = 0; if (ni->ni_wpa_ie != NULL) *ielen += 2+ni->ni_wpa_ie[1]; + if (ni->ni_rsn_ie != NULL) + *ielen += 2+ni->ni_rsn_ie[1]; if (ni->ni_wme_ie != NULL) *ielen += 2+ni->ni_wme_ie[1]; + if (ni->ni_ath_ie != NULL) + *ielen += 2+ni->ni_ath_ie[1]; return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, - sizeof(u_int32_t)); + sizeof(uint32_t)); } static void @@ -1232,7 +540,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni) struct ieee80211com *ic = ni->ni_ic; struct ieee80211req_sta_info *si; size_t ielen, len; - u_int8_t *cp; + uint8_t *cp; if (ic->ic_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ @@ -1243,14 +551,14 @@ get_sta_info(void *arg, struct ieee80211_node *ni) if (len > req->space) return; si = req->si; - KASSERT(len <= 65535 && ielen <= 65535, ("len %zu ie %zu", len, ielen)); si->isi_len = len; + si->isi_ie_off = sizeof(struct ieee80211req_sta_info); si->isi_ie_len = ielen; si->isi_freq = ni->ni_chan->ic_freq; si->isi_flags = ni->ni_chan->ic_flags; si->isi_state = ni->ni_flags; si->isi_authmode = ni->ni_authmode; - si->isi_rssi = ic->ic_node_getrssi(ni); + ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); si->isi_noise = 0; /* XXX */ si->isi_capinfo = ni->ni_capinfo; si->isi_erp = ni->ni_erp; @@ -1260,6 +568,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_nrates = 15; memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); si->isi_txrate = ni->ni_txrate; + si->isi_ie_len = ielen; si->isi_associd = ni->ni_associd; si->isi_txpower = ni->ni_txpower; si->isi_vlan = ni->ni_vlan; @@ -1279,17 +588,15 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_inact = ic->ic_inact_init; si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; - cp = (u_int8_t *)(si+1); - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; + if (ielen) { + cp = ((uint8_t *)si) + si->isi_ie_off; + cp = copyie(cp, ni->ni_wpa_ie); + cp = copyie(cp, ni->ni_rsn_ie); + cp = copyie(cp, ni->ni_wme_ie); + cp = copyie(cp, ni->ni_ath_ie); } - req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); + req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); req->space -= len; } @@ -1324,7 +631,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, else get_sta_info(&req, ni); ireq->i_len = space - req.space; - error = copyout(p, (u_int8_t *) ireq->i_data+off, ireq->i_len); + error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len); FREE(p, M_TEMP); } else ireq->i_len = 0; @@ -1337,7 +644,7 @@ bad: static int ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) { - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_req, info); struct ieee80211_node *ni; int error; @@ -1351,12 +658,8 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) ni = NULL; } else { ni = ieee80211_find_node(&ic->ic_sta, macaddr); - if (ni == NULL) { - /* XXX special-case sta-mode until bss is in ic_sta */ - if (ic->ic_opmode != IEEE80211_M_STA) - return EINVAL; /* XXX */ - ni = ieee80211_ref_node(ic->ic_bss); - } + if (ni == NULL) + return EINVAL; } return getstainfo_common(ic, ireq, ni, off); } @@ -1445,6 +748,28 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) } /* + * Return the current ``state'' of an Atheros capbility. + * If associated in station mode report the negotiated + * setting. Otherwise report the current setting. + */ +static int +getathcap(struct ieee80211com *ic, int cap) +{ + if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN) + return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0; + else + return (ic->ic_flags & cap) != 0; +} + +static int +ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(struct ieee80211_channel)) + return EINVAL; + return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan)); +} + +/* * When building the kernel with -O2 on the i386 architecture, gcc * seems to want to inline this function into ieee80211_ioctl() * (which is the only routine that calls it). When this happens, @@ -1469,7 +794,7 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error = 0; u_int kid, len, m; - u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; switch (ireq->i_type) { @@ -1477,8 +802,8 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re switch (ic->ic_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: - ireq->i_len = ic->ic_des_esslen; - memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); + ireq->i_len = ic->ic_des_ssid[0].len; + memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len); break; default: ireq->i_len = ic->ic_bss->ni_esslen; @@ -1639,7 +964,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_WPAIE: - error = ieee80211_ioctl_getwpaie(ic, ireq); + error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); + break; + case IEEE80211_IOC_WPAIE2: + error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); break; #ifdef COMPAT_FREEBSD6 case IEEE80211_IOC_SCAN_RESULTS_OLD: @@ -1684,6 +1012,42 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re case IEEE80211_IOC_PUREG: ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; break; + case IEEE80211_IOC_FF: + ireq->i_val = getathcap(ic, IEEE80211_F_FF); + break; + case IEEE80211_IOC_TURBOP: + ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP); + break; + case IEEE80211_IOC_BGSCAN: + ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */ + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */ + break; + case IEEE80211_IOC_SCANVALID: + ireq->i_val = ic->ic_scanvalid/hz; /* seconds */ + break; + case IEEE80211_IOC_ROAM_RSSI_11A: + ireq->i_val = ic->ic_roam.rssi11a; + break; + case IEEE80211_IOC_ROAM_RSSI_11B: + ireq->i_val = ic->ic_roam.rssi11bOnly; + break; + case IEEE80211_IOC_ROAM_RSSI_11G: + ireq->i_val = ic->ic_roam.rssi11b; + break; + case IEEE80211_IOC_ROAM_RATE_11A: + ireq->i_val = ic->ic_roam.rate11a; + break; + case IEEE80211_IOC_ROAM_RATE_11B: + ireq->i_val = ic->ic_roam.rate11bOnly; + break; + case IEEE80211_IOC_ROAM_RATE_11G: + ireq->i_val = ic->ic_roam.rate11b; + break; case IEEE80211_IOC_MCAST_RATE: ireq->i_val = ic->ic_mcast_rate; break; @@ -1699,6 +1063,48 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re case IEEE80211_IOC_BMISSTHRESHOLD: ireq->i_val = ic->ic_bmissthreshold; break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_getcurchan(ic, ireq); + break; + case IEEE80211_IOC_SHORTGI: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI20; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI40; + break; + case IEEE80211_IOC_AMPDU: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX) + ireq->i_val |= 1; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */ + break; + case IEEE80211_IOC_AMPDU_DENSITY: + ireq->i_val = ic->ic_ampdu_density; + break; + case IEEE80211_IOC_AMSDU: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX) + ireq->i_val |= 1; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0; + break; + case IEEE80211_IOC_DOTH: + ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0; + break; + case IEEE80211_IOC_HTCOMPAT: + ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0; + break; default: error = EINVAL; break; @@ -1722,6 +1128,8 @@ ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) return EINVAL; if (ireq->i_len > IEEE80211_MAX_OPT_IE) return EINVAL; + /* NB: data.length is validated by the wireless extensions code */ + /* XXX M_WAITOK after driver lock released */ if (ireq->i_len > 0) { MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT); if (ie == NULL) @@ -1750,7 +1158,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) struct ieee80211req_key ik; struct ieee80211_node *ni; struct ieee80211_key *wk; - u_int16_t kid; + uint16_t kid; int error; if (ireq->i_len != sizeof(ik)) @@ -1827,8 +1235,8 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) if (error) return error; kid = dk.idk_keyix; - /* XXX u_int8_t -> u_int16_t */ - if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { + /* XXX uint8_t -> uint16_t */ + if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) { struct ieee80211_node *ni; if (ic->ic_opmode == IEEE80211_M_STA) { @@ -1870,6 +1278,32 @@ domlme(void *arg, struct ieee80211_node *ni) ieee80211_node_leave(ic, ni); } +struct scanlookup { + const uint8_t *mac; + int esslen; + const uint8_t *essid; + const struct ieee80211_scan_entry *se; +}; + +/* + * Match mac address and any ssid. + */ +static void +mlmelookup(void *arg, const struct ieee80211_scan_entry *se) +{ + struct scanlookup *look = arg; + + if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr)) + return; + if (look->esslen != 0) { + if (se->se_ssid[1] != look->esslen) + return; + if (memcmp(look->essid, se->se_ssid+2, look->esslen)) + return; + } + look->se = se; +} + static int ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) { @@ -1884,31 +1318,21 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) return error; switch (mlme.im_op) { case IEEE80211_MLME_ASSOC: - if (ic->ic_opmode != IEEE80211_M_STA) - return EINVAL; - /* XXX must be in S_SCAN state? */ - - if (mlme.im_ssid_len != 0) { - /* - * Desired ssid specified; must match both bssid and - * ssid to distinguish ap advertising multiple ssid's. - */ - ni = ieee80211_find_node_with_ssid(&ic->ic_scan, - mlme.im_macaddr, - mlme.im_ssid_len, mlme.im_ssid); - } else { - /* - * Normal case; just match bssid. - */ - ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); - } - if (ni == NULL) - return EINVAL; - if (!ieee80211_sta_join(ic, ni)) { - ieee80211_free_node(ni); - return EINVAL; + /* XXX ibss/ahdemo */ + if (ic->ic_opmode == IEEE80211_M_STA) { + struct scanlookup lookup; + + lookup.se = NULL; + lookup.mac = mlme.im_macaddr; + /* XXX use revised api w/ explicit ssid */ + lookup.esslen = ic->ic_des_ssid[0].len; + lookup.essid = ic->ic_des_ssid[0].ssid; + ieee80211_scan_iterate(ic, mlmelookup, &lookup); + if (lookup.se != NULL && + ieee80211_sta_join(ic, lookup.se)) + return 0; } - break; + return EINVAL; case IEEE80211_MLME_DISASSOC: case IEEE80211_MLME_DEAUTH: switch (ic->ic_opmode) { @@ -1956,7 +1380,7 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) static int ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) { - u_int8_t mac[IEEE80211_ADDR_LEN]; + uint8_t mac[IEEE80211_ADDR_LEN]; const struct ieee80211_aclator *acl = ic->ic_acl; int error; @@ -2020,7 +1444,7 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_chanlist list; u_char chanlist[IEEE80211_CHAN_BYTES]; - int i, j, error; + int i, j, nchan, error; if (ireq->i_len != sizeof(list)) return EINVAL; @@ -2036,34 +1460,31 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) i = 1; else i = 0; + nchan = 0; for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { /* * NB: silently discard unavailable channels so users * can specify 1-255 to get all available channels. */ - if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) + if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) { setbit(chanlist, i); + nchan++; + } } - if (ic->ic_ibss_chan == NULL || - isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) - if (isset(chanlist, i)) { - ic->ic_ibss_chan = &ic->ic_channels[i]; - goto found; - } - return EINVAL; /* no active channels */ -found: - ; - } + if (nchan == 0) + return EINVAL; + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ + isclr(chanlist, ic->ic_bsschan->ic_ieee)) + ic->ic_bsschan = IEEE80211_CHAN_ANYC; memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); - return IS_UP_AUTO(ic) ? ENETRESET : 0; + return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0; } static int ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; int error; /* @@ -2193,15 +1614,273 @@ cipher2cap(int cipher) } static int +find11gchannel(struct ieee80211com *ic, int start, int freq) +{ + const struct ieee80211_channel *c; + int i; + + for (i = start+1; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + /* NB: should not be needed but in case things are mis-sorted */ + for (i = 0; i < start; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + return 0; +} + +static struct ieee80211_channel * +findchannel(struct ieee80211com *ic, int ieee, int mode) +{ + static const u_int chanflags[IEEE80211_MODE_MAX] = { + 0, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */ + IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ + IEEE80211_CHAN_STURBO, /* IEEE80211_MODE_STURBO_A */ + /* NB: handled specially below */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */ + }; + u_int modeflags; + int i; + + KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); + modeflags = chanflags[mode]; + KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO, + ("no chanflags for mode %u", mode)); + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + + if (c->ic_ieee != ieee) + continue; + if (mode == IEEE80211_MODE_AUTO) { + /* ignore turbo channels for autoselect */ + if (IEEE80211_IS_CHAN_TURBO(c)) + continue; + /* + * XXX special-case 11b/g channels so we + * always select the g channel if both + * are present. + * XXX prefer HT to non-HT? + */ + if (!IEEE80211_IS_CHAN_B(c) || + !find11gchannel(ic, i, c->ic_freq)) + return c; + } else { + /* must check HT specially */ + if ((mode == IEEE80211_MODE_11NA || + mode == IEEE80211_MODE_11NG) && + !IEEE80211_IS_CHAN_HT(c)) + continue; + if ((c->ic_flags & modeflags) == modeflags) + return c; + } + } + return NULL; +} + +/* + * Check the specified against any desired mode (aka netband). + * This is only used (presently) when operating in hostap mode + * to enforce consistency. + */ +static int +check_mode_consistency(const struct ieee80211_channel *c, int mode) +{ + KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel")); + + switch (mode) { + case IEEE80211_MODE_11B: + return (IEEE80211_IS_CHAN_B(c)); + case IEEE80211_MODE_11G: + return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_11A: + return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_STURBO_A: + return (IEEE80211_IS_CHAN_STURBO(c)); + case IEEE80211_MODE_11NA: + return (IEEE80211_IS_CHAN_HTA(c)); + case IEEE80211_MODE_11NG: + return (IEEE80211_IS_CHAN_HTG(c)); + } + return 1; + +} + +/* + * Common code to set the current channel. If the device + * is up and running this may result in an immediate channel + * change or a kick of the state machine. + */ +static int +setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + int error; + + if (c != IEEE80211_CHAN_ANYC) { + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + !check_mode_consistency(c, ic->ic_des_mode)) + return EINVAL; + if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan) + return 0; /* NB: nothing to do */ + } + ic->ic_des_chan = c; + + error = 0; + if ((ic->ic_opmode == IEEE80211_M_MONITOR || + ic->ic_opmode == IEEE80211_M_WDS) && + ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * Monitor and wds modes can switch directly. + */ + ic->ic_curchan = ic->ic_des_chan; + if (ic->ic_state == IEEE80211_S_RUN) + ic->ic_set_channel(ic); + } else { + /* + * Need to go through the state machine in case we + * need to reassociate or the like. The state machine + * will pickup the desired channel and avoid scanning. + */ + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); + else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * When not up+running and a real channel has + * been specified fix the current channel so + * there is immediate feedback; e.g. via ifconfig. + */ + ic->ic_curchan = ic->ic_des_chan; + } + } + return error; +} + +/* + * Old api for setting the current channel; this is + * deprecated because channel numbers are ambiguous. + */ +static int +ieee80211_ioctl_setchannel(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_channel *c; + + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) { + return EINVAL; + } else { + struct ieee80211_channel *c2; + + c = findchannel(ic, ireq->i_val, ic->ic_des_mode); + if (c == NULL) { + c = findchannel(ic, ireq->i_val, + IEEE80211_MODE_AUTO); + if (c == NULL) + return EINVAL; + } + /* + * Fine tune channel selection based on desired mode: + * if 11b is requested, find the 11b version of any + * 11g channel returned, + * if static turbo, find the turbo version of any + * 11a channel return, + * if 11na is requested, find the ht version of any + * 11a channel returned, + * if 11ng is requested, find the ht version of any + * 11g channel returned, + * otherwise we should be ok with what we've got. + */ + switch (ic->ic_des_mode) { + case IEEE80211_MODE_11B: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11B); + /* NB: should not happen, =>'s 11g w/o 11b */ + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_TURBO_A: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_TURBO_A); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NA: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NA); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NG: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NG); + if (c2 != NULL) + c = c2; + } + break; + default: /* NB: no static turboG */ + break; + } + } + return setcurchan(ic, c); +} + +/* + * New/current api for setting the current channel; a complete + * channel description is provide so there is no ambiguity in + * identifying the channel. + */ +static int +ieee80211_ioctl_setcurchan(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_channel chan, *c; + int error; + + if (ireq->i_len != sizeof(chan)) + return EINVAL; + error = copyin(ireq->i_data, &chan, sizeof(chan)); + if (error != 0) + return error; + /* XXX 0xffff overflows 16-bit signed */ + if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else { + c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags); + if (c == NULL) + return EINVAL; + } + return setcurchan(ic, c); +} + +static int ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) { - static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error; const struct ieee80211_authenticator *auth; - u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; - u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; + uint8_t tmpbssid[IEEE80211_ADDR_LEN]; struct ieee80211_key *k; int j, caps; u_int kid; @@ -2215,10 +1894,12 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re error = copyin(ireq->i_data, tmpssid, ireq->i_len); if (error) break; - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = ireq->i_len; - memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); - error = ENETRESET; + memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + ic->ic_des_ssid[0].len = ireq->i_len; + memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len); + ic->ic_des_nssid = (ireq->i_len > 0); + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_WEP: switch (ireq->i_val) { @@ -2235,7 +1916,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; } - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_WEPKEY: kid = (u_int) ireq->i_val; @@ -2264,16 +1946,13 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re } else error = EINVAL; ieee80211_key_update_end(ic); - if (!error) /* NB: for compatibility */ - error = ENETRESET; break; case IEEE80211_IOC_WEPTXKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID && - (u_int16_t) kid != IEEE80211_KEYIX_NONE) + (uint16_t) kid != IEEE80211_KEYIX_NONE) return EINVAL; ic->ic_def_txkey = kid; - error = ENETRESET; /* push to hardware */ break; case IEEE80211_IOC_AUTHMODE: switch (ireq->i_val) { @@ -2313,48 +1992,11 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_bss->ni_authmode = ireq->i_val; /* XXX mixed/mode/usage? */ ic->ic_auth = auth; - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_CHANNEL: - /* XXX 0xffff overflows 16-bit signed */ - if (ireq->i_val == 0 || - ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) - ic->ic_des_chan = IEEE80211_CHAN_ANYC; - else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, ireq->i_val)) { - return EINVAL; - } else - ic->ic_ibss_chan = ic->ic_des_chan = - &ic->ic_channels[ireq->i_val]; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - error = ENETRESET; - break; - default: - /* - * If the desired channel has changed (to something - * other than any) and we're not already scanning, - * then kick the state machine. - */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - ic->ic_bss->ni_chan != ic->ic_des_chan && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) - error = ENETRESET; - break; - } - if (error == ENETRESET && - ic->ic_opmode == IEEE80211_M_MONITOR) { - if (IS_UP(ic)) { - /* - * Monitor mode can switch directly. - */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) - ic->ic_curchan = ic->ic_des_chan; - error = ic->ic_reset(ic->ic_ifp); - } else - error = 0; - } + error = ieee80211_ioctl_setchannel(ic, ireq); break; case IEEE80211_IOC_POWERSAVE: switch (ireq->i_val) { @@ -2402,14 +2044,15 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re return EINVAL; ic->ic_protmode = ireq->i_val; /* NB: if not operating in 11g this can wait */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_TXPOWER: if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) return EINVAL; - if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && - ireq->i_val < IEEE80211_TXPOWER_MAX)) + if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_TXPOWER_MAX)) return EINVAL; ic->ic_txpowlimit = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; @@ -2470,7 +2113,7 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; break; } - error = ENETRESET; /* XXX? */ + error = ENETRESET; break; case IEEE80211_IOC_WME: if (ireq->i_val) { @@ -2479,7 +2122,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags |= IEEE80211_F_WME; } else ic->ic_flags &= ~IEEE80211_F_WME; - error = ENETRESET; /* XXX maybe not for station? */ + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, 0); break; case IEEE80211_IOC_HIDESSID: if (ireq->i_val) @@ -2542,8 +2186,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re break; case IEEE80211_IOC_DRIVER_CAPS: /* NB: for testing */ - ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | - ((u_int16_t) ireq->i_len); + ic->ic_caps = (((uint16_t) ireq->i_val) << 16) | + ((uint16_t) ireq->i_len); break; case IEEE80211_IOC_KEYMGTALGS: /* XXX check */ @@ -2566,17 +2210,21 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags &= ~IEEE80211_F_DESBSSID; else ic->ic_flags |= IEEE80211_F_DESBSSID; - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_CHANLIST: error = ieee80211_ioctl_setchanlist(ic, ireq); break; case IEEE80211_IOC_SCAN_REQ: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ - break; - error = ieee80211_setupscan(ic, ic->ic_chan_avail); - if (error == 0) /* XXX background scan */ - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + if (!IS_UP(ic)) + return EINVAL; + (void) ieee80211_start_scan(ic, + IEEE80211_SCAN_ACTIVE | + IEEE80211_SCAN_NOPICK | + IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER, + /* XXX use ioctl params */ + ic->ic_des_nssid, ic->ic_des_ssid); break; case IEEE80211_IOC_ADDMAC: case IEEE80211_IOC_DELMAC: @@ -2627,9 +2275,72 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re else ic->ic_flags &= ~IEEE80211_F_PUREG; /* NB: reset only if we're operating on an 11g channel */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) error = ENETRESET; break; + case IEEE80211_IOC_FF: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_FF) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_FF; + } else + ic->ic_flags &= ~IEEE80211_F_FF; + error = ENETRESET; + break; + case IEEE80211_IOC_TURBOP: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_TURBOP; + } else + ic->ic_flags &= ~IEEE80211_F_TURBOP; + error = ENETRESET; + break; + case IEEE80211_IOC_BGSCAN: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_BGSCAN; + } else + ic->ic_flags &= ~IEEE80211_F_BGSCAN; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN) + ic->ic_bgscanidle = ireq->i_val*hz/1000; + else + error = EINVAL; + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN) + ic->ic_bgscanintvl = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_SCANVALID: + if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN) + ic->ic_scanvalid = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_ROAM_RSSI_11A: + ic->ic_roam.rssi11a = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RSSI_11B: + ic->ic_roam.rssi11bOnly = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RSSI_11G: + ic->ic_roam.rssi11b = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RATE_11A: + ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL; + break; + case IEEE80211_IOC_ROAM_RATE_11B: + ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL; + break; + case IEEE80211_IOC_ROAM_RATE_11G: + ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL; + break; case IEEE80211_IOC_MCAST_RATE: ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; break; @@ -2659,12 +2370,112 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_bmissthreshold = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_setcurchan(ic, ireq); + break; + case IEEE80211_IOC_SHORTGI: + if (ireq->i_val) { +#define IEEE80211_HTCAP_SHORTGI \ + (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) + if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) + return EINVAL; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; +#undef IEEE80211_HTCAP_SHORTGI + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40); + /* XXX kick state machine? */ + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_AMPDU: + if (ireq->i_val) { + if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; + if (ireq->i_val & 2) + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX); + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + /* XXX validate */ + ic->ic_ampdu_limit = ireq->i_val; + break; + case IEEE80211_IOC_AMPDU_DENSITY: + /* XXX validate */ + ic->ic_ampdu_density = ireq->i_val; + break; + case IEEE80211_IOC_AMSDU: + if (ireq->i_val) { + if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; + if (ireq->i_val & 2) + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX); + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + /* XXX validate */ + ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + if (ireq->i_val) { + if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) + return EINVAL; + ic->ic_flags_ext |= IEEE80211_FEXT_PUREN; + } else + ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN; + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_DOTH: + if (ireq->i_val) { +#if 0 + /* XXX no capability */ + if ((ic->ic_caps & IEEE80211_C_DOTH) == 0) + return EINVAL; +#endif + ic->ic_flags |= IEEE80211_F_DOTH; + } else + ic->ic_flags &= ~IEEE80211_F_DOTH; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_HTCOMPAT: + if (ireq->i_val) { + if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) + return EINVAL; + ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT; + } else + ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT; + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; default: error = EINVAL; break; } - if (error == ENETRESET && !IS_UP_AUTO(ic)) - error = 0; + if (error == ENETRESET) + error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0; return error; } @@ -2692,15 +2503,6 @@ ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) error = ieee80211_ioctl_set80211(ic, cmd, (struct ieee80211req *) data); break; - case SIOCGIFGENERIC: - error = ieee80211_cfgget(ic, cmd, data); - break; - case SIOCSIFGENERIC: - error = priv_check(curthread, PRIV_NET80211_MANAGE); - if (error) - break; - error = ieee80211_cfgset(ic, cmd, data); - break; case SIOCG80211STATS: ifr = (struct ifreq *)data; copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 28dba0356d74..2e1700021551 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -39,146 +39,158 @@ * Per/node (station) statistics. */ struct ieee80211_nodestats { - u_int32_t ns_rx_data; /* rx data frames */ - u_int32_t ns_rx_mgmt; /* rx management frames */ - u_int32_t ns_rx_ctrl; /* rx control frames */ - u_int32_t ns_rx_ucast; /* rx unicast frames */ - u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ - u_int64_t ns_rx_bytes; /* rx data count (bytes) */ - u_int64_t ns_rx_beacons; /* rx beacon frames */ - u_int32_t ns_rx_proberesp; /* rx probe response frames */ + uint32_t ns_rx_data; /* rx data frames */ + uint32_t ns_rx_mgmt; /* rx management frames */ + uint32_t ns_rx_ctrl; /* rx control frames */ + uint32_t ns_rx_ucast; /* rx unicast frames */ + uint32_t ns_rx_mcast; /* rx multi/broadcast frames */ + uint64_t ns_rx_bytes; /* rx data count (bytes) */ + uint64_t ns_rx_beacons; /* rx beacon frames */ + uint32_t ns_rx_proberesp; /* rx probe response frames */ - u_int32_t ns_rx_dup; /* rx discard 'cuz dup */ - u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ - u_int32_t ns_rx_wepfail; /* rx wep processing failed */ - u_int32_t ns_rx_demicfail; /* rx demic failed */ - u_int32_t ns_rx_decap; /* rx decapsulation failed */ - u_int32_t ns_rx_defrag; /* rx defragmentation failed */ - u_int32_t ns_rx_disassoc; /* rx disassociation */ - u_int32_t ns_rx_deauth; /* rx deauthentication */ - u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ - u_int32_t ns_rx_unauth; /* rx on unauthorized port */ - u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ + uint32_t ns_rx_dup; /* rx discard 'cuz dup */ + uint32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ + uint32_t ns_rx_wepfail; /* rx wep processing failed */ + uint32_t ns_rx_demicfail; /* rx demic failed */ + uint32_t ns_rx_decap; /* rx decapsulation failed */ + uint32_t ns_rx_defrag; /* rx defragmentation failed */ + uint32_t ns_rx_disassoc; /* rx disassociation */ + uint32_t ns_rx_deauth; /* rx deauthentication */ + uint32_t ns_rx_action; /* rx action */ + uint32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ + uint32_t ns_rx_unauth; /* rx on unauthorized port */ + uint32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ - u_int32_t ns_tx_data; /* tx data frames */ - u_int32_t ns_tx_mgmt; /* tx management frames */ - u_int32_t ns_tx_ucast; /* tx unicast frames */ - u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ - u_int64_t ns_tx_bytes; /* tx data count (bytes) */ - u_int32_t ns_tx_probereq; /* tx probe request frames */ + uint32_t ns_tx_data; /* tx data frames */ + uint32_t ns_tx_mgmt; /* tx management frames */ + uint32_t ns_tx_ucast; /* tx unicast frames */ + uint32_t ns_tx_mcast; /* tx multi/broadcast frames */ + uint64_t ns_tx_bytes; /* tx data count (bytes) */ + uint32_t ns_tx_probereq; /* tx probe request frames */ - u_int32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ - u_int32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ + uint32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ + uint32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ - u_int32_t ns_ps_discard; /* ps discard 'cuz of age */ + uint32_t ns_ps_discard; /* ps discard 'cuz of age */ /* MIB-related state */ - u_int32_t ns_tx_assoc; /* [re]associations */ - u_int32_t ns_tx_assoc_fail; /* [re]association failures */ - u_int32_t ns_tx_auth; /* [re]authentications */ - u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/ - u_int32_t ns_tx_deauth; /* deauthentications */ - u_int32_t ns_tx_deauth_code; /* last deauth reason */ - u_int32_t ns_tx_disassoc; /* disassociations */ - u_int32_t ns_tx_disassoc_code; /* last disassociation reason */ + uint32_t ns_tx_assoc; /* [re]associations */ + uint32_t ns_tx_assoc_fail; /* [re]association failures */ + uint32_t ns_tx_auth; /* [re]authentications */ + uint32_t ns_tx_auth_fail; /* [re]authentication failures*/ + uint32_t ns_tx_deauth; /* deauthentications */ + uint32_t ns_tx_deauth_code; /* last deauth reason */ + uint32_t ns_tx_disassoc; /* disassociations */ + uint32_t ns_tx_disassoc_code; /* last disassociation reason */ }; /* * Summary statistics. */ struct ieee80211_stats { - u_int32_t is_rx_badversion; /* rx frame with bad version */ - u_int32_t is_rx_tooshort; /* rx frame too short */ - u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ - u_int32_t is_rx_dup; /* rx discard 'cuz dup */ - u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ - u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ - u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ - u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ - u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ - u_int32_t is_rx_wepfail; /* rx wep processing failed */ - u_int32_t is_rx_decap; /* rx decapsulation failed */ - u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ - u_int32_t is_rx_ctl; /* rx discard ctrl frames */ - u_int32_t is_rx_beacon; /* rx beacon frames */ - u_int32_t is_rx_rstoobig; /* rx rate set truncated */ - u_int32_t is_rx_elem_missing; /* rx required element missing*/ - u_int32_t is_rx_elem_toobig; /* rx element too big */ - u_int32_t is_rx_elem_toosmall; /* rx element too small */ - u_int32_t is_rx_elem_unknown; /* rx element unknown */ - u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ - u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ - u_int32_t is_rx_nodealloc; /* rx frame dropped */ - u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ - u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ - u_int32_t is_rx_auth_fail; /* rx sta auth failure */ - u_int32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ - u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ - u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ - u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ - u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ - u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ - u_int32_t is_rx_deauth; /* rx deauthentication */ - u_int32_t is_rx_disassoc; /* rx disassociation */ - u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ - u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ - u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ - u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ - u_int32_t is_rx_bad_auth; /* rx bad auth request */ - u_int32_t is_rx_unauth; /* rx on unauthorized port */ - u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ - u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ - u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ - u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ - u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ - u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ - u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ - u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ - u_int32_t is_rx_badcipher; /* rx failed 'cuz key type */ - u_int32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ - u_int32_t is_rx_acl; /* rx discard 'cuz acl policy */ - u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ - u_int32_t is_tx_nonode; /* tx failed for no node */ - u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ - u_int32_t is_tx_badcipher; /* tx failed 'cuz key type */ - u_int32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ - u_int32_t is_tx_noheadroom; /* tx failed 'cuz no space */ - u_int32_t is_tx_fragframes; /* tx frames fragmented */ - u_int32_t is_tx_frags; /* tx fragments created */ - u_int32_t is_scan_active; /* active scans started */ - u_int32_t is_scan_passive; /* passive scans started */ - u_int32_t is_node_timeout; /* nodes timed out inactivity */ - u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ - u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ - u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ - u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ - u_int32_t is_crypto_tkipcm; /* tkip counter measures */ - u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ - u_int32_t is_crypto_wep; /* wep crypto done in s/w */ - u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */ - u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */ - u_int32_t is_crypto_delkey; /* driver key delete failed */ - u_int32_t is_crypto_badcipher; /* unknown cipher */ - u_int32_t is_crypto_nocipher; /* cipher not available */ - u_int32_t is_crypto_attachfail; /* cipher attach failed */ - u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ - u_int32_t is_crypto_keyfail; /* driver key alloc failed */ - u_int32_t is_crypto_enmicfail; /* en-MIC failed */ - u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ - u_int32_t is_ibss_norate; /* merge failed-rate mismatch */ - u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ - u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ - u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */ - u_int32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */ - u_int32_t is_ff_tooshort; /* fast frame rx decap error */ - u_int32_t is_ff_split; /* fast frame rx split error */ - u_int32_t is_ff_decap; /* fast frames decap'd */ - u_int32_t is_ff_encap; /* fast frames encap'd for tx */ - u_int32_t is_rx_badbintval; /* rx frame w/ bogus bintval */ - u_int32_t is_rx_demicfail; /* rx demic failed */ - u_int32_t is_rx_defrag; /* rx defragmentation failed */ - u_int32_t is_rx_mgmt; /* rx management frames */ - u_int32_t is_spare[6]; + uint32_t is_rx_badversion; /* rx frame with bad version */ + uint32_t is_rx_tooshort; /* rx frame too short */ + uint32_t is_rx_wrongbss; /* rx from wrong bssid */ + uint32_t is_rx_dup; /* rx discard 'cuz dup */ + uint32_t is_rx_wrongdir; /* rx w/ wrong direction */ + uint32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ + uint32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ + uint32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ + uint32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ + uint32_t is_rx_wepfail; /* rx wep processing failed */ + uint32_t is_rx_decap; /* rx decapsulation failed */ + uint32_t is_rx_mgtdiscard; /* rx discard mgt frames */ + uint32_t is_rx_ctl; /* rx discard ctrl frames */ + uint32_t is_rx_beacon; /* rx beacon frames */ + uint32_t is_rx_rstoobig; /* rx rate set truncated */ + uint32_t is_rx_elem_missing; /* rx required element missing*/ + uint32_t is_rx_elem_toobig; /* rx element too big */ + uint32_t is_rx_elem_toosmall; /* rx element too small */ + uint32_t is_rx_elem_unknown; /* rx element unknown */ + uint32_t is_rx_badchan; /* rx frame w/ invalid chan */ + uint32_t is_rx_chanmismatch; /* rx frame chan mismatch */ + uint32_t is_rx_nodealloc; /* rx frame dropped */ + uint32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ + uint32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ + uint32_t is_rx_auth_fail; /* rx sta auth failure */ + uint32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ + uint32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ + uint32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ + uint32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ + uint32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + uint32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ + uint32_t is_rx_deauth; /* rx deauthentication */ + uint32_t is_rx_disassoc; /* rx disassociation */ + uint32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ + uint32_t is_rx_nobuf; /* rx failed for lack of buf */ + uint32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ + uint32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ + uint32_t is_rx_bad_auth; /* rx bad auth request */ + uint32_t is_rx_unauth; /* rx on unauthorized port */ + uint32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ + uint32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ + uint32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ + uint32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ + uint32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ + uint32_t is_rx_tkipformat; /* rx format bad (TKIP) */ + uint32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ + uint32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ + uint32_t is_rx_badcipher; /* rx failed 'cuz key type */ + uint32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ + uint32_t is_rx_acl; /* rx discard 'cuz acl policy */ + uint32_t is_tx_nobuf; /* tx failed for lack of buf */ + uint32_t is_tx_nonode; /* tx failed for no node */ + uint32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + uint32_t is_tx_badcipher; /* tx failed 'cuz key type */ + uint32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ + uint32_t is_tx_noheadroom; /* tx failed 'cuz no space */ + uint32_t is_tx_fragframes; /* tx frames fragmented */ + uint32_t is_tx_frags; /* tx fragments created */ + uint32_t is_scan_active; /* active scans started */ + uint32_t is_scan_passive; /* passive scans started */ + uint32_t is_node_timeout; /* nodes timed out inactivity */ + uint32_t is_crypto_nomem; /* no memory for crypto ctx */ + uint32_t is_crypto_tkip; /* tkip crypto done in s/w */ + uint32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ + uint32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ + uint32_t is_crypto_tkipcm; /* tkip counter measures */ + uint32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ + uint32_t is_crypto_wep; /* wep crypto done in s/w */ + uint32_t is_crypto_setkey_cipher;/* cipher rejected key */ + uint32_t is_crypto_setkey_nokey; /* no key index for setkey */ + uint32_t is_crypto_delkey; /* driver key delete failed */ + uint32_t is_crypto_badcipher; /* unknown cipher */ + uint32_t is_crypto_nocipher; /* cipher not available */ + uint32_t is_crypto_attachfail; /* cipher attach failed */ + uint32_t is_crypto_swfallback; /* cipher fallback to s/w */ + uint32_t is_crypto_keyfail; /* driver key alloc failed */ + uint32_t is_crypto_enmicfail; /* en-MIC failed */ + uint32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ + uint32_t is_ibss_norate; /* merge failed-rate mismatch */ + uint32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ + uint32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ + uint32_t is_ps_qempty; /* ps-poll w/ nothing to send */ + uint32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */ + uint32_t is_ff_tooshort; /* fast frame rx decap error */ + uint32_t is_ff_split; /* fast frame rx split error */ + uint32_t is_ff_decap; /* fast frames decap'd */ + uint32_t is_ff_encap; /* fast frames encap'd for tx */ + uint32_t is_rx_badbintval; /* rx frame w/ bogus bintval */ + uint32_t is_rx_demicfail; /* rx demic failed */ + uint32_t is_rx_defrag; /* rx defragmentation failed */ + uint32_t is_rx_mgmt; /* rx management frames */ + uint32_t is_rx_action; /* rx action mgt frames */ + uint32_t is_amsdu_tooshort; /* A-MSDU rx decap error */ + uint32_t is_amsdu_split; /* A-MSDU rx split error */ + uint32_t is_amsdu_decap; /* A-MSDU decap'd */ + uint32_t is_amsdu_encap; /* A-MSDU encap'd for tx */ + uint32_t is_ampdu_bar_bad; /* A-MPDU BAR out of window */ + uint32_t is_ampdu_bar_oow; /* A-MPDU BAR before ADDBA */ + uint32_t is_ampdu_bar_rx; /* A-MPDU BAR frames handled */ + uint32_t is_ampdu_rx_flush; /* A-MPDU frames flushed */ + uint32_t is_ampdu_rx_oor; /* A-MPDU frames out-of-order */ + uint32_t is_ampdu_rx_copy; /* A-MPDU frames copied down */ + uint32_t is_spare[32]; }; /* @@ -199,17 +211,17 @@ struct ieee80211_stats { more than IEEE80211_KEYBUF_SIZE. */ struct ieee80211req_key { - u_int8_t ik_type; /* key/cipher type */ - u_int8_t ik_pad; - u_int16_t ik_keyix; /* key index */ - u_int8_t ik_keylen; /* key length in bytes */ - u_int8_t ik_flags; + uint8_t ik_type; /* key/cipher type */ + uint8_t ik_pad; + uint16_t ik_keyix; /* key index */ + uint8_t ik_keylen; /* key length in bytes */ + uint8_t ik_flags; /* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */ #define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */ - u_int8_t ik_macaddr[IEEE80211_ADDR_LEN]; - u_int64_t ik_keyrsc; /* key receive sequence counter */ - u_int64_t ik_keytsc; /* key transmit sequence counter */ - u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; + uint8_t ik_macaddr[IEEE80211_ADDR_LEN]; + uint64_t ik_keyrsc; /* key receive sequence counter */ + uint64_t ik_keytsc; /* key transmit sequence counter */ + uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; }; /* @@ -217,8 +229,8 @@ struct ieee80211req_key { * to IEEE80211_KEYIX_NONE when deleting a unicast key. */ struct ieee80211req_del_key { - u_int8_t idk_keyix; /* key index */ - u_int8_t idk_macaddr[IEEE80211_ADDR_LEN]; + uint8_t idk_keyix; /* key index */ + uint8_t idk_macaddr[IEEE80211_ADDR_LEN]; }; /* @@ -228,16 +240,16 @@ struct ieee80211req_del_key { * ap (to effect a station). */ struct ieee80211req_mlme { - u_int8_t im_op; /* operation to perform */ + uint8_t im_op; /* operation to perform */ #define IEEE80211_MLME_ASSOC 1 /* associate station */ #define IEEE80211_MLME_DISASSOC 2 /* disassociate station */ #define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ #define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ #define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ - u_int8_t im_ssid_len; /* length of optional ssid */ - u_int16_t im_reason; /* 802.11 reason code */ - u_int8_t im_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t im_ssid[IEEE80211_NWID_LEN]; + uint8_t im_ssid_len; /* length of optional ssid */ + uint16_t im_reason; /* 802.11 reason code */ + uint8_t im_macaddr[IEEE80211_ADDR_LEN]; + uint8_t im_ssid[IEEE80211_NWID_LEN]; }; /* @@ -254,7 +266,7 @@ enum { }; struct ieee80211req_maclist { - u_int8_t ml_macaddr[IEEE80211_ADDR_LEN]; + uint8_t ml_macaddr[IEEE80211_ADDR_LEN]; }; /* @@ -264,7 +276,7 @@ struct ieee80211req_maclist { * scanning. */ struct ieee80211req_chanlist { - u_int8_t ic_channels[IEEE80211_CHAN_BYTES]; + uint8_t ic_channels[IEEE80211_CHAN_BYTES]; }; /* @@ -278,9 +290,14 @@ struct ieee80211req_chaninfo { /* * Retrieve the WPA/RSN information element for an associated station. */ -struct ieee80211req_wpaie { - u_int8_t wpa_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t wpa_ie[IEEE80211_MAX_OPT_IE]; +struct ieee80211req_wpaie { /* old version w/ only one ie */ + uint8_t wpa_macaddr[IEEE80211_ADDR_LEN]; + uint8_t wpa_ie[IEEE80211_MAX_OPT_IE]; +}; +struct ieee80211req_wpaie2 { + uint8_t wpa_macaddr[IEEE80211_ADDR_LEN]; + uint8_t wpa_ie[IEEE80211_MAX_OPT_IE]; + uint8_t rsn_ie[IEEE80211_MAX_OPT_IE]; }; /* @@ -289,8 +306,8 @@ struct ieee80211req_wpaie { struct ieee80211req_sta_stats { union { /* NB: explicitly force 64-bit alignment */ - u_int8_t macaddr[IEEE80211_ADDR_LEN]; - u_int64_t pad; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; } is_u; struct ieee80211_nodestats is_stats; }; @@ -300,27 +317,28 @@ struct ieee80211req_sta_stats { * to retrieve other data like stats, unicast key, etc. */ struct ieee80211req_sta_info { - u_int16_t isi_len; /* length (mult of 4) */ - u_int16_t isi_freq; /* MHz */ - u_int16_t isi_flags; /* channel flags */ - u_int16_t isi_state; /* state flags */ - u_int8_t isi_authmode; /* authentication algorithm */ + uint16_t isi_len; /* length (mult of 4) */ + uint16_t isi_ie_off; /* offset to IE data */ + uint16_t isi_ie_len; /* IE length */ + uint16_t isi_freq; /* MHz */ + uint16_t isi_flags; /* channel flags */ + uint16_t isi_state; /* state flags */ + uint8_t isi_authmode; /* authentication algorithm */ int8_t isi_rssi; /* receive signal strength */ - u_int8_t isi_capinfo; /* capabilities */ - u_int8_t isi_erp; /* ERP element */ - u_int8_t isi_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t isi_nrates; - /* negotiated rates */ - u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE]; - u_int8_t isi_txrate; /* index to isi_rates[] */ int8_t isi_noise; /* noise floor */ - u_int16_t isi_ie_len; /* IE length */ - u_int16_t isi_associd; /* assoc response */ - u_int16_t isi_txpower; /* current tx power */ - u_int16_t isi_vlan; /* vlan tag */ - u_int16_t isi_txseqs[17]; /* seq to be transmitted */ - u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/ - u_int16_t isi_inact; /* inactivity timer */ + uint8_t isi_capinfo; /* capabilities */ + uint8_t isi_erp; /* ERP element */ + uint8_t isi_macaddr[IEEE80211_ADDR_LEN]; + uint8_t isi_nrates; + /* negotiated rates */ + uint8_t isi_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isi_txrate; /* index to isi_rates[] */ + uint16_t isi_associd; /* assoc response */ + uint16_t isi_txpower; /* current tx power */ + uint16_t isi_vlan; /* vlan tag */ + uint16_t isi_txseqs[17]; /* seq to be transmitted */ + uint16_t isi_rxseqs[17]; /* seq previous for qos frames*/ + uint16_t isi_inact; /* inactivity timer */ /* XXX frag state? */ /* variable length IE data */ }; @@ -332,8 +350,8 @@ struct ieee80211req_sta_info { struct ieee80211req_sta_req { union { /* NB: explicitly force 64-bit alignment */ - u_int8_t macaddr[IEEE80211_ADDR_LEN]; - u_int64_t pad; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; } is_u; struct ieee80211req_sta_info info[1]; /* variable length */ }; @@ -342,8 +360,8 @@ struct ieee80211req_sta_req { * Get/set per-station tx power cap. */ struct ieee80211req_sta_txpow { - u_int8_t it_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t it_txpow; + uint8_t it_macaddr[IEEE80211_ADDR_LEN]; + uint8_t it_txpow; }; /* @@ -363,13 +381,14 @@ struct ieee80211req_sta_txpow { /* the first member must be matched with struct ifreq */ struct ieee80211req { char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ - u_int16_t i_type; /* req type */ + uint16_t i_type; /* req type */ int16_t i_val; /* Index or simple value */ int16_t i_len; /* Index or simple value */ void *i_data; /* Extra data */ }; #define SIOCS80211 _IOW('i', 234, struct ieee80211req) #define SIOCG80211 _IOWR('i', 235, struct ieee80211req) +#define SIOCG80211STATS _IOWR('i', 236, struct ifreq) #define IEEE80211_IOC_SSID 1 #define IEEE80211_IOC_NUMSSIDS 2 @@ -440,12 +459,38 @@ struct ieee80211req { #define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ #define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ #define IEEE80211_IOC_PUREG 56 /* pure 11g (no 11b stations) */ +#define IEEE80211_IOC_FF 57 /* ATH fast frames (on, off) */ +#define IEEE80211_IOC_TURBOP 58 /* ATH turbo' (on, off) */ +#define IEEE80211_IOC_BGSCAN 59 /* bg scanning (on, off) */ +#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */ +#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */ +#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */ +#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */ +#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */ +#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */ +#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */ +#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */ +#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */ #define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */ #define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */ #define IEEE80211_IOC_BURST 75 /* packet bursting */ #define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */ #define IEEE80211_IOC_BMISSTHRESHOLD 77 /* beacon miss threshold */ #define IEEE80211_IOC_STA_INFO 78 /* station/neighbor info */ +#define IEEE80211_IOC_WPAIE2 79 /* WPA+RSN info elements */ +#define IEEE80211_IOC_CURCHAN 80 /* current channel */ +#define IEEE80211_IOC_SHORTGI 81 /* 802.11n half GI */ +#define IEEE80211_IOC_AMPDU 82 /* 802.11n A-MPDU (on, off) */ +#define IEEE80211_IOC_AMPDU_LIMIT 83 /* A-MPDU length limit */ +#define IEEE80211_IOC_AMPDU_DENSITY 84 /* A-MPDU density */ +#define IEEE80211_IOC_AMSDU 85 /* 802.11n A-MSDU (on, off) */ +#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */ +#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */ +#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */ +#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */ +#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */ +#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */ +#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */ /* * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS. @@ -456,24 +501,27 @@ struct ieee80211req { * in isr_len. Result records are rounded to a multiple of 4 bytes. */ struct ieee80211req_scan_result { - u_int16_t isr_len; /* length (mult of 4) */ - u_int16_t isr_ie_len; /* IE length */ - u_int16_t isr_freq; /* MHz */ - u_int16_t isr_flags; /* channel flags */ + uint16_t isr_len; /* length (mult of 4) */ + uint16_t isr_ie_off; /* offset to IE data */ + uint16_t isr_ie_len; /* IE length */ + uint16_t isr_freq; /* MHz */ + uint16_t isr_flags; /* channel flags */ int8_t isr_noise; int8_t isr_rssi; - u_int8_t isr_intval; /* beacon interval */ - u_int8_t isr_capinfo; /* capabilities */ - u_int8_t isr_erp; /* ERP element */ - u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; - u_int8_t isr_nrates; - u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; - u_int8_t isr_ssid_len; /* SSID length */ - u_int8_t isr_pad[8]; + uint8_t isr_intval; /* beacon interval */ + uint8_t isr_capinfo; /* capabilities */ + uint8_t isr_erp; /* ERP element */ + uint8_t isr_bssid[IEEE80211_ADDR_LEN]; + uint8_t isr_nrates; + uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isr_ssid_len; /* SSID length */ /* variable length SSID followed by IE data */ }; -#define SIOCG80211STATS _IOWR('i', 236, struct ifreq) +struct ieee80211_clone_params { + char icp_parent[IFNAMSIZ]; /* parent device */ + int icp_opmode; /* operating mode */ +}; #endif /* __FreeBSD__ */ #endif /* _NET80211_IEEE80211_IOCTL_H_ */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 9394c0f54252..690e54b4f80a 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -59,25 +59,22 @@ __FBSDID("$FreeBSD$"); #define REFCNT_LOC "%s %p<%s> refcnt %d\n", __func__ #endif +static int ieee80211_sta_join1(struct ieee80211_node *); + static struct ieee80211_node *node_alloc(struct ieee80211_node_table *); static void node_cleanup(struct ieee80211_node *); static void node_free(struct ieee80211_node *); -static u_int8_t node_getrssi(const struct ieee80211_node *); +static int8_t node_getrssi(const struct ieee80211_node *); +static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); static void ieee80211_setup_node(struct ieee80211_node_table *, - struct ieee80211_node *, const u_int8_t *); + struct ieee80211_node *, const uint8_t *); static void _ieee80211_free_node(struct ieee80211_node *); -static void ieee80211_free_allnodes(struct ieee80211_node_table *); - -static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *); -static void ieee80211_timeout_stations(struct ieee80211_node_table *); - -static void ieee80211_set_tim(struct ieee80211_node *, int set); static void ieee80211_node_table_init(struct ieee80211com *ic, struct ieee80211_node_table *nt, const char *name, - int inact, int keyixmax, - void (*timeout)(struct ieee80211_node_table *)); + int inact, int keymaxix); +static void ieee80211_node_table_reset(struct ieee80211_node_table *); static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); @@ -90,6 +87,7 @@ ieee80211_node_attach(struct ieee80211com *ic) ic->ic_node_free = node_free; ic->ic_node_cleanup = node_cleanup; ic->ic_node_getrssi = node_getrssi; + ic->ic_node_getsignal = node_getsignal; /* default station inactivity timer setings */ ic->ic_inact_init = IEEE80211_INACT_INIT; @@ -97,9 +95,10 @@ ieee80211_node_attach(struct ieee80211com *ic) ic->ic_inact_run = IEEE80211_INACT_RUN; ic->ic_inact_probe = IEEE80211_INACT_PROBE; + callout_init(&ic->ic_inact, CALLOUT_MPSAFE); + /* NB: driver should override */ ic->ic_max_aid = IEEE80211_AID_DEF; - ic->ic_set_tim = ieee80211_set_tim; } void @@ -109,30 +108,17 @@ ieee80211_node_lateattach(struct ieee80211com *ic) if (ic->ic_max_aid > IEEE80211_AID_MAX) ic->ic_max_aid = IEEE80211_AID_MAX; - MALLOC(ic->ic_aid_bitmap, u_int32_t *, - howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t), - M_DEVBUF, M_NOWAIT | M_ZERO); + MALLOC(ic->ic_aid_bitmap, uint32_t *, + howmany(ic->ic_max_aid, 32) * sizeof(uint32_t), + M_80211_NODE, M_NOWAIT | M_ZERO); if (ic->ic_aid_bitmap == NULL) { /* XXX no way to recover */ printf("%s: no memory for AID bitmap!\n", __func__); ic->ic_max_aid = 0; } - /* XXX defer until using hostap/ibss mode */ - ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t); - MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len, - M_DEVBUF, M_NOWAIT | M_ZERO); - if (ic->ic_tim_bitmap == NULL) { - /* XXX no way to recover */ - printf("%s: no memory for TIM bitmap!\n", __func__); - } - ieee80211_node_table_init(ic, &ic->ic_sta, "station", - IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix, - ieee80211_timeout_stations); - ieee80211_node_table_init(ic, &ic->ic_scan, "scan", - IEEE80211_INACT_SCAN, 0, - ieee80211_timeout_scan_candidates); + IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix); ieee80211_reset_bss(ic); /* @@ -182,16 +168,11 @@ ieee80211_node_detach(struct ieee80211com *ic) ieee80211_free_node(ic->ic_bss); ic->ic_bss = NULL; } - ieee80211_node_table_cleanup(&ic->ic_scan); ieee80211_node_table_cleanup(&ic->ic_sta); if (ic->ic_aid_bitmap != NULL) { - FREE(ic->ic_aid_bitmap, M_DEVBUF); + FREE(ic->ic_aid_bitmap, M_80211_NODE); ic->ic_aid_bitmap = NULL; } - if (ic->ic_tim_bitmap != NULL) { - FREE(ic->ic_tim_bitmap, M_DEVBUF); - ic->ic_tim_bitmap = NULL; - } } /* @@ -218,133 +199,28 @@ ieee80211_node_unauthorize(struct ieee80211_node *ni) * to insure a consistent view by drivers. */ static void -ieee80211_set_chan(struct ieee80211com *ic, - struct ieee80211_node *ni, struct ieee80211_channel *chan) +ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni) { + struct ieee80211_channel *chan = ic->ic_bsschan; + +#if 0 + KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup")); +#else if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */ chan = ic->ic_curchan; +#endif ni->ni_chan = chan; - ni->ni_rates = *ieee80211_get_suprates(ic, chan); -} - -/* - * AP scanning support. - */ - -#ifdef IEEE80211_DEBUG -static void -dump_chanlist(const u_char chans[]) -{ - const char *sep; - int i; - - sep = " "; - for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(chans, i)) { - printf("%s%u", sep, i); - sep = ", "; - } -} -#endif /* IEEE80211_DEBUG */ - -/* - * Initialize the channel set to scan based on the - * of available channels and the current PHY mode. - */ -static void -ieee80211_reset_scan(struct ieee80211com *ic) -{ - - /* XXX ic_des_chan should be handled with ic_chan_active */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { - memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan)); - setbit(ic->ic_chan_scan, - ieee80211_chan2ieee(ic, ic->ic_des_chan)); - } else - memcpy(ic->ic_chan_scan, ic->ic_chan_active, - sizeof(ic->ic_chan_active)); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic)) { - printf("%s: scan set:", __func__); - dump_chanlist(ic->ic_chan_scan); - printf(" start chan %u\n", - ieee80211_chan2ieee(ic, ic->ic_curchan)); + if (IEEE80211_IS_CHAN_HT(chan)) { + /* + * XXX Gotta be careful here; the rate set returned by + * ieee80211_get_suprates is actually any HT rate + * set so blindly copying it will be bad. We must + * install the legacy rate est in ni_rates and the + * HT rate set in ni_htrates. + */ + ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan); } -#endif /* IEEE80211_DEBUG */ -} - -/* - * Begin an active scan. - */ -void -ieee80211_begin_scan(struct ieee80211com *ic, int reset) -{ - - ic->ic_scan.nt_scangen++; - /* - * In all but hostap mode scanning starts off in - * an active mode before switching to passive. - */ - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - ic->ic_flags |= IEEE80211_F_ASCAN; - ic->ic_stats.is_scan_active++; - } else - ic->ic_stats.is_scan_passive++; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "begin %s scan in %s mode, scangen %u\n", - (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive", - ieee80211_phymode_name[ic->ic_curmode], ic->ic_scan.nt_scangen); - /* - * Clear scan state and flush any previously seen AP's. - */ - ieee80211_reset_scan(ic); - if (reset) - ieee80211_free_allnodes(&ic->ic_scan); - - ic->ic_flags |= IEEE80211_F_SCAN; - - /* Scan the next channel. */ - ieee80211_next_scan(ic); -} - -/* - * Switch to the next channel marked for scanning. - */ -int -ieee80211_next_scan(struct ieee80211com *ic) -{ - struct ieee80211_channel *chan; - - /* - * Insure any previous mgt frame timeouts don't fire. - * This assumes the driver does the right thing in - * flushing anything queued in the driver and below. - */ - ic->ic_mgt_timer = 0; - ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; - - chan = ic->ic_curchan; - do { - if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) - chan = &ic->ic_channels[0]; - if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { - clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: chan %d->%d\n", __func__, - ieee80211_chan2ieee(ic, ic->ic_curchan), - ieee80211_chan2ieee(ic, chan)); - ic->ic_curchan = chan; - /* - * XXX drivers should do this as needed, - * XXX for now maintain compatibility - */ - ic->ic_bss->ni_rates = *ieee80211_get_suprates(ic, chan); - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - return 1; - } - } while (chan != ic->ic_curchan); - ieee80211_end_scan(ic); - return 0; + ni->ni_rates = *ieee80211_get_suprates(ic, chan); } /* @@ -366,7 +242,7 @@ ieee80211_probe_curchan(struct ieee80211com *ic, int force) ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, ifp->if_broadcastaddr, ifp->if_broadcastaddr, - ic->ic_des_essid, ic->ic_des_esslen, + ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len, ic->ic_opt_ie, ic->ic_opt_ie_len); } else ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; @@ -415,9 +291,10 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) return; } IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - ni->ni_esslen = ic->ic_des_esslen; - memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); - copy_bss(ni, ic->ic_bss); + ni->ni_esslen = ic->ic_des_ssid[0].len; + memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen); + if (ic->ic_bss != NULL) + copy_bss(ni, ic->ic_bss); ni->ni_intval = ic->ic_bintval; if (ic->ic_flags & IEEE80211_F_PRIVACY) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; @@ -444,29 +321,47 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) /* * Fix the channel and related attributes. */ - ieee80211_set_chan(ic, ni, chan); - ic->ic_curchan = chan; - ic->ic_curmode = ieee80211_chan2mode(ic, chan); + ic->ic_bsschan = chan; + ieee80211_node_set_chan(ic, ni); + ic->ic_curmode = ieee80211_chan2mode(chan); /* * Do mode-specific rate setup. */ - if (IEEE80211_IS_CHAN_FULL(chan) && - (ic->ic_curmode == IEEE80211_MODE_11G || - ic->ic_curmode == IEEE80211_MODE_11B)) - ieee80211_set11gbasicrates(&ni->ni_rates, ic->ic_curmode); + if (IEEE80211_IS_CHAN_FULL(chan)) { + if (IEEE80211_IS_CHAN_ANYG(chan)) { + /* + * Use a mixed 11b/11g rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, + IEEE80211_MODE_11G); + } else if (IEEE80211_IS_CHAN_B(chan)) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, + IEEE80211_MODE_11B); + } + } - (void) ieee80211_sta_join(ic, ieee80211_ref_node(ni)); + (void) ieee80211_sta_join1(ieee80211_ref_node(ni)); } +/* + * Reset bss state on transition to the INIT state. + * Clear any stations from the table (they have been + * deauth'd) and reset the bss node (clears key, rate + * etc. state). + */ void ieee80211_reset_bss(struct ieee80211com *ic) { struct ieee80211_node *ni, *obss; - ieee80211_node_table_reset(&ic->ic_scan); + callout_drain(&ic->ic_inact); ieee80211_node_table_reset(&ic->ic_sta); + ieee80211_reset_erp(ic); - ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr); + ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr); KASSERT(ni != NULL, ("unable to setup inital BSS node")); obss = ic->ic_bss; ic->ic_bss = ieee80211_ref_node(ni); @@ -477,21 +372,71 @@ ieee80211_reset_bss(struct ieee80211com *ic) } } -/* XXX tunable */ -#define STA_FAILS_MAX 2 /* assoc failures before ignored */ +static int +match_ssid(const struct ieee80211_node *ni, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + int i; + for (i = 0; i < nssid; i++) { + if (ni->ni_esslen == ssids[i].len && + memcmp(ni->ni_essid, ssids[i].ssid, ni->ni_esslen) == 0) + return 1; + } + return 0; +} + +/* + * Test a node for suitability/compatibility. + */ static int -ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +check_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + uint8_t rate; + + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) + return 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + return 0; + } else { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) + return 0; + } + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + return 0; + } else { + /* XXX does this mean privacy is supported or required? */ + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + return 0; + } + rate = ieee80211_fix_rate(ni, &ni->ni_rates, + IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); + if (rate & IEEE80211_RATE_BASIC) + return 0; + if (ic->ic_des_nssid != 0 && + !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid)) + return 0; + if ((ic->ic_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) + return 0; + return 1; +} + +#ifdef IEEE80211_DEBUG +/* + * Display node suitability/compatibility. + */ +static void +check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni) { - u_int8_t rate; + uint8_t rate; int fail; fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) fail |= 0x01; - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - ni->ni_chan != ic->ic_des_chan) - fail |= 0x01; if (ic->ic_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= 0x02; @@ -511,238 +456,32 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; - if (ic->ic_des_esslen != 0 && - (ni->ni_esslen != ic->ic_des_esslen || - memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) + if (ic->ic_des_nssid != 0 && + !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid)) fail |= 0x10; if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; - if (ni->ni_fails >= STA_FAILS_MAX) - fail |= 0x40; -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic)) { - printf(" %c %s", - fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+', - ether_sprintf(ni->ni_macaddr)); - printf(" %s%c", ether_sprintf(ni->ni_bssid), - fail & 0x20 ? '!' : ' '); - printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), - fail & 0x01 ? '!' : ' '); - printf(" %+4d", ni->ni_rssi); - printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, - fail & 0x08 ? '!' : ' '); - printf(" %4s%c", - (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : - (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : - "????", - fail & 0x02 ? '!' : ' '); - printf(" %3s%c ", - (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? - "wep" : "no", - fail & 0x04 ? '!' : ' '); - ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); - printf("%s\n", fail & 0x10 ? "!" : ""); - } -#endif - return fail; -} - -static __inline u_int8_t -maxrate(const struct ieee80211_node *ni) -{ - const struct ieee80211_rateset *rs = &ni->ni_rates; - /* NB: assumes rate set is sorted (happens on frame receive) */ - return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL; -} - -/* - * Compare the capabilities of two nodes and decide which is - * more desirable (return >0 if a is considered better). Note - * that we assume compatibility/usability has already been checked - * so we don't need to (e.g. validate whether privacy is supported). - * Used to select the best scan candidate for association in a BSS. - */ -static int -ieee80211_node_compare(struct ieee80211com *ic, - const struct ieee80211_node *a, - const struct ieee80211_node *b) -{ - u_int8_t maxa, maxb; - u_int8_t rssia, rssib; - int weight; - - /* privacy support preferred */ - if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) && - (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) - return 1; - if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 && - (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)) - return -1; - - /* compare count of previous failures */ - weight = b->ni_fails - a->ni_fails; - if (abs(weight) > 1) - return weight; - - rssia = ic->ic_node_getrssi(a); - rssib = ic->ic_node_getrssi(b); - if (abs(rssib - rssia) < 5) { - /* best/max rate preferred if signal level close enough XXX */ - maxa = maxrate(a); - maxb = maxrate(b); - if (maxa != maxb) - return maxa - maxb; - /* XXX use freq for channel preference */ - /* for now just prefer 5Ghz band to all other bands */ - if (IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && - !IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) - return 1; - if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && - IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) - return -1; - } - /* all things being equal, use signal level */ - return rssia - rssib; -} - -/* - * Mark an ongoing scan stopped. - */ -void -ieee80211_cancel_scan(struct ieee80211com *ic) -{ - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s: end %s scan\n", - __func__, - (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); - - ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); - ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; -} - -/* - * Complete a scan of potential channels. - */ -void -ieee80211_end_scan(struct ieee80211com *ic) -{ - struct ieee80211_node_table *nt = &ic->ic_scan; - struct ieee80211_node *ni, *selbs; - - ieee80211_cancel_scan(ic); - ieee80211_notify_scan_done(ic); - - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - u_int8_t maxrssi[IEEE80211_CHAN_MAX]; /* XXX off stack? */ - int i, bestchan; - u_int8_t rssi; - - /* - * The passive scan to look for existing AP's completed, - * select a channel to camp on. Identify the channels - * that already have one or more AP's and try to locate - * an unoccupied one. If that fails, pick a channel that - * looks to be quietest. - */ - memset(maxrssi, 0, sizeof(maxrssi)); - IEEE80211_NODE_LOCK(nt); - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { - rssi = ic->ic_node_getrssi(ni); - i = ieee80211_chan2ieee(ic, ni->ni_chan); - if (rssi > maxrssi[i]) - maxrssi[i] = rssi; - } - IEEE80211_NODE_UNLOCK(nt); - /* XXX select channel more intelligently */ - bestchan = -1; - for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i)) { - /* - * If the channel is unoccupied the max rssi - * should be zero; just take it. Otherwise - * track the channel with the lowest rssi and - * use that when all channels appear occupied. - */ - if (maxrssi[i] == 0) { - bestchan = i; - break; - } - if (bestchan == -1 || - maxrssi[i] < maxrssi[bestchan]) - bestchan = i; - } - if (bestchan != -1) { - ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]); - return; - } - /* no suitable channel, should not happen */ - } - /* - * When manually sequencing the state machine; scan just once - * regardless of whether we have a candidate or not. The - * controlling application is expected to setup state and - * initiate an association. - */ - if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL) - return; - /* - * Automatic sequencing; look for a candidate and - * if found join the network. - */ - /* NB: unlocked read should be ok */ - if (TAILQ_FIRST(&nt->nt_node) == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: no scan candidate\n", __func__); - notfound: - if (ic->ic_opmode == IEEE80211_M_IBSS && - (ic->ic_flags & IEEE80211_F_IBSSON) && - ic->ic_des_esslen != 0) { - ieee80211_create_ibss(ic, ic->ic_ibss_chan); - return; - } - /* - * Decrement the failure counts so entries will be - * reconsidered the next time around. We really want - * to do this only for sta's where we've previously - * had some success. - */ - IEEE80211_NODE_LOCK(nt); - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) - if (ni->ni_fails) - ni->ni_fails--; - IEEE80211_NODE_UNLOCK(nt); - /* - * Reset the list of channels to scan and start again. - */ - ieee80211_reset_scan(ic); - ic->ic_flags |= IEEE80211_F_SCAN; - ieee80211_next_scan(ic); - return; - } - selbs = NULL; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n", - "macaddr bssid chan rssi rate flag wep essid"); - IEEE80211_NODE_LOCK(nt); - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { - if (ieee80211_match_bss(ic, ni) == 0) { - if (selbs == NULL) - selbs = ni; - else if (ieee80211_node_compare(ic, ni, selbs) > 0) - selbs = ni; - } - } - if (selbs != NULL) /* NB: grab ref while dropping lock */ - (void) ieee80211_ref_node(selbs); - IEEE80211_NODE_UNLOCK(nt); - if (selbs == NULL) - goto notfound; - if (!ieee80211_sta_join(ic, selbs)) { - ieee80211_free_node(selbs); - goto notfound; - } + printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); + printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' '); + printf(" %3d%c", + ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); + printf(" %+4d", ni->ni_rssi); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & 0x08 ? '!' : ' '); + printf(" %4s%c", + (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : + "????", + fail & 0x02 ? '!' : ' '); + printf(" %3s%c ", + (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", + fail & 0x04 ? '!' : ' '); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf("%s\n", fail & 0x10 ? "!" : ""); } +#endif /* IEEE80211_DEBUG */ /* * Handle 802.11 ad hoc network merge. The @@ -766,9 +505,14 @@ ieee80211_ibss_merge(struct ieee80211_node *ni) /* unchanged, nothing to do */ return 0; } - if (ieee80211_match_bss(ic, ni) != 0) { /* capabilities mismatch */ + if (!check_bss(ic, ni)) { + /* capabilities mismatch */ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "%s: merge failed, capabilities mismatch\n", __func__); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_assoc(ic)) + check_bss_debug(ic, ni); +#endif ic->ic_stats.is_ibss_capmismatch++; return 0; } @@ -779,17 +523,19 @@ ieee80211_ibss_merge(struct ieee80211_node *ni) ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" ); - return ieee80211_sta_join(ic, ieee80211_ref_node(ni)); + return ieee80211_sta_join1(ieee80211_ref_node(ni)); } /* * Join the specified IBSS/BSS network. The node is assumed to * be passed in with a held reference. */ -int -ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) +static int +ieee80211_sta_join1(struct ieee80211_node *selbs) { + struct ieee80211com *ic = selbs->ni_ic; struct ieee80211_node *obss; + int canreassoc; if (ic->ic_opmode == IEEE80211_M_IBSS) { struct ieee80211_node_table *nt; @@ -809,6 +555,13 @@ ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) * Committed to selbs, setup state. */ obss = ic->ic_bss; + /* + * Check if old+new node have the same address in which + * case we can reassociate when operating in sta mode. + */ + canreassoc = (obss != NULL && + ic->ic_state == IEEE80211_S_RUN && + IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr)); ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */ if (obss != NULL) { copy_bss(selbs, obss); @@ -822,23 +575,94 @@ ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates, IEEE80211_F_DODEL | IEEE80211_F_JOIN); + ic->ic_bsschan = selbs->ni_chan; + ic->ic_curchan = ic->ic_bsschan; + ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); + ic->ic_set_channel(ic); /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ - ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan); - ic->ic_curchan = selbs->ni_chan; ieee80211_reset_erp(ic); ieee80211_wme_initparams(ic); - if (ic->ic_opmode == IEEE80211_M_STA) - ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); - else + if (ic->ic_opmode == IEEE80211_M_STA) { + if (canreassoc) { + /* Reassociate */ + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); + } else { + /* + * Act as if we received a DEAUTH frame in case we + * are invoked from the RUN state. This will cause + * us to try to re-authenticate if we are operating + * as a station. + */ + ieee80211_new_state(ic, IEEE80211_S_AUTH, + IEEE80211_FC0_SUBTYPE_DEAUTH); + } + } else ieee80211_new_state(ic, IEEE80211_S_RUN, -1); return 1; } +int +ieee80211_sta_join(struct ieee80211com *ic, + const struct ieee80211_scan_entry *se) +{ + struct ieee80211_node *ni; + + ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr); + if (ni == NULL) { + /* XXX msg */ + return 0; + } + /* + * Expand scan state into node's format. + * XXX may not need all this stuff + */ + IEEE80211_ADDR_COPY(ni->ni_bssid, se->se_bssid); + ni->ni_esslen = se->se_ssid[1]; + memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen); + ni->ni_rstamp = se->se_rstamp; + ni->ni_tstamp.tsf = se->se_tstamp.tsf; + ni->ni_intval = se->se_intval; + ni->ni_capinfo = se->se_capinfo; + /* XXX shift to 11n channel if htinfo present */ + ni->ni_chan = se->se_chan; + ni->ni_timoff = se->se_timoff; + ni->ni_fhdwell = se->se_fhdwell; + ni->ni_fhindex = se->se_fhindex; + ni->ni_erp = se->se_erp; + ni->ni_rssi = se->se_rssi; + ni->ni_noise = se->se_noise; + if (se->se_htcap_ie != NULL) + ieee80211_ht_node_init(ni, se->se_htcap_ie); + if (se->se_htinfo_ie != NULL) + ieee80211_parse_htinfo(ni, se->se_htinfo_ie); + if (se->se_wpa_ie != NULL) + ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie); + if (se->se_rsn_ie != NULL) + ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie); + if (se->se_wme_ie != NULL) + ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie); + if (se->se_ath_ie != NULL) + ieee80211_saveath(ni, se->se_ath_ie); + + ic->ic_dtim_period = se->se_dtimperiod; + ic->ic_dtim_count = 0; + + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ni, se->se_rates, se->se_xrates, + IEEE80211_F_DOSORT); + if (se->se_htcap_ie != NULL) + ieee80211_setup_htrates(ni, se->se_htcap_ie, IEEE80211_F_JOIN); + if (se->se_htinfo_ie != NULL) + ieee80211_setup_basic_htrates(ni, se->se_htinfo_ie); + + return ieee80211_sta_join1(ieee80211_ref_node(ni)); +} + /* * Leave the specified IBSS/BSS network. The node is assumed to * be passed in with a held reference. @@ -871,7 +695,7 @@ node_cleanup(struct ieee80211_node *ni) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ieee80211com *ic = ni->ni_ic; - int i, qlen; + int i; /* NB: preserve ni_table */ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { @@ -892,13 +716,12 @@ node_cleanup(struct ieee80211_node *ni) /* * Drain power save queue and, if needed, clear TIM. */ - IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen); - if (qlen != 0 && ic->ic_set_tim != NULL) + if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL) ic->ic_set_tim(ni, 0); ni->ni_associd = 0; if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } /* @@ -932,22 +755,33 @@ node_free(struct ieee80211_node *ni) ic->ic_node_cleanup(ni); if (ni->ni_wpa_ie != NULL) - FREE(ni->ni_wpa_ie, M_DEVBUF); + FREE(ni->ni_wpa_ie, M_80211_NODE); + if (ni->ni_rsn_ie != NULL) + FREE(ni->ni_rsn_ie, M_80211_NODE); if (ni->ni_wme_ie != NULL) - FREE(ni->ni_wme_ie, M_DEVBUF); + FREE(ni->ni_wme_ie, M_80211_NODE); + if (ni->ni_ath_ie != NULL) + FREE(ni->ni_ath_ie, M_80211_NODE); IEEE80211_NODE_SAVEQ_DESTROY(ni); FREE(ni, M_80211_NODE); } -static u_int8_t +static int8_t node_getrssi(const struct ieee80211_node *ni) { return ni->ni_rssi; } static void +node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) +{ + *rssi = ni->ni_rssi; + *noise = ni->ni_noise; +} + +static void ieee80211_setup_node(struct ieee80211_node_table *nt, - struct ieee80211_node *ni, const u_int8_t *macaddr) + struct ieee80211_node *ni, const uint8_t *macaddr) { struct ieee80211com *ic = nt->nt_ic; int hash; @@ -965,6 +799,7 @@ ieee80211_setup_node(struct ieee80211_node_table *nt, ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); ni->ni_inact_reload = nt->nt_inact_init; ni->ni_inact = ni->ni_inact_reload; + ni->ni_ath_defkeyix = 0x7fff; IEEE80211_NODE_SAVEQ_INIT(ni, "unknown"); IEEE80211_NODE_LOCK(nt); @@ -976,7 +811,7 @@ ieee80211_setup_node(struct ieee80211_node_table *nt, } struct ieee80211_node * -ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) +ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) { struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; @@ -996,7 +831,7 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) * once the send completes. */ struct ieee80211_node * -ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr) +ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr) { struct ieee80211_node *ni; @@ -1010,7 +845,7 @@ ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr) ieee80211_node_initref(ni); /* mark referenced */ ni->ni_txpower = ic->ic_bss->ni_txpower; /* NB: required by ieee80211_fix_rate */ - ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ieee80211_node_set_chan(ic, ni); ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); /* XXX optimize away */ @@ -1026,7 +861,7 @@ ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr) } struct ieee80211_node * -ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr) +ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr) { struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; @@ -1041,7 +876,7 @@ ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr) ni->ni_txpower = ic->ic_bss->ni_txpower; ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); - ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ieee80211_node_set_chan(ic, ni); ni->ni_rsn = ic->ic_bss->ni_rsn; } else ic->ic_stats.is_rx_nodealloc++; @@ -1051,10 +886,10 @@ ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr) static struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT _ieee80211_find_node_debug(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, const char *func, int line) + const uint8_t *macaddr, const char *func, int line) #else _ieee80211_find_node(struct ieee80211_node_table *nt, - const u_int8_t *macaddr) + const uint8_t *macaddr) #endif { struct ieee80211_node *ni; @@ -1086,9 +921,9 @@ _ieee80211_find_node(struct ieee80211_node_table *nt, struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT ieee80211_find_node_debug(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, const char *func, int line) + const uint8_t *macaddr, const char *func, int line) #else -ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) +ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) #endif { struct ieee80211_node *ni; @@ -1107,7 +942,7 @@ ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) */ struct ieee80211_node * ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, - const u_int8_t macaddr[IEEE80211_ADDR_LEN]) + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; @@ -1120,158 +955,30 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, ni->ni_rates = ic->ic_bss->ni_rates; if (ic->ic_newassoc != NULL) ic->ic_newassoc(ni, 1); - /* XXX not right for 802.1x/WPA */ - ieee80211_node_authorize(ni); if (ic->ic_opmode == IEEE80211_M_AHDEMO) { /* - * Blindly propagate capabilities based on the - * local configuration. In particular this permits - * us to use QoS to disable ACK's. + * In adhoc demo mode there are no management + * frames to use to discover neighbor capabilities, + * so blindly propagate the local configuration + * so we can do interesting things (e.g. use + * WME to disable ACK's). */ if (ic->ic_flags & IEEE80211_F_WME) ni->ni_flags |= IEEE80211_NODE_QOS; + if (ic->ic_flags & IEEE80211_F_FF) + ni->ni_flags |= IEEE80211_NODE_FF; } + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ni); } return ni; } -#ifdef IEEE80211_DEBUG -static void -dump_probe_beacon(u_int8_t subtype, int isnew, - const u_int8_t mac[IEEE80211_ADDR_LEN], - const struct ieee80211_scanparams *sp) -{ - - printf("[%s] %s%s on chan %u (bss chan %u) ", - ether_sprintf(mac), isnew ? "new " : "", - ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], - sp->chan, sp->bchan); - ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); - printf("\n"); - - if (isnew) { - printf("[%s] caps 0x%x bintval %u erp 0x%x", - ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); - if (sp->country != NULL) { -#ifdef __FreeBSD__ - printf(" country info %*D", - sp->country[1], sp->country+2, " "); -#else - int i; - printf(" country info"); - for (i = 0; i < sp->country[1]; i++) - printf(" %02x", sp->country[i+2]); -#endif - } - printf("\n"); - } -} -#endif /* IEEE80211_DEBUG */ - -static void -saveie(u_int8_t **iep, const u_int8_t *ie) -{ - - if (ie == NULL) - *iep = NULL; - else - ieee80211_saveie(iep, ie); -} - -/* - * Process a beacon or probe response frame. - */ -void -ieee80211_add_scan(struct ieee80211com *ic, - const struct ieee80211_scanparams *sp, - const struct ieee80211_frame *wh, - int subtype, int rssi, int rstamp) -{ -#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - struct ieee80211_node_table *nt = &ic->ic_scan; - struct ieee80211_node *ni; - int newnode = 0; - - ni = ieee80211_find_node(nt, wh->i_addr2); - if (ni == NULL) { - /* - * Create a new entry. - */ - ni = ic->ic_node_alloc(nt); - if (ni == NULL) { - ic->ic_stats.is_rx_nodealloc++; - return; - } - ieee80211_setup_node(nt, ni, wh->i_addr2); - /* - * XXX inherit from ic_bss. - */ - ni->ni_authmode = ic->ic_bss->ni_authmode; - ni->ni_txpower = ic->ic_bss->ni_txpower; - ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ - ieee80211_set_chan(ic, ni, ic->ic_curchan); - ni->ni_rsn = ic->ic_bss->ni_rsn; - newnode = 1; - } -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN)) - dump_probe_beacon(subtype, newnode, wh->i_addr2, sp); -#endif - /* XXX ap beaconing multiple ssid w/ same bssid */ - if (sp->ssid[1] != 0 && - (ISPROBE(subtype) || ni->ni_esslen == 0)) { - ni->ni_esslen = sp->ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); - } - ni->ni_scangen = ic->ic_scan.nt_scangen; - IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp)); - ni->ni_intval = sp->bintval; - ni->ni_capinfo = sp->capinfo; - ni->ni_chan = &ic->ic_channels[sp->chan]; - ni->ni_fhdwell = sp->fhdwell; - ni->ni_fhindex = sp->fhindex; - ni->ni_erp = sp->erp; - if (sp->tim != NULL) { - struct ieee80211_tim_ie *ie = - (struct ieee80211_tim_ie *) sp->tim; - - ni->ni_dtim_count = ie->tim_count; - ni->ni_dtim_period = ie->tim_period; - } - /* - * Record the byte offset from the mac header to - * the start of the TIM information element for - * use by hardware and/or to speedup software - * processing of beacon frames. - */ - ni->ni_timoff = sp->timoff; - /* - * Record optional information elements that might be - * used by applications or drivers. - */ - saveie(&ni->ni_wme_ie, sp->wme); - saveie(&ni->ni_wpa_ie, sp->wpa); - - /* NB: must be after ni_chan is setup */ - ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT); - - if (!newnode) - ieee80211_free_node(ni); -#undef ISPROBE -} - void ieee80211_init_neighbor(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const struct ieee80211_scanparams *sp) { - - IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, - "%s: %p<%s>\n", __func__, ni, ether_sprintf(ni->ni_macaddr)); ni->ni_esslen = sp->ssid[1]; memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); @@ -1287,6 +994,10 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, ieee80211_saveie(&ni->ni_wme_ie, sp->wme); if (sp->wpa != NULL) ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa); + if (sp->rsn != NULL) + ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn); + if (sp->ath != NULL) + ieee80211_saveath(ni, sp->ath); /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ni, sp->rates, sp->xrates, @@ -1324,6 +1035,9 @@ ieee80211_add_neighbor(struct ieee80211com *ic, ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) #define IS_PSPOLL(wh) \ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) +#define IS_BAR(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_BAR) + /* * Locate the node for sender, track state, and then pass the * (referenced) node up to the 802.11 layer for its use. We @@ -1344,17 +1058,11 @@ ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_node_table *nt; struct ieee80211_node *ni; - /* XXX may want scanned nodes in the neighbor table for adhoc */ - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_MONITOR || - (ic->ic_flags & IEEE80211_F_SCAN)) - nt = &ic->ic_scan; - else - nt = &ic->ic_sta; /* XXX check ic_bss first in station mode */ /* XXX 4-address frames? */ + nt = &ic->ic_sta; IEEE80211_NODE_LOCK(nt); - if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) + if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/) ni = _ieee80211_find_node(nt, wh->i_addr1); else ni = _ieee80211_find_node(nt, wh->i_addr2); @@ -1386,19 +1094,14 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, struct ieee80211_node_table *nt; struct ieee80211_node *ni; - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_MONITOR || - (ic->ic_flags & IEEE80211_F_SCAN)) - nt = &ic->ic_scan; - else - nt = &ic->ic_sta; + nt = &ic->ic_sta; IEEE80211_NODE_LOCK(nt); if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) ni = nt->nt_keyixmap[keyix]; else ni = NULL; if (ni == NULL) { - if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) + if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/) ni = _ieee80211_find_node(nt, wh->i_addr1); else ni = _ieee80211_find_node(nt, wh->i_addr2); @@ -1420,13 +1123,13 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni); } } - } else { + } else ieee80211_ref_node(ni); - } IEEE80211_NODE_UNLOCK(nt); return ni; } +#undef IS_BAR #undef IS_PSPOLL #undef IS_CTL @@ -1436,10 +1139,10 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, */ struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr, +ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr, const char *func, int line) #else -ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr) +ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr) #endif { struct ieee80211_node_table *nt = &ic->ic_sta; @@ -1492,54 +1195,21 @@ ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr) } /* - * Like find but search based on the channel too. - */ -struct ieee80211_node * -#ifdef IEEE80211_DEBUG_REFCNT -ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, struct ieee80211_channel *chan, - const char *func, int line) -#else -ieee80211_find_node_with_channel(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, struct ieee80211_channel *chan) -#endif -{ - struct ieee80211_node *ni; - int hash; - - hash = IEEE80211_NODE_HASH(macaddr); - IEEE80211_NODE_LOCK(nt); - LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { - if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && - ni->ni_chan == chan) { - ieee80211_ref_node(ni); /* mark referenced */ - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, - REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr), - ieee80211_node_refcnt(ni)); - break; - } - } - IEEE80211_NODE_UNLOCK(nt); - return ni; -} - -/* * Like find but search based on the ssid too. */ struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid, + const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid, const char *func, int line) #else ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt, - const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid) + const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid) #endif { #define MATCH_SSID(ni, ssid, ssidlen) \ (ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0) - static const u_int8_t zeromac[IEEE80211_ADDR_LEN]; - struct ieee80211com *ic = nt->nt_ic; + static const uint8_t zeromac[IEEE80211_ADDR_LEN]; struct ieee80211_node *ni; int hash; @@ -1563,7 +1233,7 @@ ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt, } if (ni != NULL) { ieee80211_ref_node(ni); /* mark referenced */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); } @@ -1748,46 +1418,6 @@ ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt) } node_reclaim(nt, ni); } - ieee80211_reset_erp(ic); -} - -static void -ieee80211_free_allnodes(struct ieee80211_node_table *nt) -{ - - IEEE80211_NODE_LOCK(nt); - ieee80211_free_allnodes_locked(nt); - IEEE80211_NODE_UNLOCK(nt); -} - -/* - * Timeout entries in the scan cache. - */ -static void -ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt) -{ - struct ieee80211com *ic = nt->nt_ic; - struct ieee80211_node *ni, *tni; - - IEEE80211_NODE_LOCK(nt); - ni = ic->ic_bss; - /* XXX belongs elsewhere */ - if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) { - m_freem(ni->ni_rxfrag[0]); - ni->ni_rxfrag[0] = NULL; - } - TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) { - if (ni->ni_inact && --ni->ni_inact == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, - "[%s] scan candidate purged from cache " - "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr), - ieee80211_node_refcnt(ni)); - node_reclaim(nt, ni); - } - } - IEEE80211_NODE_UNLOCK(nt); - - nt->nt_inact_timer = IEEE80211_INACT_WAIT; } /* @@ -1812,8 +1442,6 @@ ieee80211_timeout_stations(struct ieee80211_node_table *nt) ic->ic_opmode == IEEE80211_M_AHDEMO); IEEE80211_SCAN_LOCK(nt); gen = ++nt->nt_scangen; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, - "%s: %s scangen %u\n", __func__, nt->nt_name, gen); restart: IEEE80211_NODE_LOCK(nt); TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { @@ -1826,7 +1454,8 @@ restart: * will be reclaimed when the last reference to them * goes away (when frame xmits complete). */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + if ((ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_STA) && (ni->ni_flags & IEEE80211_NODE_AREF) == 0) continue; /* @@ -1848,50 +1477,27 @@ restart: ni->ni_inact--; if (ni->ni_associd != 0 || isadhoc) { /* - * Age frames on the power save queue. The - * aging interval is 4 times the listen - * interval specified by the station. This - * number is factored into the age calculations - * when the frame is placed on the queue. We - * store ages as time differences we can check - * and/or adjust only the head of the list. + * Age frames on the power save queue. */ - if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { - struct mbuf *m; - int discard = 0; - - IEEE80211_NODE_SAVEQ_LOCK(ni); - while (IF_POLL(&ni->ni_savedq, m) != NULL && - M_AGE_GET(m) < IEEE80211_INACT_WAIT) { -IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ - _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); - m_freem(m); - discard++; - } - if (m != NULL) - M_AGE_SUB(m, IEEE80211_INACT_WAIT); - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - - if (discard != 0) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_POWER, - "[%s] discard %u frames for age\n", - ether_sprintf(ni->ni_macaddr), - discard); - IEEE80211_NODE_STAT_ADD(ni, - ps_discard, discard); - if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) - ic->ic_set_tim(ni, 0); - } - } + if (ieee80211_node_saveq_age(ni) != 0 && + IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 && + ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); /* * Probe the station before time it out. We * send a null data frame which may not be * universally supported by drivers (need it * for ps-poll support so it should be...). + * + * XXX don't probe the station unless we've + * received a frame from them (and have + * some idea of the rates they are capable + * of); this will get fixed more properly + * soon with better handling of the rate set. */ if (0 < ni->ni_inact && - ni->ni_inact <= ic->ic_inact_probe) { + ni->ni_inact <= ic->ic_inact_probe && + ni->ni_rates.rs_nrates != 0) { IEEE80211_NOTE(ic, IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, "%s", @@ -1927,8 +1533,8 @@ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether * completed or by ieee80211_node_leave. * * Separately we must drop the node lock before sending - * in case the driver takes a lock, as this will result - * in LOR between the node lock and the driver lock. + * in case the driver takes a lock, as this can result + * in a LOR between the node lock and the driver lock. */ IEEE80211_NODE_UNLOCK(nt); if (ni->ni_associd != 0) { @@ -1944,8 +1550,18 @@ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether IEEE80211_NODE_UNLOCK(nt); IEEE80211_SCAN_UNLOCK(nt); +} - nt->nt_inact_timer = IEEE80211_INACT_WAIT; +void +ieee80211_node_timeout(void *arg) +{ + struct ieee80211com *ic = arg; + + ieee80211_scan_timeout(ic); + ieee80211_timeout_stations(&ic->ic_sta); + + callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, + ieee80211_node_timeout, ic); } void @@ -1987,14 +1603,20 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT, ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK, ni->ni_rxfragstamp); - printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n", - ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo); + printf("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n", + ni->ni_rstamp, ni->ni_rssi, ni->ni_noise, + ni->ni_intval, ni->ni_capinfo); printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n", ether_sprintf(ni->ni_bssid), ni->ni_esslen, ni->ni_essid, ni->ni_chan->ic_freq, ni->ni_chan->ic_flags); printf("\tfails %u inact %u txrate %u\n", ni->ni_fails, ni->ni_inact, ni->ni_txrate); + printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n", + ni->ni_htcap, ni->ni_htparam, + ni->ni_htctlchan, ni->ni_ht2ndchan); + printf("\thtopmode %x htstbc %x chw %u\n", + ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw); } void @@ -2024,7 +1646,14 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) "[%s] station needs long slot time, count %d\n", ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta); /* XXX vap's w/ conflicting needs won't work */ - ieee80211_set_shortslottime(ic, 0); + if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) { + /* + * Don't force slot time when switched to turbo + * mode as non-ERP stations won't be present; this + * need only be done when on the normal G channel. + */ + ieee80211_set_shortslottime(ic, 0); + } } /* * If the new station is not an ERP station @@ -2067,7 +1696,7 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp int newassoc; if (ni->ni_associd == 0) { - u_int16_t aid; + uint16_t aid; /* * It would be good to search the bitmap @@ -2088,20 +1717,26 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); ic->ic_sta_assoc++; newassoc = 1; - if (ic->ic_curmode == IEEE80211_MODE_11G && - IEEE80211_IS_CHAN_FULL(ni->ni_chan)) + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) ieee80211_node_join_11g(ic, ni); } else newassoc = 0; IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, - "[%s] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n", + "[%s] station %sassociated at aid %d: %s preamble, %s slot time%s%s%s%s%s\n", ether_sprintf(ni->ni_macaddr), newassoc ? "" : "re", IEEE80211_NODE_AID(ni), ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", - ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + ni->ni_flags & IEEE80211_NODE_HT ? + (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" ); /* give driver a chance to setup state like ni_txrate */ @@ -2123,9 +1758,9 @@ static void ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) { - KASSERT(ic->ic_curmode == IEEE80211_MODE_11G, - ("not in 11g, bss %u:0x%x, curmode %u", ni->ni_chan->ic_freq, - ni->ni_chan->ic_flags, ic->ic_curmode)); + KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan), + ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq, + ic->ic_bsschan->ic_flags, ic->ic_curmode)); /* * If a long slot station do the slot time bookkeeping. @@ -2191,9 +1826,7 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) "[%s] station with aid %d leaves\n", ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_AID(ni)); - KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO, + KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("unexpected operating mode %u", ic->ic_opmode)); /* * If node wasn't previously associated all @@ -2214,8 +1847,8 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) ni->ni_associd = 0; ic->ic_sta_assoc--; - if (ic->ic_curmode == IEEE80211_MODE_11G && - IEEE80211_IS_CHAN_FULL(ni->ni_chan)) + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) ieee80211_node_leave_11g(ic, ni); /* * Cleanup station state. In particular clear various @@ -2239,38 +1872,30 @@ done: ieee80211_free_node(ni); } -u_int8_t +int8_t ieee80211_getrssi(struct ieee80211com *ic) { #define NZ(x) ((x) == 0 ? 1 : (x)) struct ieee80211_node_table *nt = &ic->ic_sta; - u_int32_t rssi_samples, rssi_total; + int rssi_samples; + int32_t rssi_total; struct ieee80211_node *ni; rssi_total = 0; rssi_samples = 0; switch (ic->ic_opmode) { case IEEE80211_M_IBSS: /* average of all ibss neighbors */ - /* XXX locking */ - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) - if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) { - rssi_samples++; - rssi_total += ic->ic_node_getrssi(ni); - } - break; case IEEE80211_M_AHDEMO: /* average of all neighbors */ - /* XXX locking */ - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { - rssi_samples++; - rssi_total += ic->ic_node_getrssi(ni); - } - break; case IEEE80211_M_HOSTAP: /* average of all associated stations */ /* XXX locking */ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) - if (IEEE80211_AID(ni->ni_associd) != 0) { - rssi_samples++; - rssi_total += ic->ic_node_getrssi(ni); + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) { + int8_t rssi = ic->ic_node_getrssi(ni); + if (rssi != 0) { + rssi_samples++; + rssi_total += rssi; + } } break; case IEEE80211_M_MONITOR: /* XXX */ @@ -2285,35 +1910,16 @@ ieee80211_getrssi(struct ieee80211com *ic) #undef NZ } -/* - * Indicate whether there are frames queued for a station in power-save mode. - */ -static void -ieee80211_set_tim(struct ieee80211_node *ni, int set) +void +ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise) { - struct ieee80211com *ic = ni->ni_ic; - u_int16_t aid; - KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_IBSS, - ("operating mode %u", ic->ic_opmode)); - - aid = IEEE80211_AID(ni->ni_associd); - KASSERT(aid < ic->ic_max_aid, - ("bogus aid %u, max %u", aid, ic->ic_max_aid)); - - IEEE80211_BEACON_LOCK(ic); - if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { - if (set) { - setbit(ic->ic_tim_bitmap, aid); - ic->ic_ps_pending++; - } else { - clrbit(ic->ic_tim_bitmap, aid); - ic->ic_ps_pending--; - } - ic->ic_flags |= IEEE80211_F_TIMUPDATE; - } - IEEE80211_BEACON_UNLOCK(ic); + if (ic->ic_bss == NULL) /* NB: shouldn't happen */ + return; + ic->ic_node_getsignal(ic->ic_bss, rssi, noise); + /* for non-station mode return avg'd rssi accounting */ + if (ic->ic_opmode != IEEE80211_M_STA) + *rssi = ieee80211_getrssi(ic); } /* @@ -2323,8 +1929,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set) static void ieee80211_node_table_init(struct ieee80211com *ic, struct ieee80211_node_table *nt, - const char *name, int inact, int keyixmax, - void (*timeout)(struct ieee80211_node_table *)) + const char *name, int inact, int keyixmax) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, @@ -2338,7 +1943,6 @@ ieee80211_node_table_init(struct ieee80211com *ic, nt->nt_name = name; nt->nt_scangen = 1; nt->nt_inact_init = inact; - nt->nt_timeout = timeout; nt->nt_keyixmax = keyixmax; if (nt->nt_keyixmax > 0) { MALLOC(nt->nt_keyixmap, struct ieee80211_node **, @@ -2352,7 +1956,7 @@ ieee80211_node_table_init(struct ieee80211com *ic, nt->nt_keyixmap = NULL; } -void +static void ieee80211_node_table_reset(struct ieee80211_node_table *nt) { @@ -2360,7 +1964,6 @@ ieee80211_node_table_reset(struct ieee80211_node_table *nt) "%s %s table\n", __func__, nt->nt_name); IEEE80211_NODE_LOCK(nt); - nt->nt_inact_timer = 0; ieee80211_free_allnodes_locked(nt); IEEE80211_NODE_UNLOCK(nt); } diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index ed09ae58e45b..cb960d10882b 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -29,6 +29,7 @@ #define _NET80211_IEEE80211_NODE_H_ #include <net80211/ieee80211_ioctl.h> /* for ieee80211_nodestats */ +#include <net80211/ieee80211_ht.h> /* for aggregation state */ /* * Each ieee80211com instance has a single timer that fires once a @@ -52,23 +53,23 @@ #define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */ #define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */ -#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */ +#define IEEE80211_TRANS_WAIT 2 /* mgt frame tx timer (secs) */ #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ - (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ IEEE80211_NODE_HASHSIZE) struct ieee80211_rsnparms { - u_int8_t rsn_mcastcipher; /* mcast/group cipher */ - u_int8_t rsn_mcastkeylen; /* mcast key length */ - u_int8_t rsn_ucastcipherset; /* unicast cipher set */ - u_int8_t rsn_ucastcipher; /* selected unicast cipher */ - u_int8_t rsn_ucastkeylen; /* unicast key length */ - u_int8_t rsn_keymgmtset; /* key mangement algorithms */ - u_int8_t rsn_keymgmt; /* selected key mgmt algo */ - u_int16_t rsn_caps; /* capabilities */ + uint8_t rsn_mcastcipher; /* mcast/group cipher */ + uint8_t rsn_mcastkeylen; /* mcast key length */ + uint8_t rsn_ucastcipherset; /* unicast cipher set */ + uint8_t rsn_ucastcipher; /* selected unicast cipher */ + uint8_t rsn_ucastkeylen; /* unicast key length */ + uint8_t rsn_keymgmtset; /* key mangement algorithms */ + uint8_t rsn_keymgmt; /* selected key mgmt algo */ + uint16_t rsn_caps; /* capabilities */ }; struct ieee80211_node_table; @@ -87,53 +88,82 @@ struct ieee80211_node { LIST_ENTRY(ieee80211_node) ni_hash; u_int ni_refcnt; u_int ni_scangen; /* gen# for timeout scan */ - u_int8_t ni_authmode; /* authentication algorithm */ - u_int16_t ni_flags; /* special-purpose state */ + uint8_t ni_authmode; /* authentication algorithm */ + uint8_t ni_ath_flags; /* Atheros feature flags */ + /* NB: These must have the same values as IEEE80211_ATHC_* */ +#define IEEE80211_NODE_TURBOP 0x0001 /* Turbo prime enable */ +#define IEEE80211_NODE_COMP 0x0002 /* Compresssion enable */ +#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */ +#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */ +#define IEEE80211_NODE_AR 0x0010 /* AR capable */ +#define IEEE80211_NODE_BOOST 0x0080 +#define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */ +#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */ + uint16_t ni_flags; /* special-purpose state */ #define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ #define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ #define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ /* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */ #define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ #define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */ - u_int16_t ni_associd; /* assoc response */ - u_int16_t ni_txpower; /* current transmit power */ - u_int16_t ni_vlan; /* vlan tag */ - u_int32_t *ni_challenge; /* shared-key challenge */ - u_int8_t *ni_wpa_ie; /* captured WPA/RSN ie */ - u_int8_t *ni_wme_ie; /* captured WME ie */ +#define IEEE80211_NODE_HT 0x0040 /* HT enabled */ +#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */ + uint16_t ni_ath_defkeyix;/* Atheros def key index */ + uint16_t ni_associd; /* assoc response */ + uint16_t ni_txpower; /* current transmit power */ + uint16_t ni_vlan; /* vlan tag */ + uint32_t *ni_challenge; /* shared-key challenge */ + uint8_t *ni_wpa_ie; /* captured WPA ie */ + uint8_t *ni_rsn_ie; /* captured RSN ie */ + uint8_t *ni_wme_ie; /* captured WME ie */ + uint8_t *ni_ath_ie; /* captured Atheros ie */ #define IEEE80211_NONQOS_TID 16 /* index for non-QoS sta */ - u_int16_t ni_txseqs[17]; /* tx seq per-tid */ - u_int16_t ni_rxseqs[17]; /* rx seq previous per-tid*/ - u_int32_t ni_rxfragstamp; /* time stamp of last rx frag */ + uint16_t ni_txseqs[17]; /* tx seq per-tid */ + uint16_t ni_rxseqs[17]; /* rx seq previous per-tid*/ + uint32_t ni_rxfragstamp; /* time stamp of last rx frag */ struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */ struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */ struct ieee80211_key ni_ucastkey; /* unicast key */ /* hardware */ - u_int32_t ni_rstamp; /* recv timestamp */ - u_int8_t ni_rssi; /* recv ssi */ + uint32_t ni_rstamp; /* recv timestamp */ + int8_t ni_rssi; /* recv ssi */ + int8_t ni_noise; /* noise floor */ /* header */ - u_int8_t ni_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; + uint8_t ni_macaddr[IEEE80211_ADDR_LEN]; + uint8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ union { - u_int8_t data[8]; - u_int64_t tsf; + uint8_t data[8]; + uint64_t tsf; } ni_tstamp; /* from last rcv'd beacon */ - u_int16_t ni_intval; /* beacon interval */ - u_int16_t ni_capinfo; /* capabilities */ - u_int8_t ni_esslen; - u_int8_t ni_essid[IEEE80211_NWID_LEN]; + uint16_t ni_intval; /* beacon interval */ + uint16_t ni_capinfo; /* capabilities */ + uint8_t ni_esslen; + uint8_t ni_essid[IEEE80211_NWID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ - struct ieee80211_channel *ni_chan; /* XXX multiple uses */ - u_int16_t ni_fhdwell; /* FH only */ - u_int8_t ni_fhindex; /* FH only */ - u_int8_t ni_erp; /* ERP from beacon/probe resp */ - u_int16_t ni_timoff; /* byte offset to TIM ie */ - u_int8_t ni_dtim_period; /* DTIM period */ - u_int8_t ni_dtim_count; /* DTIM count for last bcn */ + struct ieee80211_channel *ni_chan; + uint16_t ni_fhdwell; /* FH only */ + uint8_t ni_fhindex; /* FH only */ + uint8_t ni_erp; /* ERP from beacon/probe resp */ + uint16_t ni_timoff; /* byte offset to TIM ie */ + uint8_t ni_dtim_period; /* DTIM period */ + uint8_t ni_dtim_count; /* DTIM count for last bcn */ + + /* 11n state */ + uint16_t ni_htcap; /* HT capabilities */ + uint8_t ni_htparam; /* HT params */ + uint8_t ni_htctlchan; /* HT control channel */ + uint8_t ni_ht2ndchan; /* HT 2nd channel */ + uint8_t ni_htopmode; /* HT operating mode */ + uint8_t ni_htstbc; /* HT */ + uint8_t ni_reqcw; /* requested tx channel width */ + uint8_t ni_chw; /* negotiated channel width */ + struct ieee80211_htrateset ni_htrates; /* negotiated ht rate set */ + struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC]; + struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID]; /* others */ int ni_fails; /* failure count to associate */ @@ -145,6 +175,8 @@ struct ieee80211_node { }; MALLOC_DECLARE(M_80211_NODE); +#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP) + #define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd) #define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++) @@ -180,15 +212,13 @@ ieee80211_node_is_authorized(const struct ieee80211_node *ni) void ieee80211_node_authorize(struct ieee80211_node *); void ieee80211_node_unauthorize(struct ieee80211_node *); -void ieee80211_begin_scan(struct ieee80211com *, int); -int ieee80211_next_scan(struct ieee80211com *); void ieee80211_probe_curchan(struct ieee80211com *, int); void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *); void ieee80211_reset_bss(struct ieee80211com *); -void ieee80211_cancel_scan(struct ieee80211com *); -void ieee80211_end_scan(struct ieee80211com *); int ieee80211_ibss_merge(struct ieee80211_node *); -int ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *); +struct ieee80211_scan_entry; +int ieee80211_sta_join(struct ieee80211com *, + const struct ieee80211_scan_entry *); void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *); /* @@ -202,46 +232,43 @@ struct ieee80211_node_table { ieee80211_node_lock_t nt_nodelock; /* on node table */ TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */ LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE]; + struct ieee80211_node **nt_keyixmap; /* key ix -> node map */ + int nt_keyixmax; /* keyixmap size */ const char *nt_name; /* for debugging */ ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */ u_int nt_scangen; /* gen# for timeout scan */ - int nt_inact_timer; /* inactivity timer */ int nt_inact_init; /* initial node inact setting */ - struct ieee80211_node **nt_keyixmap; /* key ix -> node map */ - int nt_keyixmax; /* keyixmap size */ - - void (*nt_timeout)(struct ieee80211_node_table *); }; -void ieee80211_node_table_reset(struct ieee80211_node_table *); struct ieee80211_node *ieee80211_alloc_node( - struct ieee80211_node_table *, const u_int8_t *); + struct ieee80211_node_table *, const uint8_t *); struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *, - const u_int8_t *macaddr); + const uint8_t *macaddr); struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *, - const u_int8_t *); + const uint8_t *); #ifdef IEEE80211_DEBUG_REFCNT void ieee80211_free_node_debug(struct ieee80211_node *, const char *func, int line); -struct ieee80211_node *ieee80211_find_node_debug( - struct ieee80211_node_table *, const u_int8_t *, +struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *, + const uint8_t *, + const char *func, int line); +struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *, + const struct ieee80211_frame_min *, const char *func, int line); -struct ieee80211_node * ieee80211_find_rxnode_debug( - struct ieee80211com *, const struct ieee80211_frame_min *, +struct ieee80211_node * ieee80211_find_rxnode_withkey_debug( + struct ieee80211com *, + const struct ieee80211_frame_min *, uint16_t keyix, const char *func, int line); struct ieee80211_node * ieee80211_find_rxnode_withkey_debug( struct ieee80211com *, - const struct ieee80211_frame_min *, u_int16_t keyix, + const struct ieee80211_frame_min *, uint16_t keyix, const char *func, int line); -struct ieee80211_node *ieee80211_find_txnode_debug( - struct ieee80211com *, const u_int8_t *, +struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *, + const uint8_t *, const char *func, int line); -struct ieee80211_node *ieee80211_find_node_with_channel_debug( - struct ieee80211_node_table *, const u_int8_t *macaddr, - struct ieee80211_channel *, const char *func, int line); struct ieee80211_node *ieee80211_find_node_with_ssid_debug( - struct ieee80211_node_table *, const u_int8_t *macaddr, - u_int ssidlen, const u_int8_t *ssid, + struct ieee80211_node_table *, const uint8_t *macaddr, + u_int ssidlen, const uint8_t *ssid, const char *func, int line); #define ieee80211_free_node(ni) \ ieee80211_free_node_debug(ni, __func__, __LINE__) @@ -253,28 +280,24 @@ struct ieee80211_node *ieee80211_find_node_with_ssid_debug( ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__) #define ieee80211_find_txnode(nt, mac) \ ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__) -#define ieee80211_find_node_with_channel(nt, mac, c) \ - ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__) #define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \ ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__) #else void ieee80211_free_node(struct ieee80211_node *); -struct ieee80211_node *ieee80211_find_node( - struct ieee80211_node_table *, const u_int8_t *); -struct ieee80211_node * ieee80211_find_rxnode( - struct ieee80211com *, const struct ieee80211_frame_min *); +struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *, + const uint8_t *); +struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *, + const struct ieee80211_frame_min *); struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *, - const struct ieee80211_frame_min *, u_int16_t keyix); -struct ieee80211_node *ieee80211_find_txnode( - struct ieee80211com *, const u_int8_t *); -struct ieee80211_node *ieee80211_find_node_with_channel( - struct ieee80211_node_table *, const u_int8_t *macaddr, - struct ieee80211_channel *); + const struct ieee80211_frame_min *, uint16_t keyix); +struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, + const uint8_t *); struct ieee80211_node *ieee80211_find_node_with_ssid( - struct ieee80211_node_table *, const u_int8_t *macaddr, - u_int ssidlen, const u_int8_t *ssid); + struct ieee80211_node_table *, const uint8_t *macaddr, + u_int ssidlen, const uint8_t *ssid); #endif int ieee80211_node_delucastkey(struct ieee80211_node *); +void ieee80211_node_timeout(void *arg); typedef void ieee80211_iter_func(void *, struct ieee80211_node *); void ieee80211_iterate_nodes(struct ieee80211_node_table *, @@ -285,45 +308,16 @@ void ieee80211_dump_node(struct ieee80211_node_table *, void ieee80211_dump_nodes(struct ieee80211_node_table *); struct ieee80211_node *ieee80211_fakeup_adhoc_node( - struct ieee80211_node_table *, const u_int8_t macaddr[]); -void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int); -void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); -u_int8_t ieee80211_getrssi(struct ieee80211com *ic); - -/* - * Parameters supplied when adding/updating an entry in a - * scan cache. Pointer variables should be set to NULL - * if no data is available. Pointer references can be to - * local data; any information that is saved will be copied. - * All multi-byte values must be in host byte order. - */ -struct ieee80211_scanparams { - u_int16_t capinfo; /* 802.11 capabilities */ - u_int16_t fhdwell; /* FHSS dwell interval */ - u_int8_t chan; /* */ - u_int8_t bchan; - u_int8_t fhindex; - u_int8_t erp; - u_int16_t bintval; - u_int8_t timoff; - u_int8_t *tim; - u_int8_t *tstamp; - u_int8_t *country; - u_int8_t *ssid; - u_int8_t *rates; - u_int8_t *xrates; - u_int8_t *wpa; - u_int8_t *wme; -}; - -void ieee80211_add_scan(struct ieee80211com *, - const struct ieee80211_scanparams *, - const struct ieee80211_frame *, - int subtype, int rssi, int rstamp); + struct ieee80211_node_table *, const uint8_t macaddr[]); +struct ieee80211_scanparams; void ieee80211_init_neighbor(struct ieee80211_node *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); +void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int); +void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); +int8_t ieee80211_getrssi(struct ieee80211com *); +void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *); #endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index d9e92a36006f..5b08f68dc8e9 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <net/if_vlan_var.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> #ifdef INET #include <netinet/in.h> @@ -53,6 +54,16 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip.h> #endif +#define ETHER_HEADER_COPY(dst, src) \ + memcpy(dst, src, sizeof(struct ether_header)) + +static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic, + struct mbuf *m1, const struct ether_header *eh1, + struct mbuf *m2, const struct ether_header *eh2); +static int ieee80211_fragment(struct ieee80211com *, struct mbuf *, + u_int hdrsize, u_int ciphdrsize, u_int mtu); +static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); + #ifdef IEEE80211_DEBUG /* * Decide if an outbound management frame should be @@ -82,9 +93,9 @@ ieee80211_send_setup(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_frame *wh, int type, - const u_int8_t sa[IEEE80211_ADDR_LEN], - const u_int8_t da[IEEE80211_ADDR_LEN], - const u_int8_t bssid[IEEE80211_ADDR_LEN]) + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN]) { #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) @@ -110,6 +121,14 @@ ieee80211_send_setup(struct ieee80211com *ic, IEEE80211_ADDR_COPY(wh->i_addr2, bssid); IEEE80211_ADDR_COPY(wh->i_addr3, sa); break; + case IEEE80211_M_WDS: + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + /* XXX cheat, bssid holds RA */ + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); + break; case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ break; } @@ -119,9 +138,9 @@ ieee80211_send_setup(struct ieee80211com *ic, IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } - *(u_int16_t *)&wh->i_dur[0] = 0; + *(uint16_t *)&wh->i_dur[0] = 0; /* NB: use non-QoS tid */ - *(u_int16_t *)&wh->i_seq[0] = + *(uint16_t *)&wh->i_seq[0] = htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[IEEE80211_NONQOS_TID]++; #undef WH4 @@ -134,9 +153,9 @@ ieee80211_send_setup(struct ieee80211com *ic, * dispatched to the driver, then it is responsible for freeing the * reference (and potentially free'ing up any associated storage). */ -static int +int ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m, int type, int timer) + struct mbuf *m, int type) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame *wh; @@ -186,14 +205,9 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, #endif IEEE80211_NODE_STAT(ni, tx_mgmt); IF_ENQUEUE(&ic->ic_mgtq, m); - if (timer) { - /* - * Set the mgt frame timeout. - */ - ic->ic_mgt_timer = timer; - ifp->if_timer = 1; - } if_start(ifp); + ifp->if_opackets++; + return 0; } @@ -292,7 +306,7 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, * to the 802.11 layer and continue. We'll get * the frame back when the time is right. */ - ieee80211_pwrsave(ic, ni, m); + ieee80211_pwrsave(ni, m); error = 0; goto reclaim; } @@ -339,10 +353,11 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { /* XXX debug msg */ - ic->ic_stats.is_tx_nobuf++; ieee80211_unref_node(&ni); + ic->ic_stats.is_tx_nobuf++; return ENOMEM; } + MH_ALIGN(m, sizeof(struct ieee80211_frame)); m->m_pkthdr.rcvif = (void *) ni; wh = mtod(m, struct ieee80211_frame *); @@ -351,7 +366,8 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); /* NB: power management bit is never sent by an AP */ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - ic->ic_opmode != IEEE80211_M_HOSTAP) + ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_WDS) wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); @@ -428,7 +444,7 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod eh = mtod(m, struct ether_header *); if (eh->ether_type == htons(ETHERTYPE_IP)) { const struct ip *ip = (struct ip *) - (mtod(m, u_int8_t *) + sizeof (*eh)); + (mtod(m, uint8_t *) + sizeof (*eh)); /* * IP frame, map the TOS field. */ @@ -495,7 +511,7 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, struct ieee80211_key *key, struct mbuf *m) { #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) - int needed_space = hdrsize; + int needed_space = ic->ic_headroom + hdrsize; if (key != NULL) { /* XXX belongs in crypto code? */ @@ -613,8 +629,13 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_frame *wh; struct ieee80211_key *key; struct llc *llc; - int hdrsize, datalen, addqos; + int hdrsize, datalen, addqos, txfrag, isff; + /* + * Copy existing Ethernet header to a safe place. The + * rest of the code assumes it's ok to strip it when + * reorganizing state for the final encapsulation. + */ KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); @@ -653,29 +674,83 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * once negotiated in which case we'll need to make this * configurable. */ - addqos = (ni->ni_flags & IEEE80211_NODE_QOS) && + addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && eh.ether_type != htons(ETHERTYPE_PAE); if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else hdrsize = sizeof(struct ieee80211_frame); if (ic->ic_flags & IEEE80211_F_DATAPAD) - hdrsize = roundup(hdrsize, sizeof(u_int32_t)); - m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); - if (m == NULL) { - /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ - goto bad; - } + hdrsize = roundup(hdrsize, sizeof(uint32_t)); - /* NB: this could be optimized because of ieee80211_mbuf_adjust */ - m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); - llc = mtod(m, struct llc *); - llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; - llc->llc_control = LLC_UI; - llc->llc_snap.org_code[0] = 0; - llc->llc_snap.org_code[1] = 0; - llc->llc_snap.org_code[2] = 0; - llc->llc_snap.ether_type = eh.ether_type; + if ((isff = m->m_flags & M_FF) != 0) { + struct mbuf *m2; + struct ether_header eh2; + + /* + * Fast frame encapsulation. There must be two packets + * chained with m_nextpkt. We do header adjustment for + * each, add the tunnel encapsulation, and then concatenate + * the mbuf chains to form a single frame for transmission. + */ + m2 = m->m_nextpkt; + if (m2 == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: only one frame\n", __func__); + goto bad; + } + m->m_nextpkt = NULL; + /* + * Include fast frame headers in adjusting header + * layout; this allocates space according to what + * ieee80211_encap_fastframe will do. + */ + m = ieee80211_mbuf_adjust(ic, + hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 + + sizeof(struct ether_header), + key, m); + if (m == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + m_freem(m2); + goto bad; + } + /* + * Copy second frame's Ethernet header out of line + * and adjust for encapsulation headers. Note that + * we make room for padding in case there isn't room + * at the end of first frame. + */ + KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); + memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header)); + m2 = ieee80211_mbuf_adjust(ic, + ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), + NULL, m2); + if (m2 == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + goto bad; + } + m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2); + if (m == NULL) + goto bad; + } else { + /* + * Normal frame. + */ + m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); + if (m == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + goto bad; + } + /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh.ether_type; + } datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ M_PREPEND(m, hdrsize, M_DONTWAIT); @@ -685,7 +760,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; - *(u_int16_t *)wh->i_dur = 0; + *(uint16_t *)wh->i_dur = 0; switch (ic->ic_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; @@ -711,6 +786,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); break; case IEEE80211_M_MONITOR: + case IEEE80211_M_WDS: goto bad; } if (m->m_flags & M_MORE_DATA) @@ -724,19 +800,52 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, /* map from access class/queue to 11e header priorty value */ tid = WME_AC_TO_TID(ac); qwh->i_qos[0] = tid & IEEE80211_QOS_TID; + /* + * Check if A-MPDU tx aggregation is setup or if we + * should try to enable it. The sta must be associated + * with HT and A-MPDU enabled for use. On the first + * frame that goes out We issue an ADDBA request and + * wait for a reply. The frame being encapsulated + * will go out w/o using A-MPDU, or possibly it might + * be collected by the driver and held/retransmit. + * ieee80211_ampdu_request handles staggering requests + * in case the receiver NAK's us or we are otherwise + * unable to establish a BA stream. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + + if (IEEE80211_AMPDU_RUNNING(tap)) { + /* + * Operational, mark frame for aggregation. + */ + qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; + } else if (!IEEE80211_AMPDU_REQUESTED(tap)) { + /* + * Not negotiated yet, request service. + */ + ieee80211_ampdu_request(ni, tap); + } + } + /* XXX works even when BA marked above */ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) - qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; + qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; qwh->i_qos[1] = 0; qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; - *(u_int16_t *)wh->i_seq = + *(uint16_t *)wh->i_seq = htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[tid]++; } else { - *(u_int16_t *)wh->i_seq = + *(uint16_t *)wh->i_seq = htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[IEEE80211_NONQOS_TID]++; } + /* check if xmit fragmentation is required */ + txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold && + !IEEE80211_IS_MULTICAST(wh->i_addr1) && + !isff); /* NB: don't fragment ff's */ if (key != NULL) { /* * IEEE 802.1X: send EAPOL frames always in the clear. @@ -748,8 +857,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, !IEEE80211_KEY_UNDEFINED(key) : !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { wh->i_fc[1] |= IEEE80211_FC1_WEP; - /* XXX do fragmentation */ - if (!ieee80211_crypto_enmic(ic, key, m, 0)) { + if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, "[%s] enmic failed, discard frame\n", ether_sprintf(eh.ether_dhost)); @@ -758,6 +866,15 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } } } + /* + * NB: frag flags may leak from above; they should only + * be set on return to the caller if we fragment at + * the 802.11 layer. + */ + m->m_flags &= ~(M_FRAG | M_FIRSTFRAG); + if (txfrag && !ieee80211_fragment(ic, m, hdrsize, + key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold)) + goto bad; IEEE80211_NODE_STAT(ni, tx_data); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) @@ -774,10 +891,231 @@ bad: } /* + * Do Ethernet-LLC encapsulation for each payload in a fast frame + * tunnel encapsulation. The frame is assumed to have an Ethernet + * header at the front that must be stripped before prepending the + * LLC followed by the Ethernet header passed in (with an Ethernet + * type that specifies the payload size). + */ +static struct mbuf * +ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, + const struct ether_header *eh) +{ + struct llc *llc; + uint16_t payload; + + /* XXX optimize by combining m_adj+M_PREPEND */ + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh->ether_type; + payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ + + M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); + if (m == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: no space for ether_header\n", __func__); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + ETHER_HEADER_COPY(mtod(m, void *), eh); + mtod(m, struct ether_header *)->ether_type = htons(payload); + return m; +} + +/* + * Do fast frame tunnel encapsulation. The two frames and + * Ethernet headers are supplied. The caller is assumed to + * have arrange for space in the mbuf chains for encapsulating + * headers (to avoid major mbuf fragmentation). + * + * The encapsulated frame is returned or NULL if there is a + * problem (should not happen). + */ +static struct mbuf * +ieee80211_encap_fastframe(struct ieee80211com *ic, + struct mbuf *m1, const struct ether_header *eh1, + struct mbuf *m2, const struct ether_header *eh2) +{ + struct llc *llc; + struct mbuf *m; + int pad; + + /* + * First, each frame gets a standard encapsulation. + */ + m1 = ieee80211_encap1(ic, m1, eh1); + if (m1 == NULL) { + m_freem(m2); + return NULL; + } + m2 = ieee80211_encap1(ic, m2, eh2); + if (m2 == NULL) { + m_freem(m1); + return NULL; + } + + /* + * Pad leading frame to a 4-byte boundary. If there + * is space at the end of the first frame, put it + * there; otherwise prepend to the front of the second + * frame. We know doing the second will always work + * because we reserve space above. We prefer appending + * as this typically has better DMA alignment properties. + */ + for (m = m1; m->m_next != NULL; m = m->m_next) + ; + pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len; + if (pad) { + if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */ + m2->m_data -= pad; + m2->m_len += pad; + m2->m_pkthdr.len += pad; + } else { /* append to first */ + m->m_len += pad; + m1->m_pkthdr.len += pad; + } + } + + /* + * Now, stick 'em together and prepend the tunnel headers; + * first the Atheros tunnel header (all zero for now) and + * then a special fast frame LLC. + * + * XXX optimize by prepending together + */ + m->m_next = m2; /* NB: last mbuf from above */ + m1->m_pkthdr.len += m2->m_pkthdr.len; + M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT); + if (m1 == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: no space for tunnel header\n", __func__); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); + + M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT); + if (m1 == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: no space for llc header\n", __func__); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + llc = mtod(m1, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0; + llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1; + llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; + llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); + + ic->ic_stats.is_ff_encap++; + + return m1; +} + +/* + * Fragment the frame according to the specified mtu. + * The size of the 802.11 header (w/o padding) is provided + * so we don't need to recalculate it. We create a new + * mbuf for each fragment and chain it through m_nextpkt; + * we might be able to optimize this by reusing the original + * packet's mbufs but that is significantly more complicated. + */ +static int +ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, + u_int hdrsize, u_int ciphdrsize, u_int mtu) +{ + struct ieee80211_frame *wh, *whf; + struct mbuf *m, *prev, *next; + u_int totalhdrsize, fragno, fragsize, off, remainder, payload; + + KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); + KASSERT(m0->m_pkthdr.len > mtu, + ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); + + wh = mtod(m0, struct ieee80211_frame *); + /* NB: mark the first frag; it will be propagated below */ + wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; + totalhdrsize = hdrsize + ciphdrsize; + fragno = 1; + off = mtu - ciphdrsize; + remainder = m0->m_pkthdr.len - off; + prev = m0; + do { + fragsize = totalhdrsize + remainder; + if (fragsize > mtu) + fragsize = mtu; + KASSERT(fragsize < MCLBYTES, + ("fragment size %u too big!", fragsize)); + if (fragsize > MHLEN) + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + goto bad; + /* leave room to prepend any cipher header */ + m_align(m, fragsize - ciphdrsize); + + /* + * Form the header in the fragment. Note that since + * we mark the first fragment with the MORE_FRAG bit + * it automatically is propagated to each fragment; we + * need only clear it on the last fragment (done below). + */ + whf = mtod(m, struct ieee80211_frame *); + memcpy(whf, wh, hdrsize); + *(uint16_t *)&whf->i_seq[0] |= htole16( + (fragno & IEEE80211_SEQ_FRAG_MASK) << + IEEE80211_SEQ_FRAG_SHIFT); + fragno++; + + payload = fragsize - totalhdrsize; + /* NB: destination is known to be contiguous */ + m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize); + m->m_len = hdrsize + payload; + m->m_pkthdr.len = hdrsize + payload; + m->m_flags |= M_FRAG; + + /* chain up the fragment */ + prev->m_nextpkt = m; + prev = m; + + /* deduct fragment just formed */ + remainder -= payload; + off += payload; + } while (remainder != 0); + whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; + + /* strip first mbuf now that everything has been copied */ + m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); + m0->m_flags |= M_FIRSTFRAG | M_FRAG; + + ic->ic_stats.is_tx_fragframes++; + ic->ic_stats.is_tx_frags += fragno-1; + + return 1; +bad: + /* reclaim fragments but leave original frame for caller to free */ + for (m = m0->m_nextpkt; m != NULL; m = next) { + next = m->m_nextpkt; + m->m_nextpkt = NULL; /* XXX paranoid */ + m_freem(m); + } + m0->m_nextpkt = NULL; + return 0; +} + +/* * Add a supported rates element id to a frame. */ -static u_int8_t * -ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) +static uint8_t * +ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) { int nrates; @@ -793,8 +1131,8 @@ ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) /* * Add an extended supported rates element id to a frame. */ -static u_int8_t * -ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) +static uint8_t * +ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) { /* * Add an extended supported rates element if operating in 11g mode. @@ -812,8 +1150,8 @@ ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) /* * Add an ssid elemet to a frame. */ -static u_int8_t * -ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) +static uint8_t * +ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) { *frm++ = IEEE80211_ELEMID_SSID; *frm++ = len; @@ -824,10 +1162,10 @@ ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) /* * Add an erp element to a frame. */ -static u_int8_t * -ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) +static uint8_t * +ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) { - u_int8_t erp; + uint8_t erp; *frm++ = IEEE80211_ELEMID_ERP; *frm++ = 1; @@ -842,8 +1180,8 @@ ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) return frm; } -static u_int8_t * -ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) +static uint8_t * +ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie) { #define WPA_OUI_BYTES 0x00, 0x50, 0xf2 #define ADDSHORT(frm, v) do { \ @@ -855,8 +1193,8 @@ ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) memcpy(frm, sel, 4); \ frm += 4; \ } while (0) - static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; - static const u_int8_t cipher_suite[][4] = { + static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; + static const uint8_t cipher_suite[][4] = { { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ { WPA_OUI_BYTES, WPA_CSE_TKIP }, { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ @@ -864,15 +1202,15 @@ ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ { WPA_OUI_BYTES, WPA_CSE_NULL }, }; - static const u_int8_t wep104_suite[4] = + static const uint8_t wep104_suite[4] = { WPA_OUI_BYTES, WPA_CSE_WEP104 }; - static const u_int8_t key_mgt_unspec[4] = + static const uint8_t key_mgt_unspec[4] = { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; - static const u_int8_t key_mgt_psk[4] = + static const uint8_t key_mgt_psk[4] = { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; - u_int8_t *frm = ie; - u_int8_t *selcnt; + uint8_t *frm = ie; + uint8_t *selcnt; *frm++ = IEEE80211_ELEMID_VENDOR; *frm++ = 0; /* length filled in below */ @@ -928,8 +1266,8 @@ ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) #undef WPA_OUI_BYTES } -static u_int8_t * -ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) +static uint8_t * +ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie) { #define RSN_OUI_BYTES 0x00, 0x0f, 0xac #define ADDSHORT(frm, v) do { \ @@ -941,7 +1279,7 @@ ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) memcpy(frm, sel, 4); \ frm += 4; \ } while (0) - static const u_int8_t cipher_suite[][4] = { + static const uint8_t cipher_suite[][4] = { { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ { RSN_OUI_BYTES, RSN_CSE_TKIP }, { RSN_OUI_BYTES, RSN_CSE_WRAP }, @@ -949,15 +1287,15 @@ ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ { RSN_OUI_BYTES, RSN_CSE_NULL }, }; - static const u_int8_t wep104_suite[4] = + static const uint8_t wep104_suite[4] = { RSN_OUI_BYTES, RSN_CSE_WEP104 }; - static const u_int8_t key_mgt_unspec[4] = + static const uint8_t key_mgt_unspec[4] = { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; - static const u_int8_t key_mgt_psk[4] = + static const uint8_t key_mgt_psk[4] = { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; - u_int8_t *frm = ie; - u_int8_t *selcnt; + uint8_t *frm = ie; + uint8_t *selcnt; *frm++ = IEEE80211_ELEMID_RSN; *frm++ = 0; /* length filled in below */ @@ -1014,8 +1352,8 @@ ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) /* * Add a WPA/RSN element to a frame. */ -static u_int8_t * -ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic) +static uint8_t * +ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic) { KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); @@ -1030,8 +1368,8 @@ ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic) /* * Add a WME information element to a frame. */ -static u_int8_t * -ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme) +static uint8_t * +ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) { static const struct ieee80211_wme_info info = { .wme_id = IEEE80211_ELEMID_VENDOR, @@ -1049,8 +1387,8 @@ ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme) /* * Add a WME parameters element to a frame. */ -static u_int8_t * -ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme) +static uint8_t * +ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) { #define SM(_v, _f) (((_v) << _f##_S) & _f) #define ADDSHORT(frm, v) do { \ @@ -1091,23 +1429,48 @@ ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme) } #undef WME_OUI_BYTES +#define ATH_OUI_BYTES 0x00, 0x03, 0x7f +/* + * Add a WME information element to a frame. + */ +static uint8_t * +ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix) +{ + static const struct ieee80211_ath_ie info = { + .ath_id = IEEE80211_ELEMID_VENDOR, + .ath_len = sizeof(struct ieee80211_ath_ie) - 2, + .ath_oui = { ATH_OUI_BYTES }, + .ath_oui_type = ATH_OUI_TYPE, + .ath_oui_subtype= ATH_OUI_SUBTYPE, + .ath_version = ATH_OUI_VERSION, + }; + struct ieee80211_ath_ie *ath = (struct ieee80211_ath_ie *) frm; + + memcpy(frm, &info, sizeof(info)); + ath->ath_capability = caps; + ath->ath_defkeyix[0] = (defkeyix & 0xff); + ath->ath_defkeyix[1] = ((defkeyix >> 8) & 0xff); + return frm + sizeof(info); +} +#undef ATH_OUI_BYTES + /* * Send a probe request frame with the specified ssid * and any optional information element data. */ int ieee80211_send_probereq(struct ieee80211_node *ni, - const u_int8_t sa[IEEE80211_ADDR_LEN], - const u_int8_t da[IEEE80211_ADDR_LEN], - const u_int8_t bssid[IEEE80211_ADDR_LEN], - const u_int8_t *ssid, size_t ssidlen, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t *ssid, size_t ssidlen, const void *optie, size_t optielen) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; const struct ieee80211_rateset *rs; struct mbuf *m; - u_int8_t *frm; + uint8_t *frm; /* * Hold a reference on the node so it doesn't go away until after @@ -1129,6 +1492,7 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * [tlv] user-specified ie's */ m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) @@ -1149,7 +1513,7 @@ ieee80211_send_probereq(struct ieee80211_node *ni, memcpy(frm, optie, optielen); frm += optielen; } - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); if (m == NULL) @@ -1179,10 +1543,10 @@ ieee80211_send_probereq(struct ieee80211_node *ni, /* * Calculate capability information for mgt frames. */ -static u_int16_t +static uint16_t getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) { - u_int16_t capinfo; + uint16_t capinfo; KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); @@ -1213,9 +1577,9 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, { #define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) struct mbuf *m; - u_int8_t *frm; - u_int16_t capinfo; - int has_challenge, is_shared_key, ret, timer, status; + uint8_t *frm; + uint16_t capinfo; + int has_challenge, is_shared_key, ret, status; KASSERT(ni != NULL, ("null node")); @@ -1231,7 +1595,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); - timer = 0; switch (type) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: /* @@ -1247,11 +1610,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] extended supported rates * [tlv] WPA * [tlv] WME (optional) + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * [tlv] Atheros capabilities */ m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), 8 - + sizeof(u_int16_t) - + sizeof(u_int16_t) + + sizeof(uint16_t) + + sizeof(uint16_t) + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 7 /* max(7,3) */ @@ -1262,23 +1631,27 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + (ic->ic_flags & IEEE80211_F_WPA ? 2*sizeof(struct ieee80211_ie_wpa) : 0) + sizeof(struct ieee80211_wme_param) + /* XXX check for cluster requirement */ + + 2*sizeof(struct ieee80211_ie_htcap) + 4 + + 2*sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_ath_ie) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); memset(frm, 0, 8); /* timestamp should be filled later */ frm += 8; - *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); + *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval); frm += 2; capinfo = getcapinfo(ic, ic->ic_curchan); - *(u_int16_t *)frm = htole16(capinfo); + *(uint16_t *)frm = htole16(capinfo); frm += 2; frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); - if (ic->ic_phytype == IEEE80211_T_FH) { + if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) { *frm++ = IEEE80211_ELEMID_FHPARMS; *frm++ = 5; *frm++ = ni->ni_fhdwell & 0x00ff; @@ -1301,12 +1674,23 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, } if (ic->ic_flags & IEEE80211_F_WPA) frm = ieee80211_add_wpa(frm, ic); - if (ic->ic_curmode == IEEE80211_MODE_11G) + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) frm = ieee80211_add_erp(frm, ic); frm = ieee80211_add_xrates(frm, &ni->ni_rates); if (ic->ic_flags & IEEE80211_F_WME) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { + frm = ieee80211_add_htcap(frm, ni); + frm = ieee80211_add_htinfo(frm, ni); + if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + frm = ieee80211_add_htcap_vendor(frm, ni); + frm = ieee80211_add_htinfo_vendor(frm, ni); + } + } + if (ni->ni_ath_ie != NULL) + frm = ieee80211_add_ath(frm, ni->ni_ath_flags, + ni->ni_ath_defkeyix); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_AUTH: @@ -1329,27 +1713,28 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); m = ieee80211_getmgtframe(&frm, - 3 * sizeof(u_int16_t) + ic->ic_headroom + sizeof(struct ieee80211_frame), + 3 * sizeof(uint16_t) + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? - sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0) + sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - ((u_int16_t *)frm)[0] = + ((uint16_t *)frm)[0] = (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) : htole16(IEEE80211_AUTH_ALG_OPEN); - ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ - ((u_int16_t *)frm)[2] = htole16(status);/* status */ + ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ + ((uint16_t *)frm)[2] = htole16(status);/* status */ if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { - ((u_int16_t *)frm)[3] = + ((uint16_t *)frm)[3] = htole16((IEEE80211_CHALLENGE_LEN << 8) | IEEE80211_ELEMID_CHALLENGE); - memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, + memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, IEEE80211_CHALLENGE_LEN); m->m_pkthdr.len = m->m_len = - 4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN; + 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, "[%s] request encrypt frame (%s)\n", @@ -1357,7 +1742,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, m->m_flags |= M_LINK0; /* WEP-encrypt, please */ } } else - m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t); + m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); /* XXX not right for shared key */ if (status == IEEE80211_STATUS_SUCCESS) @@ -1366,18 +1751,21 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, IEEE80211_NODE_STAT(ni, tx_auth_fail); if (ic->ic_opmode == IEEE80211_M_STA) - timer = IEEE80211_TRANS_WAIT; + ieee80211_add_callback(m, ieee80211_tx_mgt_cb, + (void *) ic->ic_state); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, "[%s] send station deauthenticate (reason %d)\n", ether_sprintf(ni->ni_macaddr), arg); - m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - *(u_int16_t *)frm = htole16(arg); /* reason */ - m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); IEEE80211_NODE_STAT(ni, tx_deauth); IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); @@ -1396,16 +1784,22 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] supported rates * [tlv] extended supported rates * [tlv] WME + * [tlv] HT capabilities + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Atheros capabilities (if negotiated) * [tlv] user-specified ie's */ m = ieee80211_getmgtframe(&frm, - sizeof(u_int16_t) - + sizeof(u_int16_t) + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) + + sizeof(uint16_t) + IEEE80211_ADDR_LEN + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_wme_info) + + 2*sizeof(struct ieee80211_ie_htcap) + 4 + + sizeof(struct ieee80211_ath_ie) + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) ); if (m == NULL) @@ -1423,13 +1817,16 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; - if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) && + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; - *(u_int16_t *)frm = htole16(capinfo); + *(uint16_t *)frm = htole16(capinfo); frm += 2; - *(u_int16_t *)frm = htole16(ic->ic_lintval); + KASSERT(ic->ic_bss->ni_intval != 0, + ("beacon interval is zero!")); + *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, + ic->ic_bss->ni_intval)); frm += 2; if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { @@ -1442,46 +1839,66 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_xrates(frm, &ni->ni_rates); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { + frm = ieee80211_add_htcap(frm, ni); + if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) + frm = ieee80211_add_htcap_vendor(frm, ni); + } + if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + frm = ieee80211_add_ath(frm, + IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), + (ic->ic_flags & IEEE80211_F_WPA) == 0 && + ni->ni_authmode != IEEE80211_AUTH_8021X && + ic->ic_def_txkey != IEEE80211_KEYIX_NONE ? + ic->ic_def_txkey : 0x7fff); if (ic->ic_opt_ie != NULL) { memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); frm += ic->ic_opt_ie_len; } - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); - timer = IEEE80211_TRANS_WAIT; + ieee80211_add_callback(m, ieee80211_tx_mgt_cb, + (void *) ic->ic_state); break; case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: /* - * asreq frame format + * asresp frame format * [2] capability information * [2] status * [2] association ID * [tlv] supported rates * [tlv] extended supported rates * [tlv] WME (if enabled and STA enabled) + * [tlv] HT capabilities (standard or vendor OUI) + * [tlv] HT information (standard or vendor OUI) + * [tlv] Atheros capabilities (if enabled and STA enabled) */ m = ieee80211_getmgtframe(&frm, - sizeof(u_int16_t) - + sizeof(u_int16_t) - + sizeof(u_int16_t) + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) + + sizeof(uint16_t) + + sizeof(uint16_t) + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_wme_param) + + sizeof(struct ieee80211_ie_htcap) + 4 + + sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_ath_ie) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); capinfo = getcapinfo(ic, ic->ic_curchan); - *(u_int16_t *)frm = htole16(capinfo); + *(uint16_t *)frm = htole16(capinfo); frm += 2; - *(u_int16_t *)frm = htole16(arg); /* status */ + *(uint16_t *)frm = htole16(arg); /* status */ frm += 2; if (arg == IEEE80211_STATUS_SUCCESS) { - *(u_int16_t *)frm = htole16(ni->ni_associd); + *(uint16_t *)frm = htole16(ni->ni_associd); IEEE80211_NODE_STAT(ni, tx_assoc); } else IEEE80211_NODE_STAT(ni, tx_assoc_fail); @@ -1491,18 +1908,34 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_xrates(frm, &ni->ni_rates); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { + /* NB: respond according to what we received */ + if (ni->ni_flags & IEEE80211_NODE_HTCOMPAT) { + frm = ieee80211_add_htcap_vendor(frm, ni); + frm = ieee80211_add_htinfo_vendor(frm, ni); + } else { + frm = ieee80211_add_htcap(frm, ni); + frm = ieee80211_add_htinfo(frm, ni); + } + } + if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + frm = ieee80211_add_ath(frm, + IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), + ni->ni_ath_defkeyix); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] send station disassociate (reason %d)\n", ether_sprintf(ni->ni_macaddr), arg); - m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - *(u_int16_t *)frm = htole16(arg); /* reason */ - m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); IEEE80211_NODE_STAT(ni, tx_disassoc); IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); @@ -1515,15 +1948,57 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } - ret = ieee80211_mgmt_output(ic, ni, m, type, timer); - if (ret != 0) { + + ret = ieee80211_mgmt_output(ic, ni, m, type); + if (ret != 0) + goto bad; + return 0; bad: - ieee80211_free_node(ni); - } + ieee80211_free_node(ni); return ret; #undef senderr } +static void +ieee80211_tx_mgt_timeout(void *arg) +{ + struct ieee80211_node *ni = arg; + struct ieee80211com *ic = ni->ni_ic; + + if (ic->ic_state != IEEE80211_S_INIT && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* + * NB: it's safe to specify a timeout as the reason here; + * it'll only be used in the right state. + */ + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_TIMEOUT); + } +} + +static void +ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) +{ + struct ieee80211com *ic = ni->ni_ic; + enum ieee80211_state ostate = (enum ieee80211_state) arg; + + /* + * Frame transmit completed; arrange timer callback. If + * transmit was successfuly we wait for response. Otherwise + * we arrange an immediate callback instead of doing the + * callback directly since we don't know what state the driver + * is in (e.g. what locks it is holding). This work should + * not be too time-critical and not happen too often so the + * added overhead is acceptable. + * + * XXX what happens if !acked but response shows up before callback? + */ + if (ic->ic_state == ostate) + callout_reset(&ic->ic_mgtsend, + status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, + ieee80211_tx_mgt_timeout, ni); +} + /* * Allocate a beacon frame and fillin the appropriate bits. */ @@ -1535,8 +2010,8 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_frame *wh; struct mbuf *m; int pktlen; - u_int8_t *frm, *efrm; - u_int16_t capinfo; + uint8_t *frm; + uint16_t capinfo; struct ieee80211_rateset *rs; /* @@ -1548,29 +2023,39 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] supported rates * [3] parameter set (DS) * [tlv] parameter set (IBSS/TIM) + * [tlv] country code * [tlv] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] WME parameters * [tlv] WPA/RSN parameters + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) * XXX Vendor-specific OIDs (e.g. Atheros) * NB: we allocate the max space required for the TIM bitmap. */ rs = &ni->ni_rates; pktlen = 8 /* time stamp */ - + sizeof(u_int16_t) /* beacon interval */ - + sizeof(u_int16_t) /* capabilities */ + + sizeof(uint16_t) /* beacon interval */ + + sizeof(uint16_t) /* capabilities */ + 2 + ni->ni_esslen /* ssid */ + 2 + IEEE80211_RATE_SIZE /* supported rates */ + 2 + 1 /* DS parameters */ + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ + + sizeof(struct ieee80211_country_ie) /* country code */ + 2 + 1 /* ERP */ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ sizeof(struct ieee80211_wme_param) : 0) + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 2*sizeof(struct ieee80211_ie_wpa) : 0) + /* XXX conditional? */ + + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ ; - m = ieee80211_getmgtframe(&frm, pktlen); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); if (m == NULL) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, "%s: cannot get buf; size %u\n", __func__, pktlen); @@ -1580,11 +2065,11 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ frm += 8; - *(u_int16_t *)frm = htole16(ni->ni_intval); + *(uint16_t *)frm = htole16(ni->ni_intval); frm += 2; capinfo = getcapinfo(ic, ni->ni_chan); - bo->bo_caps = (u_int16_t *)frm; - *(u_int16_t *)frm = htole16(capinfo); + bo->bo_caps = (uint16_t *)frm; + *(uint16_t *)frm = htole16(capinfo); frm += 2; *frm++ = IEEE80211_ELEMID_SSID; if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { @@ -1594,10 +2079,10 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, } else *frm++ = 0; frm = ieee80211_add_rates(frm, rs); - if (ic->ic_curmode != IEEE80211_MODE_FH) { + if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; - *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); } bo->bo_tim = frm; if (ic->ic_opmode == IEEE80211_M_IBSS) { @@ -1618,20 +2103,35 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, bo->bo_tim_len = 1; } bo->bo_trailer = frm; + if (ic->ic_flags & IEEE80211_F_DOTH) + frm = ieee80211_add_countryie(frm, ic, + ic->ic_countrycode, ic->ic_location); if (ic->ic_flags & IEEE80211_F_WME) { bo->bo_wme = frm; frm = ieee80211_add_wme_param(frm, &ic->ic_wme); ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; - } + } else + bo->bo_wme = NULL; if (ic->ic_flags & IEEE80211_F_WPA) frm = ieee80211_add_wpa(frm, ic); - if (ic->ic_curmode == IEEE80211_MODE_11G) { + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { bo->bo_erp = frm; frm = ieee80211_add_erp(frm, ic); - } - efrm = ieee80211_add_xrates(frm, rs); - bo->bo_trailer_len = efrm - bo->bo_trailer; - m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *); + } else + bo->bo_erp = NULL; + frm = ieee80211_add_xrates(frm, rs); + if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) { + frm = ieee80211_add_htcap(frm, ni); + bo->bo_htinfo = frm; + frm = ieee80211_add_htinfo(frm, ni); + if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + frm = ieee80211_add_htcap_vendor(frm, ni); + frm = ieee80211_add_htinfo_vendor(frm, ni); + } + } else + bo->bo_htinfo = NULL; + bo->bo_trailer_len = frm - bo->bo_trailer; + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); KASSERT(m != NULL, ("no space for 802.11 header?")); @@ -1639,11 +2139,11 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - *(u_int16_t *)wh->i_dur = 0; + *(uint16_t *)wh->i_dur = 0; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); - *(u_int16_t *)wh->i_seq = 0; + *(uint16_t *)wh->i_seq = 0; return m; } @@ -1656,7 +2156,7 @@ ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) { int len_changed = 0; - u_int16_t capinfo; + uint16_t capinfo; IEEE80211_BEACON_LOCK(ic); /* XXX faster to recalculate entirely or just changes? */ @@ -1704,6 +2204,15 @@ ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, } } + if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) { + struct ieee80211_ie_htinfo *ht = + (struct ieee80211_ie_htinfo *) bo->bo_htinfo; + if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; + else + ht->hi_byte1 &= ~IEEE80211_HTINFO_TXWIDTH_2040; + } + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) bo->bo_tim; @@ -1750,6 +2259,7 @@ ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, bo->bo_trailer += adjust; bo->bo_wme += adjust; bo->bo_erp += adjust; + bo->bo_htinfo += adjust; bo->bo_tim_len = timlen; /* update information element */ @@ -1788,49 +2298,3 @@ ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, return len_changed; } - -/* - * Save an outbound packet for a node in power-save sleep state. - * The new packet is placed on the node's saved queue, and the TIM - * is changed, if necessary. - */ -void -ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m) -{ - int qlen, age; - - IEEE80211_NODE_SAVEQ_LOCK(ni); - if (_IF_QFULL(&ni->ni_savedq)) { - _IF_DROP(&ni->ni_savedq); - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] pwr save q overflow, drops %d (size %d)\n", - ether_sprintf(ni->ni_macaddr), - ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_dumppkts(ic)) - ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1); -#endif - m_freem(m); - return; - } - /* - * Tag the frame with it's expiry time and insert - * it in the queue. The aging interval is 4 times - * the listen interval specified by the station. - * Frames that sit around too long are reclaimed - * using this information. - */ - /* XXX handle overflow? */ - age = ((ni->ni_intval * ic->ic_bintval) << 2) / 1024; /* TU -> secs */ - _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] save frame with age %d, %u now queued\n", - ether_sprintf(ni->ni_macaddr), age, qlen); - - if (qlen == 1) - ic->ic_set_tim(ni, 1); -} diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c new file mode 100644 index 000000000000..2064807814f7 --- /dev/null +++ b/sys/net80211/ieee80211_power.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 power save support. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +static void ieee80211_set_tim(struct ieee80211_node *ni, int set); + +void +ieee80211_power_attach(struct ieee80211com *ic) +{ + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS) { + /* NB: driver should override */ + ic->ic_set_tim = ieee80211_set_tim; + } +} + +void +ieee80211_power_lateattach(struct ieee80211com *ic) +{ + /* + * Allocate these only if needed. Beware that we + * know adhoc mode doesn't support ATIM yet... + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t); + MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len, + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ic->ic_tim_bitmap == NULL) { + printf("%s: no memory for TIM bitmap!\n", __func__); + /* XXX good enough to keep from crashing? */ + ic->ic_tim_len = 0; + } + } +} + +void +ieee80211_power_detach(struct ieee80211com *ic) +{ + if (ic->ic_tim_bitmap != NULL) { + FREE(ic->ic_tim_bitmap, M_DEVBUF); + ic->ic_tim_bitmap = NULL; + } +} + +/* + * Clear any frames queued on a node's power save queue. + * The number of frames that were present is returned. + */ +int +ieee80211_node_saveq_drain(struct ieee80211_node *ni) +{ + int qlen; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); + _IF_DRAIN(&ni->ni_savedq); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + + return qlen; +} + +/* + * Age frames on the power save queue. The aging interval is + * 4 times the listen interval specified by the station. This + * number is factored into the age calculations when the frame + * is placed on the queue. We store ages as time differences + * so we can check and/or adjust only the head of the list. + * If a frame's age exceeds the threshold then discard it. + * The number of frames discarded is returned so the caller + * can check if it needs to adjust the tim. + */ +int +ieee80211_node_saveq_age(struct ieee80211_node *ni) +{ + int discard = 0; + + if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { + struct mbuf *m; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + while (IF_POLL(&ni->ni_savedq, m) != NULL && + M_AGE_GET(m) < IEEE80211_INACT_WAIT) { +IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ + _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); + m_freem(m); + discard++; + } + if (m != NULL) + M_AGE_SUB(m, IEEE80211_INACT_WAIT); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + + IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni, + "discard %u frames for age", discard); + IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); + } + return discard; +} + +/* + * Indicate whether there are frames queued for a station in power-save mode. + */ +static void +ieee80211_set_tim(struct ieee80211_node *ni, int set) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t aid; + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS, + ("operating mode %u", ic->ic_opmode)); + + aid = IEEE80211_AID(ni->ni_associd); + KASSERT(aid < ic->ic_max_aid, + ("bogus aid %u, max %u", aid, ic->ic_max_aid)); + + IEEE80211_BEACON_LOCK(ic); + if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { + if (set) { + setbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending++; + } else { + clrbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending--; + } + ic->ic_flags |= IEEE80211_F_TIMUPDATE; + } + IEEE80211_BEACON_UNLOCK(ic); +} + +/* + * Save an outbound packet for a node in power-save sleep state. + * The new packet is placed on the node's saved queue, and the TIM + * is changed, if necessary. + */ +void +ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211com *ic = ni->ni_ic; + int qlen, age; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + if (_IF_QFULL(&ni->ni_savedq)) { + _IF_DROP(&ni->ni_savedq); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%s] pwr save q overflow, drops %d (size %d)\n", + ether_sprintf(ni->ni_macaddr), + ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumppkts(ic)) + ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1); +#endif + m_freem(m); + return; + } + /* + * Tag the frame with it's expiry time and insert + * it in the queue. The aging interval is 4 times + * the listen interval specified by the station. + * Frames that sit around too long are reclaimed + * using this information. + */ + /* TU -> secs. XXX handle overflow? */ + age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; + _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] save frame with age %d, %u now queued\n", + ether_sprintf(ni->ni_macaddr), age, qlen); + + if (qlen == 1 && ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 1); +} + +/* + * Handle station power-save state change. + */ +void +ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m, *mhead, *mtail; + int mcount; + + if (enable) { + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) + ic->ic_ps_sta++; + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + "power save mode on, %u sta's in ps mode", ic->ic_ps_sta); + return; + } + + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) + ic->ic_ps_sta--; + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + "power save mode off, %u sta's in ps mode", ic->ic_ps_sta); + /* XXX if no stations in ps mode, flush mc frames */ + + /* + * Flush queued unicast frames. + */ + if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); /* just in case */ + return; + } + IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni)); + /* + * Unload the frames from the ps q but don't send them + * to the driver yet. We do this in two stages to minimize + * locking but also because there's no easy way to preserve + * ordering given the existing ifnet access mechanisms. + * XXX could be optimized + */ + IEEE80211_NODE_SAVEQ_LOCK(ni); + mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); + mhead = mtail = NULL; + for (;;) { + _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); + if (m == NULL) + break; + if (mhead == NULL) { + mhead = m; + m->m_nextpkt = NULL; + } else + mtail->m_nextpkt = m; + mtail = m; + } + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + if (mhead != NULL) { + /* XXX need different driver interface */ + /* XXX bypasses q max */ + IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); + } + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); +} + +/* + * Handle power-save state change in station mode. + */ +void +ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable) +{ + struct ieee80211_node *ni = ic->ic_bss; + int qlen; + + if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) + return; + + IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + "sta power save mode %s", enable ? "on" : "off"); + if (!enable) { + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + ieee80211_send_nulldata(ieee80211_ref_node(ni)); + /* + * Flush any queued frames; we can do this immediately + * because we know they'll be queued behind the null + * data frame we send the ap. + * XXX can we use a data frame to take us out of ps? + */ + qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); + if (qlen != 0) { + IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + "flush ps queue, %u packets queued", qlen); + for (;;) { + struct mbuf *m; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + if (m == NULL) + break; + /* XXX need different driver interface */ + /* XXX bypasses q max */ + IF_ENQUEUE(&ic->ic_ifp->if_snd, m); + } + } + } else { + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + ieee80211_send_nulldata(ieee80211_ref_node(ni)); + } +} diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h new file mode 100644 index 000000000000..c8461f6040f5 --- /dev/null +++ b/sys/net80211/ieee80211_power.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_POWER_H_ +#define _NET80211_IEEE80211_POWER_H_ + +struct ieee80211com; + +void ieee80211_power_attach(struct ieee80211com *); +void ieee80211_power_lateattach(struct ieee80211com *); +void ieee80211_power_detach(struct ieee80211com *); + +int ieee80211_node_saveq_drain(struct ieee80211_node *); +int ieee80211_node_saveq_age(struct ieee80211_node *); +void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *); +void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); +void ieee80211_sta_pwrsave(struct ieee80211com *, int enable); + +void ieee80211_power_poll(struct ieee80211com *); +#endif /* _NET80211_IEEE80211_POWER_H_ */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 458c168b43e9..40fef21845fa 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -35,8 +35,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> -#include <sys/systm.h> - +#include <sys/systm.h> + #include <sys/socket.h> #include <net/if.h> @@ -103,6 +103,7 @@ ieee80211_proto_attach(struct ieee80211com *ic) ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; ic->ic_bmiss_max = IEEE80211_BMISS_MAX; callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); + callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE); ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; ic->ic_protmode = IEEE80211_PROT_CTSONLY; ic->ic_roaming = IEEE80211_ROAMING_AUTO; @@ -237,9 +238,9 @@ ieee80211_aclator_get(const char *name) } void -ieee80211_print_essid(const u_int8_t *essid, int len) +ieee80211_print_essid(const uint8_t *essid, int len) { - const u_int8_t *p; + const uint8_t *p; int i; if (len > IEEE80211_NWID_LEN) @@ -262,7 +263,8 @@ ieee80211_print_essid(const u_int8_t *essid, int len) } void -ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) +ieee80211_dump_pkt(struct ieee80211com *ic, + const uint8_t *buf, int len, int rate, int rssi) { const struct ieee80211_frame *wh; int i; @@ -285,7 +287,7 @@ ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: - printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); + printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); @@ -304,12 +306,22 @@ ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } + if (IEEE80211_QOS_HAS_SEQ(wh)) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *)buf; + printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, + qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); + } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - int i; - printf(" WEP [IV"); - for (i = 0; i < IEEE80211_WEP_IVLEN; i++) - printf(" %.02x", buf[sizeof(*wh)+i]); - printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); + int off; + + off = ieee80211_anyhdrspace(ic, wh); + printf(" WEP [IV %.02x %.02x %.02x", + buf[off+0], buf[off+1], buf[off+2]); + if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) + printf(" %.02x %.02x %.02x", + buf[off+4], buf[off+5], buf[off+6]); + printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); } if (rate >= 0) printf(" %dM", rate / 2); @@ -346,17 +358,11 @@ ieee80211_fix_rate(struct ieee80211_node *ni, int i, j, rix, error; int okrate, badrate, fixedrate; const struct ieee80211_rateset *srs; - u_int8_t r; + uint8_t r; - /* - * If the fixed rate check was requested but no - * fixed has been defined then just remove it. - */ - if ((flags & IEEE80211_F_DOFRATE) && - ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) - flags &= ~IEEE80211_F_DOFRATE; error = 0; - okrate = badrate = fixedrate = 0; + okrate = badrate = 0; + fixedrate = IEEE80211_FIXED_RATE_NONE; srs = ieee80211_get_suprates(ic, ni->ni_chan); for (i = 0; i < nrs->rs_nrates; ) { if (flags & IEEE80211_F_DOSORT) { @@ -373,13 +379,11 @@ ieee80211_fix_rate(struct ieee80211_node *ni, } r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; badrate = r; - if (flags & IEEE80211_F_DOFRATE) { - /* - * Check any fixed rate is included. - */ - if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) - fixedrate = r; - } + /* + * Check for fixed rate. + */ + if (r == ic->ic_fixed_rate) + fixedrate = r; /* * Check against supported rates. */ @@ -418,7 +422,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni, i++; } if (okrate == 0 || error != 0 || - ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) + ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate)) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); @@ -440,14 +444,15 @@ ieee80211_reset_erp(struct ieee80211com *ic) * the driver is capable of doing it. */ ieee80211_set_shortslottime(ic, - ic->ic_curmode == IEEE80211_MODE_11A || - (ic->ic_curmode == IEEE80211_MODE_11G && + IEEE80211_IS_CHAN_A(ic->ic_curchan) || + IEEE80211_IS_CHAN_HT(ic->ic_curchan) || + (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && ic->ic_opmode == IEEE80211_M_HOSTAP && (ic->ic_caps & IEEE80211_C_SHSLOT))); /* * Set short preamble and ERP barker-preamble flags. */ - if (ic->ic_curmode == IEEE80211_MODE_11A || + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; @@ -511,14 +516,17 @@ ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) void ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) { - static const struct ieee80211_rateset basic[] = { - { 0 }, /* IEEE80211_MODE_AUTO */ + static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { + { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ - { 0 }, /* IEEE80211_MODE_FH */ + { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ /* IEEE80211_MODE_PUREG (not yet) */ { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ + /* IEEE80211_MODE_11NG (mixed b/g) */ + { 7, { 2, 4, 11, 22, 12, 24, 48 } }, }; int i, j; @@ -536,76 +544,97 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode * WME protocol support. The following parameters come from the spec. */ typedef struct phyParamType { - u_int8_t aifsn; - u_int8_t logcwmin; - u_int8_t logcwmax; - u_int16_t txopLimit; - u_int8_t acm; + uint8_t aifsn; + uint8_t logcwmin; + uint8_t logcwmax; + uint16_t txopLimit; + uint8_t acm; } paramType; static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { - { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ - { 3, 4, 6 }, /* IEEE80211_MODE_11A */ - { 3, 5, 7 }, /* IEEE80211_MODE_11B */ - { 3, 4, 6 }, /* IEEE80211_MODE_11G */ - { 3, 5, 7 }, /* IEEE80211_MODE_FH */ - { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ - { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ + { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ + { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_11B */ + { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ + { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_FH */ + { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ /* XXXcheck*/ + { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ /* XXXcheck*/ }; static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { - { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ - { 7, 4, 10 }, /* IEEE80211_MODE_11A */ - { 7, 5, 10 }, /* IEEE80211_MODE_11B */ - { 7, 4, 10 }, /* IEEE80211_MODE_11G */ - { 7, 5, 10 }, /* IEEE80211_MODE_FH */ - { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ - { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ + { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ + { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ + { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ + { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ + { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ + { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ + { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { - { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ - { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ - { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ - { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ - { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ - { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ - { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ + { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ + { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ + { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ + { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ + { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ + { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { - { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ - { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ - { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ - { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ - { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ - { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ - { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ + { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ + { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ + { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ + { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ + { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { - { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ - { 3, 4, 10 }, /* IEEE80211_MODE_11A */ - { 3, 5, 10 }, /* IEEE80211_MODE_11B */ - { 3, 4, 10 }, /* IEEE80211_MODE_11G */ - { 3, 5, 10 }, /* IEEE80211_MODE_FH */ - { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ - { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ + { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ + { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ + { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ + { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ + { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ + { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { - { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ - { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ - { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ - { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ - { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ - { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ - { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ + { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ + { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ + { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ + { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ + { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ + { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { - { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ - { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ - { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ - { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ - { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ - { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ - { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ + { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ + { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ + { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ + { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ + { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ }; void @@ -614,29 +643,40 @@ ieee80211_wme_initparams(struct ieee80211com *ic) struct ieee80211_wme_state *wme = &ic->ic_wme; const paramType *pPhyParam, *pBssPhyParam; struct wmeParams *wmep; + enum ieee80211_phymode mode; int i; if ((ic->ic_caps & IEEE80211_C_WME) == 0) return; + /* + * Select mode; we can be called early in which case we + * always use auto mode. We know we'll be called when + * entering the RUN state with bsschan setup properly + * so state will eventually get set correctly + */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) + mode = ieee80211_chan2mode(ic->ic_bsschan); + else + mode = IEEE80211_MODE_AUTO; for (i = 0; i < WME_NUM_AC; i++) { switch (i) { case WME_AC_BK: - pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; - pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; + pPhyParam = &phyParamForAC_BK[mode]; + pBssPhyParam = &phyParamForAC_BK[mode]; break; case WME_AC_VI: - pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; - pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; + pPhyParam = &phyParamForAC_VI[mode]; + pBssPhyParam = &bssPhyParamForAC_VI[mode]; break; case WME_AC_VO: - pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; - pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; + pPhyParam = &phyParamForAC_VO[mode]; + pBssPhyParam = &bssPhyParamForAC_VO[mode]; break; case WME_AC_BE: default: - pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; - pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; + pPhyParam = &phyParamForAC_BE[mode]; + pBssPhyParam = &bssPhyParamForAC_BE[mode]; break; } @@ -704,17 +744,21 @@ void ieee80211_wme_updateparams_locked(struct ieee80211com *ic) { static const paramType phyParam[IEEE80211_MODE_MAX] = { - { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ - { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ - { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ - { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ - { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ - { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ - { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ + { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ + { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ + { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ + { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ + { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ + { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ + { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ + { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ + { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ }; struct ieee80211_wme_state *wme = &ic->ic_wme; const struct wmeParams *wmep; struct wmeParams *chanp, *bssp; + enum ieee80211_phymode mode; int i; /* set up the channel access parameters for the physical device */ @@ -735,6 +779,17 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) } /* + * Select mode; we can be called early in which case we + * always use auto mode. We know we'll be called when + * entering the RUN state with bsschan setup properly + * so state will eventually get set correctly + */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) + mode = ieee80211_chan2mode(ic->ic_bsschan); + else + mode = IEEE80211_MODE_AUTO; + + /* * This implements agressive mode as found in certain * vendors' AP's. When there is significant high * priority (VI/VO) traffic in the BSS throttle back BE @@ -750,15 +805,14 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; - chanp->wmep_aifsn = bssp->wmep_aifsn = - phyParam[ic->ic_curmode].aifsn; + chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; chanp->wmep_logcwmin = bssp->wmep_logcwmin = - phyParam[ic->ic_curmode].logcwmin; + phyParam[mode].logcwmin; chanp->wmep_logcwmax = bssp->wmep_logcwmax = - phyParam[ic->ic_curmode].logcwmax; + phyParam[mode].logcwmax; chanp->wmep_txopLimit = bssp->wmep_txopLimit = (ic->ic_flags & IEEE80211_F_BURST) ? - phyParam[ic->ic_curmode].txopLimit : 0; + phyParam[mode].txopLimit : 0; IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ @@ -773,7 +827,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) if (ic->ic_opmode == IEEE80211_M_HOSTAP && ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { - static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { + static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 3, /* IEEE80211_MODE_AUTO */ 3, /* IEEE80211_MODE_11A */ 4, /* IEEE80211_MODE_11B */ @@ -781,12 +835,14 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) 4, /* IEEE80211_MODE_FH */ 3, /* IEEE80211_MODE_TURBO_A */ 3, /* IEEE80211_MODE_TURBO_G */ + 3, /* IEEE80211_MODE_STURBO_A */ + 3, /* IEEE80211_MODE_11NA */ + 3, /* IEEE80211_MODE_11NG */ }; chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; - chanp->wmep_logcwmin = bssp->wmep_logcwmin = - logCwMin[ic->ic_curmode]; + chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s log2(cwmin) %u\n", __func__ , ieee80211_wme_acnames[WME_AC_BE] @@ -823,6 +879,85 @@ ieee80211_wme_updateparams(struct ieee80211com *ic) } } +/* + * Start a device. If this is the first vap running on the + * underlying device then we first bring it up. + */ +int +ieee80211_init(struct ieee80211com *ic, int forcescan) +{ + + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s\n", "start running"); + + /* + * Kick the 802.11 state machine as appropriate. + */ + if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Try to be intelligent about clocking the state + * machine. If we're currently in RUN state then + * we should be able to apply any new state/parameters + * simply by re-associating. Otherwise we need to + * re-scan to select an appropriate ap. + */ + if (ic->ic_state != IEEE80211_S_RUN || forcescan) + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + else + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); + } else { + /* + * For monitor+wds modes there's nothing to do but + * start running. Otherwise, if this is the first + * vap to be brought up, start a scan which may be + * preempted if the station is locked to a particular + * channel. + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR || + ic->ic_opmode == IEEE80211_M_WDS) { + ic->ic_state = IEEE80211_S_INIT; /* XXX*/ + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + } else + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + } + } + return 0; +} + +/* + * Switch between turbo and non-turbo operating modes. + * Use the specified channel flags to locate the new + * channel, update 802.11 state, and then call back into + * the driver to effect the change. + */ +void +ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) +{ + struct ieee80211_channel *chan; + + chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); + if (chan == NULL) { /* XXX should not happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: no channel with freq %u flags 0x%x\n", + __func__, ic->ic_bsschan->ic_freq, newflags); + return; + } + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, + ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], + ieee80211_phymode_name[ieee80211_chan2mode(chan)], + chan->ic_freq, chan->ic_flags); + + ic->ic_bsschan = chan; + ic->ic_prevchan = ic->ic_curchan; + ic->ic_curchan = chan; + ic->ic_set_channel(ic); + /* NB: do not need to reset ERP state 'cuz we're in sta mode */ +} + void ieee80211_beacon_miss(struct ieee80211com *ic) { @@ -831,8 +966,7 @@ ieee80211_beacon_miss(struct ieee80211com *ic) /* XXX check ic_curchan != ic_bsschan? */ return; } - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "%s\n", "beacon miss"); /* @@ -857,7 +991,28 @@ ieee80211_beacon_miss(struct ieee80211com *ic) return; } ic->ic_bmiss_count = 0; - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { + /* + * If we receive a beacon miss interrupt when using + * dynamic turbo, attempt to switch modes before + * reassociating. + */ + if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP)) + ieee80211_dturbo_switch(ic, + ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); + /* + * Try to reassociate before scanning for a new ap. + */ + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); + } else { + /* + * Somebody else is controlling state changes (e.g. + * a user-mode app) don't do anything that would + * confuse them; just drop into scan mode so they'll + * notified of the state change and given control. + */ + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + } } /* @@ -901,6 +1056,34 @@ sta_deauth(void *arg, struct ieee80211_node *ni) IEEE80211_REASON_ASSOC_LEAVE); } +/* + * Handle deauth with reason. We retry only for + * the cases where we might succeed. Otherwise + * we downgrade the ap and scan. + */ +static void +sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason) +{ + switch (reason) { + case IEEE80211_STATUS_TIMEOUT: + case IEEE80211_REASON_ASSOC_EXPIRE: + case IEEE80211_REASON_NOT_AUTHED: + case IEEE80211_REASON_NOT_ASSOCED: + case IEEE80211_REASON_ASSOC_LEAVE: + case IEEE80211_REASON_ASSOC_NOT_AUTHED: + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + default: + ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan(ic, + IEEE80211_SCAN_ACTIVE, + IEEE80211_SCAN_FOREVER, + ic->ic_des_nssid, ic->ic_des_ssid); + break; + } +} + static int ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { @@ -912,6 +1095,9 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); ic->ic_state = nstate; /* state transition */ + callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */ + if (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(ic); /* background scan */ ni = ic->ic_bss; /* NB: no reference held */ if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) callout_stop(&ic->ic_swbmiss); @@ -959,9 +1145,9 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg } if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ - ic->ic_mgt_timer = 0; ieee80211_drain_ifq(&ic->ic_mgtq); ieee80211_reset_bss(ic); + ieee80211_scan_flush(ic); } if (ic->ic_auth->ia_detach != NULL) ic->ic_auth->ia_detach(ic); @@ -969,50 +1155,71 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_INIT: + createibss: if ((ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_AHDEMO) && ic->ic_des_chan != IEEE80211_CHAN_ANYC) { /* - * AP operation and we already have a channel; - * bypass the scan and startup immediately. + * Already have a channel; bypass the + * scan and startup immediately. Because + * of this explicitly sync the scanner state. */ + ieee80211_scan_update(ic); ieee80211_create_ibss(ic, ic->ic_des_chan); } else { - ieee80211_begin_scan(ic, arg); + ieee80211_check_scan(ic, + IEEE80211_SCAN_ACTIVE | + IEEE80211_SCAN_FLUSH, + IEEE80211_SCAN_FOREVER, + ic->ic_des_nssid, ic->ic_des_ssid); } break; case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: /* - * Scan next. If doing an active scan probe - * for the requested ap (if any). + * These can happen either because of a timeout + * on an assoc/auth response or because of a + * change in state that requires a reset. For + * the former we're called with a non-zero arg + * that is the cause for the failure; pass this + * to the scan code so it can update state. + * Otherwise trigger a new scan unless we're in + * manual roaming mode in which case an application + * must issue an explicit scan request. */ - if (ic->ic_flags & IEEE80211_F_ASCAN) - ieee80211_probe_curchan(ic, 0); + if (arg != 0) + ieee80211_scan_assoc_fail(ic, + ic->ic_bss->ni_macaddr, arg); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan(ic, + IEEE80211_SCAN_ACTIVE, + IEEE80211_SCAN_FOREVER, + ic->ic_des_nssid, ic->ic_des_ssid); break; - case IEEE80211_S_RUN: - /* beacon miss */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, - "no recent beacons from %s; rescanning\n", - ether_sprintf(ic->ic_bss->ni_bssid)); - ieee80211_sta_leave(ic, ni); - ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ - /* FALLTHRU */ - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - /* timeout restart scan */ - ni = ieee80211_find_node(&ic->ic_scan, - ic->ic_bss->ni_macaddr); - if (ni != NULL) { - ni->ni_fails++; - ieee80211_unref_node(&ni); + case IEEE80211_S_RUN: /* beacon miss */ + if (ic->ic_opmode == IEEE80211_M_STA) { + ieee80211_sta_leave(ic, ni); + ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan(ic, + IEEE80211_SCAN_ACTIVE, + IEEE80211_SCAN_FOREVER, + ic->ic_des_nssid, + ic->ic_des_ssid); + } else { + ieee80211_iterate_nodes(&ic->ic_sta, + sta_disassoc, ic); + goto createibss; } - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) - ieee80211_begin_scan(ic, arg); break; } break; case IEEE80211_S_AUTH: + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("switch to %s state when operating in mode %u", + ieee80211_state_name[nstate], ic->ic_opmode)); switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: @@ -1021,19 +1228,19 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: - switch (arg) { + switch (arg & 0xff) { case IEEE80211_FC0_SUBTYPE_AUTH: /* ??? */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - /* ignore and retry scan on timeout */ + sta_authretry(ic, ni, arg>>8); break; } break; case IEEE80211_S_RUN: - switch (arg) { + switch (arg & 0xff) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); @@ -1052,22 +1259,26 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg } break; case IEEE80211_S_ASSOC: + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("switch to %s state when operating in mode %u", + ieee80211_state_name[nstate], ic->ic_opmode)); switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: - case IEEE80211_S_ASSOC: IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, "%s: invalid transition\n", __func__); break; case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); break; case IEEE80211_S_RUN: ieee80211_sta_leave(ic, ni); if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + IEEE80211_SEND_MGMT(ic, ni, arg ? + IEEE80211_FC0_SUBTYPE_REASSOC_REQ : + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); } break; } @@ -1078,8 +1289,18 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg } switch (ostate) { case IEEE80211_S_INIT: - if (ic->ic_opmode == IEEE80211_M_MONITOR) + if (ic->ic_opmode == IEEE80211_M_MONITOR || + ic->ic_opmode == IEEE80211_M_WDS || + ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* + * Already have a channel; bypass the + * scan and startup immediately. Because + * of this explicitly sync the scanner state. + */ + ieee80211_scan_update(ic); + ieee80211_create_ibss(ic, ic->ic_curchan); break; + } /* fall thru... */ case IEEE80211_S_AUTH: IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, @@ -1107,10 +1328,12 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } #endif - ic->ic_mgt_timer = 0; - if (ic->ic_opmode == IEEE80211_M_STA) + if (ic->ic_opmode == IEEE80211_M_STA) { + ieee80211_scan_assoc_success(ic, + ni->ni_macaddr); ieee80211_notify_node_join(ic, ni, arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + } if_start(ifp); /* XXX not authorized yet */ break; } @@ -1150,8 +1373,8 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg * Enable inactivity processing. * XXX */ - ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; - ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; + callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, + ieee80211_node_timeout, ic); break; } return 0; diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index f650cf59ea57..b00c8ec83cd4 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -52,32 +52,36 @@ void ieee80211_proto_detach(struct ieee80211com *); struct ieee80211_node; int ieee80211_input(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, int, u_int32_t); + struct ieee80211_node *, int, int, uint32_t); +void ieee80211_deliver_data(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +struct mbuf *ieee80211_decap1(struct mbuf *, int *); int ieee80211_setup_rates(struct ieee80211_node *ni, - const u_int8_t *rates, const u_int8_t *xrates, int flags); -void ieee80211_saveie(u_int8_t **, const u_int8_t *); + const uint8_t *rates, const uint8_t *xrates, int flags); +void ieee80211_saveie(uint8_t **, const uint8_t *); +void ieee80211_saveath(struct ieee80211_node *, uint8_t *); void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, int, int, u_int32_t); + struct ieee80211_node *, int, int, int, uint32_t); +int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *, + struct mbuf *, int type); struct ieee80211_bpf_params; int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); int ieee80211_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); int ieee80211_send_nulldata(struct ieee80211_node *); -int ieee80211_send_probereq(struct ieee80211_node *ni, - const u_int8_t sa[IEEE80211_ADDR_LEN], - const u_int8_t da[IEEE80211_ADDR_LEN], - const u_int8_t bssid[IEEE80211_ADDR_LEN], - const u_int8_t *ssid, size_t ssidlen, - const void *optie, size_t optielen); int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, int, int); +int ieee80211_send_probereq(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t *ssid, size_t ssidlen, + const void *optie, size_t optielen); int ieee80211_classify(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); -void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, - struct mbuf *); void ieee80211_reset_erp(struct ieee80211com *); void ieee80211_set_shortslottime(struct ieee80211com *, int onoff); @@ -101,12 +105,12 @@ ieee80211_hdrsize(const void *data) if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) size += IEEE80211_ADDR_LEN; if (IEEE80211_QOS_HAS_SEQ(wh)) - size += sizeof(u_int16_t); + size += sizeof(uint16_t); return size; } /* - * Return the size of the 802.11 header; handles any type of frame. + * Like ieee80211_hdrsize, but handles any type of frame. */ static __inline int ieee80211_anyhdrsize(const void *data) @@ -118,6 +122,8 @@ ieee80211_anyhdrsize(const void *data) case IEEE80211_FC0_SUBTYPE_CTS: case IEEE80211_FC0_SUBTYPE_ACK: return sizeof(struct ieee80211_frame_ack); + case IEEE80211_FC0_SUBTYPE_BAR: + return sizeof(struct ieee80211_frame_bar); } return sizeof(struct ieee80211_frame_min); } else @@ -154,11 +160,11 @@ struct ieee80211_aclator { int (*iac_attach)(struct ieee80211com *); void (*iac_detach)(struct ieee80211com *); int (*iac_check)(struct ieee80211com *, - const u_int8_t mac[IEEE80211_ADDR_LEN]); + const uint8_t mac[IEEE80211_ADDR_LEN]); int (*iac_add)(struct ieee80211com *, - const u_int8_t mac[IEEE80211_ADDR_LEN]); + const uint8_t mac[IEEE80211_ADDR_LEN]); int (*iac_remove)(struct ieee80211com *, - const u_int8_t mac[IEEE80211_ADDR_LEN]); + const uint8_t mac[IEEE80211_ADDR_LEN]); int (*iac_flush)(struct ieee80211com *); int (*iac_setpolicy)(struct ieee80211com *, int); int (*iac_getpolicy)(struct ieee80211com *); @@ -174,7 +180,8 @@ const struct ieee80211_aclator *ieee80211_aclator_get(const char *name); #define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ #define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ #define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ -#define IEEE80211_F_JOIN 0x00000010 /* sta joining our bss */ +#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */ +#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */ int ieee80211_fix_rate(struct ieee80211_node *, struct ieee80211_rateset *, int); @@ -182,18 +189,18 @@ int ieee80211_fix_rate(struct ieee80211_node *, * WME/WMM support. */ struct wmeParams { - u_int8_t wmep_acm; - u_int8_t wmep_aifsn; - u_int8_t wmep_logcwmin; /* log2(cwmin) */ - u_int8_t wmep_logcwmax; /* log2(cwmax) */ - u_int8_t wmep_txopLimit; - u_int8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */ + uint8_t wmep_acm; + uint8_t wmep_aifsn; + uint8_t wmep_logcwmin; /* log2(cwmin) */ + uint8_t wmep_logcwmax; /* log2(cwmax) */ + uint8_t wmep_txopLimit; + uint8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */ }; #define IEEE80211_TXOP_TO_US(_txop) ((_txop)<<5) #define IEEE80211_US_TO_TXOP(_us) ((_us)>>5) struct chanAccParams { - u_int8_t cap_info; /* version of the current set */ + uint8_t cap_info; /* version of the current set */ struct wmeParams cap_wmeParams[WME_NUM_AC]; }; @@ -219,9 +226,12 @@ void ieee80211_wme_updateparams_locked(struct ieee80211com *); #define ieee80211_new_state(_ic, _nstate, _arg) \ (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) +int ieee80211_init(struct ieee80211com *, int forcescan); +void ieee80211_dturbo_switch(struct ieee80211com *, int newflags); void ieee80211_beacon_miss(struct ieee80211com *); -void ieee80211_print_essid(const u_int8_t *, int); -void ieee80211_dump_pkt(const u_int8_t *, int, int, int); +void ieee80211_print_essid(const uint8_t *, int); +void ieee80211_dump_pkt(struct ieee80211com *, + const uint8_t *, int, int, int); extern const char *ieee80211_opmode_name[]; extern const char *ieee80211_state_name[IEEE80211_S_MAX]; @@ -233,13 +243,14 @@ extern const char *ieee80211_wme_acnames[]; * can update the frame later w/ minimal overhead. */ struct ieee80211_beacon_offsets { - u_int16_t *bo_caps; /* capabilities */ - u_int8_t *bo_tim; /* start of atim/dtim */ - u_int8_t *bo_wme; /* start of WME parameters */ - u_int8_t *bo_trailer; /* start of fixed-size trailer */ - u_int16_t bo_tim_len; /* atim/dtim length in bytes */ - u_int16_t bo_trailer_len; /* trailer length in bytes */ - u_int8_t *bo_erp; /* start of ERP element */ + uint16_t *bo_caps; /* capabilities */ + uint8_t *bo_tim; /* start of atim/dtim */ + uint8_t *bo_wme; /* start of WME parameters */ + uint8_t *bo_trailer; /* start of fixed-size trailer */ + uint16_t bo_tim_len; /* atim/dtim length in bytes */ + uint16_t bo_trailer_len; /* trailer length in bytes */ + uint8_t *bo_erp; /* start of ERP element */ + uint8_t *bo_htinfo; /* start of HT info element */ }; struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *, struct ieee80211_node *, struct ieee80211_beacon_offsets *); diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h index fea0bbe80698..a52f6a3ef69e 100644 --- a/sys/net80211/ieee80211_radiotap.h +++ b/sys/net80211/ieee80211_radiotap.h @@ -60,18 +60,18 @@ * Note well: all radiotap fields are little-endian. */ struct ieee80211_radiotap_header { - u_int8_t it_version; /* Version 0. Only increases + uint8_t it_version; /* Version 0. Only increases * for drastic changes, * introduction of compatible * new fields does not count. */ - u_int8_t it_pad; - u_int16_t it_len; /* length of the whole + uint8_t it_pad; + uint16_t it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - u_int32_t it_present; /* A bitmap telling which + uint32_t it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. @@ -84,24 +84,25 @@ struct ieee80211_radiotap_header { * Name Data type Units * ---- --------- ----- * - * IEEE80211_RADIOTAP_TSFT u_int64_t microseconds + * IEEE80211_RADIOTAP_TSFT uint64_t microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * - * IEEE80211_RADIOTAP_CHANNEL 2 x u_int16_t MHz, bitmap + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * - * IEEE80211_RADIOTAP_FHSS u_int16_t see below + * IEEE80211_RADIOTAP_FHSS uint16_t see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * - * IEEE80211_RADIOTAP_RATE u_int8_t 500kb/s + * IEEE80211_RADIOTAP_RATE uint8_t 500kb/s or index * - * Tx/Rx data rate + * Tx/Rx data rate. If bit 0x80 is set then it represents an + * an MCS index and not an IEEE rate. * * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from * one milliwatt (dBm) @@ -115,30 +116,30 @@ struct ieee80211_radiotap_header { * RF noise power at the antenna, decibel difference from one * milliwatt. * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL u_int8_t decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * - * IEEE80211_RADIOTAP_DB_ANTNOISE u_int8_t decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * - * IEEE80211_RADIOTAP_LOCK_QUALITY u_int16_t unitless + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * - * IEEE80211_RADIOTAP_TX_ATTENUATION u_int16_t unitless + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t decibels (dB) + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically @@ -151,15 +152,26 @@ struct ieee80211_radiotap_header { * reference). This is the absolute power level measured at * the antenna port. * - * IEEE80211_RADIOTAP_FLAGS u_int8_t bitmap + * IEEE80211_RADIOTAP_FLAGS uint8_t bitmap * * Properties of transmitted and received frames. See flags * defined below. * - * IEEE80211_RADIOTAP_ANTENNA u_int8_t antenna index + * IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_XCHANNEL uint32_t bitmap + * uint16_t MHz + * uint8_t channel number + * int8_t .5 dBm + * + * Extended channel specification: flags (see below) followed by + * frequency in MHz, the corresponding IEEE channel number, and + * finally the maximum regulatory transmit power cap in .5 dBm + * units. This property supersedes IEEE80211_RADIOTAP_CHANNEL + * and only one of the two should be present. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -176,19 +188,27 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_XCHANNEL = 14, IEEE80211_RADIOTAP_EXT = 31, }; #ifndef _KERNEL /* Channel flags. */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_CHAN_TURBO 0x00010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x00020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x00040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x00080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x00100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x00200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x00400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x00800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_CHAN_GSM 0x01000 /* 900 MHz spectrum channel */ +#define IEEE80211_CHAN_STURBO 0x02000 /* 11a static turbo channel only */ +#define IEEE80211_CHAN_HALF 0x04000 /* Half rate channel */ +#define IEEE80211_CHAN_QUARTER 0x08000 /* Quarter rate channel */ +#define IEEE80211_CHAN_HT20 0x10000 /* HT 20 channel */ +#define IEEE80211_CHAN_HT40U 0x20000 /* HT 40 channel w/ ext above */ +#define IEEE80211_CHAN_HT40D 0x40000 /* HT 40 channel w/ ext below */ #endif /* !_KERNEL */ /* For IEEE80211_RADIOTAP_FLAGS */ @@ -211,5 +231,6 @@ enum ieee80211_radiotap_type { * (to 32-bit boundary) */ #define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */ +#define IEEE80211_RADIOTAP_F_SHORTGI 0x80 /* HT short GI */ #endif /* !_NET80211_IEEE80211_RADIOTAP_H_ */ diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c new file mode 100644 index 000000000000..257884f60a66 --- /dev/null +++ b/sys/net80211/ieee80211_regdomain.c @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 regdomain support. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> + +void +ieee80211_regdomain_attach(struct ieee80211com *ic) +{ + ic->ic_regdomain = 0; /* XXX */ + ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */ + ic->ic_location = 1+2; /* both */ +} + +void +ieee80211_regdomain_detach(struct ieee80211com *ic) +{ +} + +static void +addchan(struct ieee80211com *ic, int ieee, int flags) +{ + struct ieee80211_channel *c; + + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_freq = ieee80211_ieee2mhz(ieee, flags); + c->ic_ieee = ieee; + c->ic_flags = flags; +} + +/* + * Setup the channel list for the specified regulatory domain, + * country code, and operating modes. This interface is used + * when a driver does not obtain the channel list from another + * source (such as firmware). + */ +void +ieee80211_init_channels(struct ieee80211com *ic, + int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm) +{ + int i; + + /* XXX just do something for now */ + ic->ic_nchans = 0; + if (isset(&bands, IEEE80211_MODE_11B) || + isset(&bands, IEEE80211_MODE_11G)) { + for (i = 1; i <= (ecm ? 14 : 11); i++) { + if (isset(&bands, IEEE80211_MODE_11B)) + addchan(ic, i, IEEE80211_CHAN_B); + if (isset(&bands, IEEE80211_MODE_11G)) + addchan(ic, i, IEEE80211_CHAN_G); + } + } + if (isset(&bands, IEEE80211_MODE_11A)) { + for (i = 36; i <= 64; i += 4) + addchan(ic, i, IEEE80211_CHAN_A); + for (i = 100; i <= 140; i += 4) + addchan(ic, i, IEEE80211_CHAN_A); + for (i = 149; i <= 161; i += 4) + addchan(ic, i, IEEE80211_CHAN_A); + } + ic->ic_regdomain = rd; + ic->ic_countrycode = cc; + ic->ic_location = outdoor; +} + +/* + * Add Country Information IE. + */ +uint8_t * +ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic, + enum ISOCountryCode cc, int location) +{ +#define CHAN_UNINTERESTING \ + (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \ + IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER) + /* XXX what about auto? */ + /* flag set of channels to be excluded */ + static const int skipflags[IEEE80211_MODE_MAX] = { + CHAN_UNINTERESTING, /* MODE_AUTO */ + CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11A */ + CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11B */ + CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11G */ + CHAN_UNINTERESTING | IEEE80211_CHAN_OFDM | /* MODE_FH */ + IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN, + CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_TURBO_A */ + CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_TURBO_G */ + CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_STURBO_A */ + CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */ + CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */ + }; + struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm; + const char *iso_name; + uint8_t nextchan, chans[IEEE80211_CHAN_BYTES]; + int i, skip; + + ie->ie = IEEE80211_ELEMID_COUNTRY; + iso_name = ieee80211_cctoiso(cc); + if (iso_name == NULL) { + if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc); + iso_name = " "; + } + ie->cc[0] = iso_name[0]; + ie->cc[1] = iso_name[1]; + /* + * Indoor/Outdoor portion of country string. + * NB: this is not quite right, since we should have one of: + * 'I' indoor only + * 'O' outdoor only + * ' ' all enviroments + */ + ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O'); + + /* + * Run-length encoded channel+max tx power info. + */ + frm = (uint8_t *)&ie->band[0]; + nextchan = 0; /* NB: impossible channel # */ + memset(chans, 0, sizeof(chans)); + skip = skipflags[ic->ic_curmode]; + for (i = 0; i < ic->ic_nchans; i++) { + const struct ieee80211_channel *c = &ic->ic_channels[i]; + + if (isset(chans, c->ic_ieee)) /* suppress dup's */ + continue; + if ((c->ic_flags & skip) == 0) /* skip band, etc. */ + continue; + setbit(chans, c->ic_ieee); + if (c->ic_ieee != nextchan || + c->ic_maxregpower != frm[-1]) { /* new run */ + /* XXX max of 83 runs */ + frm[0] = c->ic_ieee; /* starting channel # */ + frm[1] = 1; /* # channels in run */ + frm[2] = c->ic_maxregpower; /* tx power cap */ + frm += 3; + nextchan = c->ic_ieee + 1; /* overflow? */ + } else { /* extend run */ + frm[-2]++; + nextchan++; + } + } + ie->len = frm - ie->cc; + if (ie->len & 1) /* pad to multiple of 2 */ + ie->len++; + return frm; +#undef CHAN_UNINTERESTING +} + +/* + * Country Code Table for code-to-string conversion. + */ +static const struct { + enum ISOCountryCode iso_code; + const char* iso_name; +} country_strings[] = { + { CTRY_DEBUG, "DB" }, /* NB: nonstandard */ + { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */ + { CTRY_ALBANIA, "AL" }, + { CTRY_ALGERIA, "DZ" }, + { CTRY_ARGENTINA, "AR" }, + { CTRY_ARMENIA, "AM" }, + { CTRY_AUSTRALIA, "AU" }, + { CTRY_AUSTRIA, "AT" }, + { CTRY_AZERBAIJAN, "AZ" }, + { CTRY_BAHRAIN, "BH" }, + { CTRY_BELARUS, "BY" }, + { CTRY_BELGIUM, "BE" }, + { CTRY_BELIZE, "BZ" }, + { CTRY_BOLIVIA, "BO" }, + { CTRY_BRAZIL, "BR" }, + { CTRY_BRUNEI_DARUSSALAM, "BN" }, + { CTRY_BULGARIA, "BG" }, + { CTRY_CANADA, "CA" }, + { CTRY_CHILE, "CL" }, + { CTRY_CHINA, "CN" }, + { CTRY_COLOMBIA, "CO" }, + { CTRY_COSTA_RICA, "CR" }, + { CTRY_CROATIA, "HR" }, + { CTRY_CYPRUS, "CY" }, + { CTRY_CZECH, "CZ" }, + { CTRY_DENMARK, "DK" }, + { CTRY_DOMINICAN_REPUBLIC, "DO" }, + { CTRY_ECUADOR, "EC" }, + { CTRY_EGYPT, "EG" }, + { CTRY_EL_SALVADOR, "SV" }, + { CTRY_ESTONIA, "EE" }, + { CTRY_FINLAND, "FI" }, + { CTRY_FRANCE, "FR" }, + { CTRY_FRANCE2, "F2" }, + { CTRY_GEORGIA, "GE" }, + { CTRY_GERMANY, "DE" }, + { CTRY_GREECE, "GR" }, + { CTRY_GUATEMALA, "GT" }, + { CTRY_HONDURAS, "HN" }, + { CTRY_HONG_KONG, "HK" }, + { CTRY_HUNGARY, "HU" }, + { CTRY_ICELAND, "IS" }, + { CTRY_INDIA, "IN" }, + { CTRY_INDONESIA, "ID" }, + { CTRY_IRAN, "IR" }, + { CTRY_IRELAND, "IE" }, + { CTRY_ISRAEL, "IL" }, + { CTRY_ITALY, "IT" }, + { CTRY_JAMAICA, "JM" }, + { CTRY_JAPAN, "JP" }, + { CTRY_JAPAN1, "J1" }, + { CTRY_JAPAN2, "J2" }, + { CTRY_JAPAN3, "J3" }, + { CTRY_JAPAN4, "J4" }, + { CTRY_JAPAN5, "J5" }, + { CTRY_JORDAN, "JO" }, + { CTRY_KAZAKHSTAN, "KZ" }, + { CTRY_KOREA_NORTH, "KP" }, + { CTRY_KOREA_ROC, "KR" }, + { CTRY_KOREA_ROC2, "K2" }, + { CTRY_KUWAIT, "KW" }, + { CTRY_LATVIA, "LV" }, + { CTRY_LEBANON, "LB" }, + { CTRY_LIECHTENSTEIN, "LI" }, + { CTRY_LITHUANIA, "LT" }, + { CTRY_LUXEMBOURG, "LU" }, + { CTRY_MACAU, "MO" }, + { CTRY_MACEDONIA, "MK" }, + { CTRY_MALAYSIA, "MY" }, + { CTRY_MEXICO, "MX" }, + { CTRY_MONACO, "MC" }, + { CTRY_MOROCCO, "MA" }, + { CTRY_NETHERLANDS, "NL" }, + { CTRY_NEW_ZEALAND, "NZ" }, + { CTRY_NORWAY, "NO" }, + { CTRY_OMAN, "OM" }, + { CTRY_PAKISTAN, "PK" }, + { CTRY_PANAMA, "PA" }, + { CTRY_PERU, "PE" }, + { CTRY_PHILIPPINES, "PH" }, + { CTRY_POLAND, "PL" }, + { CTRY_PORTUGAL, "PT" }, + { CTRY_PUERTO_RICO, "PR" }, + { CTRY_QATAR, "QA" }, + { CTRY_ROMANIA, "RO" }, + { CTRY_RUSSIA, "RU" }, + { CTRY_SAUDI_ARABIA, "SA" }, + { CTRY_SINGAPORE, "SG" }, + { CTRY_SLOVAKIA, "SK" }, + { CTRY_SLOVENIA, "SI" }, + { CTRY_SOUTH_AFRICA, "ZA" }, + { CTRY_SPAIN, "ES" }, + { CTRY_SWEDEN, "SE" }, + { CTRY_SWITZERLAND, "CH" }, + { CTRY_SYRIA, "SY" }, + { CTRY_TAIWAN, "TW" }, + { CTRY_THAILAND, "TH" }, + { CTRY_TRINIDAD_Y_TOBAGO, "TT" }, + { CTRY_TUNISIA, "TN" }, + { CTRY_TURKEY, "TR" }, + { CTRY_UKRAINE, "UA" }, + { CTRY_UAE, "AE" }, + { CTRY_UNITED_KINGDOM, "GB" }, + { CTRY_UNITED_STATES, "US" }, + { CTRY_URUGUAY, "UY" }, + { CTRY_UZBEKISTAN, "UZ" }, + { CTRY_VENEZUELA, "VE" }, + { CTRY_VIET_NAM, "VN" }, + { CTRY_YEMEN, "YE" }, + { CTRY_ZIMBABWE, "ZW" } +}; + +const char * +ieee80211_cctoiso(enum ISOCountryCode cc) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(country_strings); i++) { + if (country_strings[i].iso_code == cc) + return country_strings[i].iso_name; + } + return NULL; +#undef N +} + +int +ieee80211_isotocc(const char iso[2]) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(country_strings); i++) { + if (country_strings[i].iso_name[0] == iso[0] && + country_strings[i].iso_name[1] == iso[1]) + return country_strings[i].iso_code; + } + return -1; +#undef N +} diff --git a/sys/net80211/ieee80211_regdomain.h b/sys/net80211/ieee80211_regdomain.h new file mode 100644 index 000000000000..9c1345e0e7ca --- /dev/null +++ b/sys/net80211/ieee80211_regdomain.h @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_REGDOMAIN_H_ +#define _NET80211_IEEE80211_REGDOMAIN_H_ + +/* + * 802.11 regulatory domain definitions. + */ + +/* + * ISO 3166 Country/Region Codes + * http://ftp.ics.uci.edu/pub/ietf/http/related/iso3166.txt + */ +enum ISOCountryCode { + CTRY_AFGHANISTAN = 4, + CTRY_ALBANIA = 8, /* Albania */ + CTRY_ALGERIA = 12, /* Algeria */ + CTRY_AMERICAN_SAMOA = 16, + CTRY_ANDORRA = 20, + CTRY_ANGOLA = 24, + CTRY_ANGUILLA = 660, + /* XXX correct remainder */ + CTRY_ARGENTINA = 32, /* Argentina */ + CTRY_ARMENIA = 51, /* Armenia */ + CTRY_AUSTRALIA = 36, /* Australia */ + CTRY_AUSTRIA = 40, /* Austria */ + CTRY_AZERBAIJAN = 31, /* Azerbaijan */ + CTRY_BAHRAIN = 48, /* Bahrain */ + CTRY_BELARUS = 112, /* Belarus */ + CTRY_BELGIUM = 56, /* Belgium */ + CTRY_BELIZE = 84, /* Belize */ + CTRY_BOLIVIA = 68, /* Bolivia */ + CTRY_BRAZIL = 76, /* Brazil */ + CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */ + CTRY_BULGARIA = 100, /* Bulgaria */ + CTRY_CANADA = 124, /* Canada */ + CTRY_CHILE = 152, /* Chile */ + CTRY_CHINA = 156, /* People's Republic of China */ + CTRY_COLOMBIA = 170, /* Colombia */ + CTRY_COSTA_RICA = 188, /* Costa Rica */ + CTRY_CROATIA = 191, /* Croatia */ + CTRY_CYPRUS = 196, /* Cyprus */ + CTRY_CZECH = 203, /* Czech Republic */ + CTRY_DENMARK = 208, /* Denmark */ + CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ + CTRY_ECUADOR = 218, /* Ecuador */ + CTRY_EGYPT = 818, /* Egypt */ + CTRY_EL_SALVADOR = 222, /* El Salvador */ + CTRY_ESTONIA = 233, /* Estonia */ + CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */ + CTRY_FINLAND = 246, /* Finland */ + CTRY_FRANCE = 250, /* France */ + CTRY_FRANCE2 = 255, /* France2 */ + CTRY_GEORGIA = 268, /* Georgia */ + CTRY_GERMANY = 276, /* Germany */ + CTRY_GREECE = 300, /* Greece */ + CTRY_GUATEMALA = 320, /* Guatemala */ + CTRY_HONDURAS = 340, /* Honduras */ + CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */ + CTRY_HUNGARY = 348, /* Hungary */ + CTRY_ICELAND = 352, /* Iceland */ + CTRY_INDIA = 356, /* India */ + CTRY_INDONESIA = 360, /* Indonesia */ + CTRY_IRAN = 364, /* Iran */ + CTRY_IRAQ = 368, /* Iraq */ + CTRY_IRELAND = 372, /* Ireland */ + CTRY_ISRAEL = 376, /* Israel */ + CTRY_ITALY = 380, /* Italy */ + CTRY_JAMAICA = 388, /* Jamaica */ + CTRY_JAPAN = 392, /* Japan */ + CTRY_JAPAN1 = 393, /* Japan (JP1) */ + CTRY_JAPAN2 = 394, /* Japan (JP0) */ + CTRY_JAPAN3 = 395, /* Japan (JP1-1) */ + CTRY_JAPAN4 = 396, /* Japan (JE1) */ + CTRY_JAPAN5 = 397, /* Japan (JE2) */ + CTRY_JORDAN = 400, /* Jordan */ + CTRY_KAZAKHSTAN = 398, /* Kazakhstan */ + CTRY_KENYA = 404, /* Kenya */ + CTRY_KOREA_NORTH = 408, /* North Korea */ + CTRY_KOREA_ROC = 410, /* South Korea */ + CTRY_KOREA_ROC2 = 411, /* South Korea */ + CTRY_KUWAIT = 414, /* Kuwait */ + CTRY_LATVIA = 428, /* Latvia */ + CTRY_LEBANON = 422, /* Lebanon */ + CTRY_LIBYA = 434, /* Libya */ + CTRY_LIECHTENSTEIN = 438, /* Liechtenstein */ + CTRY_LITHUANIA = 440, /* Lithuania */ + CTRY_LUXEMBOURG = 442, /* Luxembourg */ + CTRY_MACAU = 446, /* Macau */ + CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */ + CTRY_MALAYSIA = 458, /* Malaysia */ + CTRY_MEXICO = 484, /* Mexico */ + CTRY_MONACO = 492, /* Principality of Monaco */ + CTRY_MOROCCO = 504, /* Morocco */ + CTRY_NETHERLANDS = 528, /* Netherlands */ + CTRY_NEW_ZEALAND = 554, /* New Zealand */ + CTRY_NICARAGUA = 558, /* Nicaragua */ + CTRY_NORWAY = 578, /* Norway */ + CTRY_OMAN = 512, /* Oman */ + CTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */ + CTRY_PANAMA = 591, /* Panama */ + CTRY_PARAGUAY = 600, /* Paraguay */ + CTRY_PERU = 604, /* Peru */ + CTRY_PHILIPPINES = 608, /* Republic of the Philippines */ + CTRY_POLAND = 616, /* Poland */ + CTRY_PORTUGAL = 620, /* Portugal */ + CTRY_PUERTO_RICO = 630, /* Puerto Rico */ + CTRY_QATAR = 634, /* Qatar */ + CTRY_ROMANIA = 642, /* Romania */ + CTRY_RUSSIA = 643, /* Russia */ + CTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */ + CTRY_SINGAPORE = 702, /* Singapore */ + CTRY_SLOVAKIA = 703, /* Slovak Republic */ + CTRY_SLOVENIA = 705, /* Slovenia */ + CTRY_SOUTH_AFRICA = 710, /* South Africa */ + CTRY_SPAIN = 724, /* Spain */ + CTRY_SWEDEN = 752, /* Sweden */ + CTRY_SWITZERLAND = 756, /* Switzerland */ + CTRY_SYRIA = 760, /* Syria */ + CTRY_TAIWAN = 158, /* Taiwan */ + CTRY_THAILAND = 764, /* Thailand */ + CTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */ + CTRY_TUNISIA = 788, /* Tunisia */ + CTRY_TURKEY = 792, /* Turkey */ + CTRY_UAE = 784, /* U.A.E. */ + CTRY_UKRAINE = 804, /* Ukraine */ + CTRY_UNITED_KINGDOM = 826, /* United Kingdom */ + CTRY_UNITED_STATES = 840, /* United States */ + CTRY_URUGUAY = 858, /* Uruguay */ + CTRY_UZBEKISTAN = 860, /* Uzbekistan */ + CTRY_VENEZUELA = 862, /* Venezuela */ + CTRY_VIET_NAM = 704, /* Viet Nam */ + CTRY_YEMEN = 887, /* Yemen */ + CTRY_ZIMBABWE = 716, /* Zimbabwe */ +}; + +#if defined(__KERNEL__) || defined(_KERNEL) +#define CTRY_DEBUG 0x1ff /* debug */ +#define CTRY_DEFAULT 0 /* default */ + +void ieee80211_regdomain_attach(struct ieee80211com *); +void ieee80211_regdomain_detach(struct ieee80211com *); + +void ieee80211_init_channels(struct ieee80211com *ic, + int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm); +uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *, + enum ISOCountryCode cc, int location); +const char *ieee80211_cctoiso(enum ISOCountryCode); +int ieee80211_isotocc(const char iso[2]); +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ +#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */ diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c new file mode 100644 index 000000000000..609974445cb8 --- /dev/null +++ b/sys/net80211/ieee80211_scan.c @@ -0,0 +1,990 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 scanning support. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +struct scan_state { + struct ieee80211_scan_state base; /* public state */ + + u_int ss_iflags; /* flags used internally */ +#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ +#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ +#define ISCAN_CANCEL 0x0004 /* cancel current scan */ +#define ISCAN_START 0x0008 /* 1st time through next_scan */ + unsigned long ss_chanmindwell; /* min dwell on curchan */ + unsigned long ss_scanend; /* time scan must stop */ + u_int ss_duration; /* duration for next scan */ + struct callout ss_scan_timer; /* scan timer */ +}; +#define SCAN_PRIVATE(ss) ((struct scan_state *) ss) + +/* + * Amount of time to go off-channel during a background + * scan. This value should be large enough to catch most + * ap's but short enough that we can return on-channel + * before our listen interval expires. + * + * XXX tunable + * XXX check against configured listen interval + */ +#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) + +/* + * Roaming-related defaults. RSSI thresholds are as returned by the + * driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e + * .5M units). + */ +#define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */ +#define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */ +#define ROAM_RSSI_11BONLY_DEFAULT 14 /* rssi threshold for 11b-only bss */ +#define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */ +#define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */ +#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */ + +static void scan_restart_pwrsav(void *); +static void scan_curchan(struct ieee80211com *, unsigned long); +static void scan_mindwell(struct ieee80211com *); +static void scan_next(void *); + +MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); + +void +ieee80211_scan_attach(struct ieee80211com *ic) +{ + struct scan_state *ss; + + ic->ic_roaming = IEEE80211_ROAMING_AUTO; + + MALLOC(ss, struct scan_state *, sizeof(struct scan_state), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (ss == NULL) { + ic->ic_scan = NULL; + return; + } + callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE); + ic->ic_scan = &ss->base; + + ic->ic_scan_curchan = scan_curchan; + ic->ic_scan_mindwell = scan_mindwell; + + ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; + ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; + ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; + ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT; + ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT; + ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT; + ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT; + ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT; + ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT; +} + +void +ieee80211_scan_detach(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss != NULL) { + callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); + if (ss->ss_ops != NULL) { + ss->ss_ops->scan_detach(ss); + ss->ss_ops = NULL; + } + ic->ic_flags &= ~IEEE80211_F_SCAN; + ic->ic_scan = NULL; + FREE(SCAN_PRIVATE(ss), M_80211_SCAN); + } +} + +/* + * Simple-minded scanner module support. + */ +#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1) + +static const char *scan_modnames[IEEE80211_SCANNER_MAX] = { + "wlan_scan_sta", /* IEEE80211_M_IBSS */ + "wlan_scan_sta", /* IEEE80211_M_STA */ + "wlan_scan_wds", /* IEEE80211_M_WDS */ + "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ + "wlan_scan_4", /* n/a */ + "wlan_scan_5", /* n/a */ + "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ + "wlan_scan_7", /* n/a */ + "wlan_scan_monitor", /* IEEE80211_M_MONITOR */ +}; +static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX]; + +const struct ieee80211_scanner * +ieee80211_scanner_get(enum ieee80211_opmode mode) +{ + if (mode >= IEEE80211_SCANNER_MAX) + return NULL; + if (scanners[mode] == NULL) + ieee80211_load_module(scan_modnames[mode]); + return scanners[mode]; +} + +void +ieee80211_scanner_register(enum ieee80211_opmode mode, + const struct ieee80211_scanner *scan) +{ + if (mode >= IEEE80211_SCANNER_MAX) + return; + scanners[mode] = scan; +} + +void +ieee80211_scanner_unregister(enum ieee80211_opmode mode, + const struct ieee80211_scanner *scan) +{ + if (mode >= IEEE80211_SCANNER_MAX) + return; + if (scanners[mode] == scan) + scanners[mode] = NULL; +} + +void +ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) +{ + int m; + + for (m = 0; m < IEEE80211_SCANNER_MAX; m++) + if (scanners[m] == scan) + scanners[m] = NULL; +} + +/* + * Update common scanner state to reflect the current + * operating mode. This is called when the state machine + * is transitioned to RUN state w/o scanning--e.g. when + * operating in monitor mode. The purpose of this is to + * ensure later callbacks find ss_ops set to properly + * reflect current operating mode. + */ +int +ieee80211_scan_update(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + const struct ieee80211_scanner *scan; + + scan = ieee80211_scanner_get(ic->ic_opmode); + IEEE80211_LOCK(ic); + if (scan == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scanner support for mode %u\n", + __func__, ic->ic_opmode); + /* XXX stat */ + } + ss->ss_ic = ic; + if (ss->ss_ops != scan) { + /* switch scanners; detach old, attach new */ + if (ss->ss_ops != NULL) + ss->ss_ops->scan_detach(ss); + if (scan != NULL && !scan->scan_attach(ss)) { + /* XXX attach failure */ + /* XXX stat+msg */ + ss->ss_ops = NULL; + } else + ss->ss_ops = scan; + } + IEEE80211_UNLOCK(ic); + + return (scan != NULL); +} + +static void +change_channel(struct ieee80211com *ic, + struct ieee80211_channel *chan) +{ + ic->ic_curchan = chan; + ic->ic_set_channel(ic); +} + +static char +channel_type(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_ST(c)) + return 'S'; + if (IEEE80211_IS_CHAN_108A(c)) + return 'T'; + if (IEEE80211_IS_CHAN_108G(c)) + return 'G'; + if (IEEE80211_IS_CHAN_HT(c)) + return 'n'; + if (IEEE80211_IS_CHAN_A(c)) + return 'a'; + if (IEEE80211_IS_CHAN_ANYG(c)) + return 'g'; + if (IEEE80211_IS_CHAN_B(c)) + return 'b'; + return 'f'; +} + +void +ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) +{ + struct ieee80211com *ic = ss->ss_ic; + const char *sep; + int i; + + sep = ""; + for (i = ss->ss_next; i < ss->ss_last; i++) { + const struct ieee80211_channel *c = ss->ss_chans[i]; + + printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), + channel_type(c)); + sep = ", "; + } +} + +/* + * Enable station power save mode and start/restart the scanning thread. + */ +static void +scan_restart_pwrsav(void *arg) +{ + struct scan_state *ss = (struct scan_state *) arg; + struct ieee80211com *ic = ss->base.ss_ic; + int delay; + + ieee80211_sta_pwrsave(ic, 1); + /* + * Use an initial 1ms delay to insure the null + * data frame has a chance to go out. + * XXX 1ms is a lot, better to trigger scan + * on tx complete. + */ + delay = hz/1000; + if (delay < 1) + delay = 1; + ic->ic_scan_start(ic); /* notify driver */ + ss->ss_scanend = ticks + delay + ss->ss_duration; + ss->ss_iflags |= ISCAN_START; + callout_reset(&ss->ss_scan_timer, delay, scan_next, ss); +} + +/* + * Start/restart scanning. If we're operating in station mode + * and associated notify the ap we're going into power save mode + * and schedule a callback to initiate the work (where there's a + * better context for doing the work). Otherwise, start the scan + * directly. + */ +static int +scan_restart(struct scan_state *ss, u_int duration) +{ + struct ieee80211com *ic = ss->base.ss_ic; + int defer = 0; + + if (ss->base.ss_next == ss->base.ss_last) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no channels to scan\n", __func__); + return 0; + } + if (ic->ic_opmode == IEEE80211_M_STA && + ic->ic_state == IEEE80211_S_RUN) { + if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { + /* + * Initiate power save before going off-channel. + * Note that we cannot do this directly because + * of locking issues; instead we defer it to a + * tasklet. + */ + ss->ss_duration = duration; + defer = 1; + } + } + + if (!defer) { + ic->ic_scan_start(ic); /* notify driver */ + ss->ss_scanend = ticks + duration; + ss->ss_iflags |= ISCAN_START; + callout_reset(&ss->ss_scan_timer, 0, scan_next, ss); + } else + scan_restart_pwrsav(ss); + return 1; +} + +static void +copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + if (nssid > IEEE80211_SCAN_MAX_SSID) { + /* XXX printf */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: too many ssid %d, ignoring all of them\n", + __func__, nssid); + return; + } + memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0])); + ss->ss_nssid = nssid; +} + +/* + * Start a scan unless one is already going. + */ +int +ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + const struct ieee80211_scanner *scan; + struct ieee80211_scan_state *ss = ic->ic_scan; + + scan = ieee80211_scanner_get(ic->ic_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scanner support for mode %u\n", + __func__, ic->ic_opmode); + /* XXX stat */ + return 0; + } + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n" + , __func__ + , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" + , duration + , ieee80211_phymode_name[ic->ic_des_mode] + , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" + , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" + , flags & IEEE80211_SCAN_ONCE ? ", once" : "" + ); + + ss->ss_ic = ic; + if (ss->ss_ops != scan) { + /* switch scanners; detach old, attach new */ + if (ss->ss_ops != NULL) + ss->ss_ops->scan_detach(ss); + if (!scan->scan_attach(ss)) { + /* XXX attach failure */ + /* XXX stat+msg */ + ss->ss_ops = NULL; + } else + ss->ss_ops = scan; + } + if (ss->ss_ops != NULL) { + if ((flags & IEEE80211_SCAN_NOSSID) == 0) + copy_ssid(ic, ss, nssid, ssids); + + /* NB: top 4 bits for internal use */ + ss->ss_flags = flags & 0xfff; + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ic->ic_stats.is_scan_active++; + else + ic->ic_stats.is_scan_passive++; + if (flags & IEEE80211_SCAN_FLUSH) + ss->ss_ops->scan_flush(ss); + + /* NB: flush frames rx'd before 1st channel change */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + ss->ss_ops->scan_start(ss, ic); + if (scan_restart(SCAN_PRIVATE(ss), duration)) + ic->ic_flags |= IEEE80211_F_SCAN; + } + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + IEEE80211_UNLOCK(ic); + + /* NB: racey, does it matter? */ + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Check the scan cache for an ap/channel to use; if that + * fails then kick off a new scan. + */ +int +ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + int checkscanlist = 0; + + /* + * Check if there's a list of scan candidates already. + * XXX want more than the ap we're currently associated with + */ + + IEEE80211_LOCK(ic); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n" + , __func__ + , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" + , duration + , ieee80211_phymode_name[ic->ic_des_mode] + , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" + , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" + , flags & IEEE80211_SCAN_ONCE ? ", once" : "" + ); + + if (ss->ss_ops != NULL) { + /* XXX verify ss_ops matches ic->ic_opmode */ + if ((flags & IEEE80211_SCAN_NOSSID) == 0) { + /* + * Update the ssid list and mark flags so if + * we call start_scan it doesn't duplicate work. + */ + copy_ssid(ic, ss, nssid, ssids); + flags |= IEEE80211_SCAN_NOSSID; + } + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && + time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) { + /* + * We're not currently scanning and the cache is + * deemed hot enough to consult. Lock out others + * by marking IEEE80211_F_SCAN while we decide if + * something is already in the scan cache we can + * use. Also discard any frames that might come + * in while temporarily marked as scanning. + */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + ic->ic_flags |= IEEE80211_F_SCAN; + checkscanlist = 1; + } + } + IEEE80211_UNLOCK(ic); + if (checkscanlist) { + const struct ieee80211_scanner *scan; + + scan = ieee80211_scanner_get(ic->ic_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scanner support for mode %u\n", + __func__, ic->ic_opmode); + /* XXX stat */ + return 0; + } + if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) { + /* found an ap, just clear the flag */ + ic->ic_flags &= ~IEEE80211_F_SCAN; + return 1; + } + /* no ap, clear the flag before starting a scan */ + ic->ic_flags &= ~IEEE80211_F_SCAN; + } + return ieee80211_start_scan(ic, flags, duration, nssid, ssids); +} + +/* + * Restart a previous scan. If the previous scan completed + * then we start again using the existing channel list. + */ +int +ieee80211_bg_scan(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + u_int duration; + /* + * Go off-channel for a fixed interval that is large + * enough to catch most ap's but short enough that + * we can return on-channel before our listen interval + * expires. + */ + duration = IEEE80211_SCAN_OFFCHANNEL; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s scan, ticks %u duration %lu\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", + ticks, duration); + + if (ss->ss_ops != NULL) { + ss->ss_ic = ic; + /* + * A background scan does not select a new sta; it + * just refreshes the scan cache. Also, indicate + * the scan logic should follow the beacon schedule: + * we go off-channel and scan for a while, then + * return to the bss channel to receive a beacon, + * then go off-channel again. All during this time + * we notify the ap we're in power save mode. When + * the scan is complete we leave power save mode. + * If any beacon indicates there are frames pending + * for us then we drop out of power save mode + * (and background scan) automatically by way of the + * usual sta power save logic. + */ + ss->ss_flags |= IEEE80211_SCAN_NOPICK + | IEEE80211_SCAN_BGSCAN; + /* if previous scan completed, restart */ + if (ss->ss_next >= ss->ss_last) { + ss->ss_next = 0; + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ic->ic_stats.is_scan_active++; + else + ic->ic_stats.is_scan_passive++; + ss->ss_ops->scan_restart(ss, ic); + } + /* NB: flush frames rx'd before 1st channel change */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + ss->ss_maxdwell = duration; + if (scan_restart(SCAN_PRIVATE(ss), duration)) { + ic->ic_flags |= IEEE80211_F_SCAN; + ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; + } + } else { + /* XXX msg+stat */ + } + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + IEEE80211_UNLOCK(ic); + + /* NB: racey, does it matter? */ + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Cancel any scan currently going on. + */ +void +ieee80211_cancel_scan(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* force it to fire asap */ + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, + 0, scan_next, ss); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Public access to scan_next for drivers that manage + * scanning themselves (e.g. for firmware-based devices). + */ +void +ieee80211_scan_next(struct ieee80211com *ic) +{ + /* + * XXX: We might need/want to decouple context here by either: + * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); + * or using a taskqueue. Let's see what kind of problems direct + * dispatch has for now. + */ + scan_next(ic->ic_scan); +} + +/* + * Scan curchan. If this is an active scan and the channel + * is not marked passive then send probe request frame(s). + * Arrange for the channel change after maxdwell ticks. + */ +static void +scan_curchan(struct ieee80211com *ic, unsigned long maxdwell) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { + struct ifnet *ifp = ic->ic_ifp; + int i; + + /* + * Send a broadcast probe request followed by + * any specified directed probe requests. + * XXX suppress broadcast probe req? + * XXX remove dependence on ic/ic->ic_bss + * XXX move to policy code? + */ + ieee80211_send_probereq(ic->ic_bss, + ic->ic_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + "", 0, + ic->ic_opt_ie, ic->ic_opt_ie_len); + for (i = 0; i < ss->ss_nssid; i++) + ieee80211_send_probereq(ic->ic_bss, + ic->ic_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + ss->ss_ssid[i].ssid, + ss->ss_ssid[i].len, + ic->ic_opt_ie, ic->ic_opt_ie_len); + } + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, + maxdwell, scan_next, ss); +} + +/* + * Handle mindwell requirements completed; initiate a channel + * change to the next channel asap. + */ +static void +scan_mindwell(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); +} + +/* + * Switch to the next channel marked for scanning. + */ +static void +scan_next(void *arg) +{ +#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD) + struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + struct ieee80211com *ic = ss->ss_ic; + struct ieee80211_channel *chan; + unsigned long maxdwell, scanend; + int scanning, scandone; + + IEEE80211_LOCK(ic); + scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0; + IEEE80211_UNLOCK(ic); + if (!scanning) /* canceled */ + return; + +again: + scandone = (ss->ss_next >= ss->ss_last) || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; + scanend = SCAN_PRIVATE(ss)->ss_scanend; + if (!scandone && + (ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 && + ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) || + time_before(ticks + ss->ss_mindwell, scanend))) { + chan = ss->ss_chans[ss->ss_next++]; + + /* + * Watch for truncation due to the scan end time. + */ + if (time_after(ticks + ss->ss_maxdwell, scanend)) + maxdwell = scanend - ticks; + else + maxdwell = ss->ss_maxdwell; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + channel_type(ic->ic_curchan), + ieee80211_chan2ieee(ic, chan), channel_type(chan), + (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? + "active" : "passive", + ss->ss_mindwell, maxdwell); + + /* + * Potentially change channel and phy mode. + */ + change_channel(ic, chan); + + /* + * Scan curchan. Drivers for "intelligent hardware" + * override ic_scan_curchan to tell the device to do + * the work. Otherwise we manage the work outselves; + * sending a probe request (as needed), and arming the + * timeout to switch channels after maxdwell ticks. + */ + ic->ic_scan_curchan(ic, maxdwell); + + SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; + /* clear mindwell lock and initial channel change flush */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + } else { + ic->ic_scan_end(ic); /* notify driver */ + /* + * Record scan complete time. Note that we also do + * this when canceled so any background scan will + * not be restarted for a while. + */ + if (scandone) + ic->ic_lastscan = ticks; + /* return to the bss channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + ic->ic_curchan != ic->ic_bsschan) + change_channel(ic, ic->ic_bsschan); + /* clear internal flags and any indication of a pick */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; + + /* + * If not canceled and scan completed, do post-processing. + * If the callback function returns 0, then it wants to + * continue/restart scanning. Unfortunately we needed to + * notify the driver to end the scan above to avoid having + * rx frames alter the scan candidate list. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && + !ss->ss_ops->scan_end(ss, ic) && + (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && + time_before(ticks + ss->ss_mindwell, scanend)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: done, restart " + "[ticks %u, dwell min %lu scanend %lu]\n", + __func__, + ticks, ss->ss_mindwell, scanend); + ss->ss_next = 0; /* reset to begining */ + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ic->ic_stats.is_scan_active++; + else + ic->ic_stats.is_scan_passive++; + + ic->ic_scan_start(ic); /* notify driver */ + goto again; + } else { + /* past here, scandone is ``true'' if not in bg mode */ + if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) + scandone = 1; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: %s, " + "[ticks %u, dwell min %lu scanend %lu]\n", + __func__, scandone ? "done" : "stopped", + ticks, ss->ss_mindwell, scanend); + + /* + * Clear the SCAN bit first in case frames are + * pending on the station power save queue. If + * we defer this then the dispatch of the frames + * may generate a request to cancel scanning. + */ + ic->ic_flags &= ~IEEE80211_F_SCAN; + /* + * Drop out of power save mode when a scan has + * completed. If this scan was prematurely terminated + * because it is a background scan then don't notify + * the ap; we'll either return to scanning after we + * receive the beacon frame or we'll drop out of power + * save mode because the beacon indicates we have frames + * waiting for us. + */ + if (scandone) { + ieee80211_sta_pwrsave(ic, 0); + if (ss->ss_next >= ss->ss_last) { + ieee80211_notify_scan_done(ic); + ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; + } + } + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL; + ss->ss_flags &= + ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); + } + } +#undef ISCAN_REP +} + +#ifdef IEEE80211_DEBUG +static void +dump_probe_beacon(uint8_t subtype, int isnew, + const uint8_t mac[IEEE80211_ADDR_LEN], + const struct ieee80211_scanparams *sp) +{ + + printf("[%s] %s%s on chan %u (bss chan %u) ", + ether_sprintf(mac), isnew ? "new " : "", + ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], + sp->chan, sp->bchan); + ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); + printf("\n"); + + if (isnew) { + printf("[%s] caps 0x%x bintval %u erp 0x%x", + ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); + if (sp->country != NULL) { +#ifdef __FreeBSD__ + printf(" country info %*D", + sp->country[1], sp->country+2, " "); +#else + int i; + printf(" country info"); + for (i = 0; i < sp->country[1]; i++) + printf(" %02x", sp->country[i+2]); +#endif + } + printf("\n"); + } +} +#endif /* IEEE80211_DEBUG */ + +/* + * Process a beacon or probe response frame. + */ +void +ieee80211_add_scan(struct ieee80211com *ic, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise, int rstamp) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + /* + * Frames received during startup are discarded to avoid + * using scan state setup on the initial entry to the timer + * callback. This can occur because the device may enable + * rx prior to our doing the initial channel change in the + * timer routine (we defer the channel change to the timer + * code to simplify locking on linux). + */ + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) + return; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN)) + dump_probe_beacon(subtype, 1, wh->i_addr2, sp); +#endif + if (ss->ss_ops != NULL && + ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) { + /* + * If we've reached the min dwell time terminate + * the timer so we'll switch to the next channel. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && + time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: chan %3d%c min dwell met (%u > %lu)\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + channel_type(ic->ic_curchan), + ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); + /* + * XXX + * We want to just kick the timer and still + * process frames until it fires but linux + * will livelock unless we discard frames. + */ +#if 0 + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; +#else + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; +#endif + /* + * NB: trigger at next clock tick or wait for the + * hardware + */ + ic->ic_scan_mindwell(ic); + } + } +} + +/* + * Timeout/age scan cache entries; called from sta timeout + * timer (XXX should be self-contained). + */ +void +ieee80211_scan_timeout(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) + ss->ss_ops->scan_age(ss); +} + +/* + * Mark a scan cache entry after a successful associate. + */ +void +ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[]) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) { + IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, + mac, "%s", __func__); + ss->ss_ops->scan_assoc_success(ss, mac); + } +} + +/* + * Demerit a scan cache entry after failing to associate. + */ +void +ieee80211_scan_assoc_fail(struct ieee80211com *ic, + const uint8_t mac[], int reason) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) { + IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac, + "%s: reason %u", __func__, reason); + ss->ss_ops->scan_assoc_fail(ss, mac, reason); + } +} + +/* + * Iterate over the contents of the scan cache. + */ +void +ieee80211_scan_iterate(struct ieee80211com *ic, + ieee80211_scan_iter_func *f, void *arg) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) + ss->ss_ops->scan_iterate(ss, f, arg); +} + +/* + * Flush the contents of the scan cache. + */ +void +ieee80211_scan_flush(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s\n", __func__); + ss->ss_ops->scan_flush(ss); + } +} diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h new file mode 100644 index 000000000000..4881aea954e2 --- /dev/null +++ b/sys/net80211/ieee80211_scan.h @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_SCAN_H_ +#define _NET80211_IEEE80211_SCAN_H_ + +#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX + +struct ieee80211_scanner; + +struct ieee80211_scan_ssid { + int len; /* length in bytes */ + uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */ +}; +#define IEEE80211_SCAN_MAX_SSID 1 + +struct ieee80211_scan_state { + struct ieee80211com *ss_ic; + const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */ + void *ss_priv; /* scanner private state */ + uint16_t ss_flags; +#define IEEE80211_SCAN_NOPICK 0x0001 /* scan only, no selection */ +#define IEEE80211_SCAN_ACTIVE 0x0002 /* active scan (probe req) */ +#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */ +#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */ +#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */ +#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */ + uint8_t ss_nssid; /* # ssid's to probe/match */ + struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID]; + /* ssid's to probe/match */ + /* ordered channel set */ + struct ieee80211_channel *ss_chans[IEEE80211_SCAN_MAX]; + uint16_t ss_next; /* ix of next chan to scan */ + uint16_t ss_last; /* ix+1 of last chan to scan */ + unsigned long ss_mindwell; /* min dwell on channel */ + unsigned long ss_maxdwell; /* max dwell on channel */ +}; + +/* + * The upper 16 bits of the flags word is used to communicate + * information to the scanning code that is NOT recorded in + * ss_flags. It might be better to split this stuff out into + * a separate variable to avoid confusion. + */ +#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */ +#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */ + +struct ieee80211com; +void ieee80211_scan_attach(struct ieee80211com *); +void ieee80211_scan_detach(struct ieee80211com *); + +void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *); + +int ieee80211_scan_update(struct ieee80211com *); +#define IEEE80211_SCAN_FOREVER 0x7fffffff +int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration, + u_int nssid, const struct ieee80211_scan_ssid ssids[]); +int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration, + u_int nssid, const struct ieee80211_scan_ssid ssids[]); +int ieee80211_bg_scan(struct ieee80211com *); +void ieee80211_cancel_scan(struct ieee80211com *); +void ieee80211_scan_next(struct ieee80211com *); + +struct ieee80211_scanparams; +void ieee80211_add_scan(struct ieee80211com *, + const struct ieee80211_scanparams *, + const struct ieee80211_frame *, + int subtype, int rssi, int noise, int rstamp); +void ieee80211_scan_timeout(struct ieee80211com *); + +void ieee80211_scan_assoc_success(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +enum { + IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */ + IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */ +}; +void ieee80211_scan_assoc_fail(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN], int reason); +void ieee80211_scan_flush(struct ieee80211com *); + +struct ieee80211_scan_entry; +typedef void ieee80211_scan_iter_func(void *, + const struct ieee80211_scan_entry *); +void ieee80211_scan_iterate(struct ieee80211com *, + ieee80211_scan_iter_func, void *); + +/* + * Parameters supplied when adding/updating an entry in a + * scan cache. Pointer variables should be set to NULL + * if no data is available. Pointer references can be to + * local data; any information that is saved will be copied. + * All multi-byte values must be in host byte order. + */ +struct ieee80211_scanparams { + uint16_t capinfo; /* 802.11 capabilities */ + uint16_t fhdwell; /* FHSS dwell interval */ + uint8_t chan; /* */ + uint8_t bchan; + uint8_t fhindex; + uint8_t erp; + uint16_t bintval; + uint8_t timoff; + uint8_t *tim; + uint8_t *tstamp; + uint8_t *country; + uint8_t *ssid; + uint8_t *rates; + uint8_t *xrates; + uint8_t *doth; + uint8_t *wpa; + uint8_t *rsn; + uint8_t *wme; + uint8_t *htcap; + uint8_t *htinfo; + uint8_t *ath; +}; + +/* + * Scan cache entry format used when exporting data from a policy + * module; this data may be represented some other way internally. + */ +struct ieee80211_scan_entry { + uint8_t se_macaddr[IEEE80211_ADDR_LEN]; + uint8_t se_bssid[IEEE80211_ADDR_LEN]; + uint8_t se_ssid[2+IEEE80211_NWID_LEN]; + uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE]; + uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE]; + uint32_t se_rstamp; /* recv timestamp */ + union { + uint8_t data[8]; + uint64_t tsf; + } se_tstamp; /* from last rcv'd beacon */ + uint16_t se_intval; /* beacon interval (host byte order) */ + uint16_t se_capinfo; /* capabilities (host byte order) */ + struct ieee80211_channel *se_chan;/* channel where sta found */ + uint16_t se_timoff; /* byte offset to TIM ie */ + uint16_t se_fhdwell; /* FH only (host byte order) */ + uint8_t se_fhindex; /* FH only */ + uint8_t se_erp; /* ERP from beacon/probe resp */ + int8_t se_rssi; /* avg'd recv ssi */ + int8_t se_noise; /* noise floor */ + uint8_t se_dtimperiod; /* DTIM period */ + uint8_t *se_wpa_ie; /* captured WPA ie */ + uint8_t *se_rsn_ie; /* captured RSN ie */ + uint8_t *se_wme_ie; /* captured WME ie */ + uint8_t *se_htcap_ie; /* captured HTP cap ie */ + uint8_t *se_htinfo_ie; /* captured HTP info ie */ + uint8_t *se_ath_ie; /* captured Atheros ie */ + u_int se_age; /* age of entry (0 on create) */ +}; +MALLOC_DECLARE(M_80211_SCAN); + +/* + * Template for an in-kernel scan policy module. + * Modules register with the scanning code and are + * typically loaded as needed. + */ +struct ieee80211_scanner { + const char *scan_name; /* printable name */ + int (*scan_attach)(struct ieee80211_scan_state *); + int (*scan_detach)(struct ieee80211_scan_state *); + int (*scan_start)(struct ieee80211_scan_state *, + struct ieee80211com *); + int (*scan_restart)(struct ieee80211_scan_state *, + struct ieee80211com *); + int (*scan_cancel)(struct ieee80211_scan_state *, + struct ieee80211com *); + int (*scan_end)(struct ieee80211_scan_state *, + struct ieee80211com *); + int (*scan_flush)(struct ieee80211_scan_state *); + /* add an entry to the cache */ + int (*scan_add)(struct ieee80211_scan_state *, + const struct ieee80211_scanparams *, + const struct ieee80211_frame *, + int subtype, int rssi, int noise, int rstamp); + /* age and/or purge entries in the cache */ + void (*scan_age)(struct ieee80211_scan_state *); + /* note that association failed for an entry */ + void (*scan_assoc_fail)(struct ieee80211_scan_state *, + const uint8_t macaddr[IEEE80211_ADDR_LEN], + int reason); + /* note that association succeed for an entry */ + void (*scan_assoc_success)(struct ieee80211_scan_state *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); + /* iterate over entries in the scan cache */ + void (*scan_iterate)(struct ieee80211_scan_state *, + ieee80211_scan_iter_func *, void *); +}; +void ieee80211_scanner_register(enum ieee80211_opmode, + const struct ieee80211_scanner *); +void ieee80211_scanner_unregister(enum ieee80211_opmode, + const struct ieee80211_scanner *); +void ieee80211_scanner_unregister_all(const struct ieee80211_scanner *); +const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode); +#endif /* _NET80211_IEEE80211_SCAN_H_ */ diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c new file mode 100644 index 000000000000..9aa09ede78e0 --- /dev/null +++ b/sys/net80211/ieee80211_scan_ap.c @@ -0,0 +1,407 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 ap scanning support. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +struct ap_state { + int as_maxrssi[IEEE80211_CHAN_MAX]; +}; + +static int ap_flush(struct ieee80211_scan_state *); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +/* + * Attach prior to any scanning work. + */ +static int +ap_attach(struct ieee80211_scan_state *ss) +{ + struct ap_state *as; + + MALLOC(as, struct ap_state *, sizeof(struct ap_state), + M_80211_SCAN, M_NOWAIT); + ss->ss_priv = as; + ap_flush(ss); + nrefs++; /* NB: we assume caller locking */ + return 1; +} + +/* + * Cleanup any private state. + */ +static int +ap_detach(struct ieee80211_scan_state *ss) +{ + struct ap_state *as = ss->ss_priv; + + if (as != NULL) { + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ + FREE(as, M_80211_SCAN); + } + return 1; +} + +/* + * Flush all per-scan state. + */ +static int +ap_flush(struct ieee80211_scan_state *ss) +{ + struct ap_state *as = ss->ss_priv; + + memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi)); + ss->ss_last = 0; /* insure no channel will be picked */ + return 0; +} + +static int +find11gchannel(struct ieee80211com *ic, int i, int freq) +{ + const struct ieee80211_channel *c; + int j; + + /* + * The normal ordering in the channel list is b channel + * immediately followed by g so optimize the search for + * this. We'll still do a full search just in case. + */ + for (j = i+1; j < ic->ic_nchans; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + for (j = 0; j < i; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + return 0; +} + +/* + * Start an ap scan by populating the channel list. + */ +static int +ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct ieee80211_channel *c; + int i; + + ss->ss_last = 0; + if (ic->ic_des_mode == IEEE80211_MODE_AUTO) { + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (IEEE80211_IS_CHAN_TURBO(c)) { +#ifdef IEEE80211_F_XR + /* XR is not supported on turbo channels */ + if (ic->ic_flags & IEEE80211_F_XR) + continue; +#endif + /* dynamic channels are scanned in base mode */ + if (!IEEE80211_IS_CHAN_ST(c)) + continue; + } else if (IEEE80211_IS_CHAN_HT(c)) { + /* HT channels are scanned in legacy */ + continue; + } else { + /* + * Use any 11g channel instead of 11b one. + */ + if (IEEE80211_IS_CHAN_B(c) && + find11gchannel(ic, i, c->ic_freq)) + continue; + } + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + ss->ss_chans[ss->ss_last++] = c; + } + } else { + static const u_int chanflags[IEEE80211_MODE_MAX] = { + 0, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */ + IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ + IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */ + }; + u_int modeflags; + + modeflags = chanflags[ic->ic_des_mode]; + if ((ic->ic_flags & IEEE80211_F_TURBOP) && + modeflags != IEEE80211_CHAN_ST) { + if (ic->ic_des_mode == IEEE80211_MODE_11G) + modeflags = IEEE80211_CHAN_108G; + else + modeflags = IEEE80211_CHAN_108A; + } + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if ((c->ic_flags & modeflags) != modeflags) + continue; +#ifdef IEEE80211_F_XR + /* XR is not supported on turbo channels */ + if (IEEE80211_IS_CHAN_TURBO(c) && + (ic->ic_flags & IEEE80211_F_XR)) + continue; +#endif + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + /* + * Do not select static turbo channels if + * the mode is not static turbo. + */ + if (IEEE80211_IS_CHAN_STURBO(c) && + ic->ic_des_mode != IEEE80211_MODE_STURBO_A) + continue; + ss->ss_chans[ss->ss_last++] = c; + } + } + ss->ss_next = 0; + /* XXX tunables */ + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */ + +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + if_printf(ic->ic_ifp, "scan set "); + ieee80211_scan_dump_channels(ss); + printf(" dwell min %ld max %ld\n", + ss->ss_mindwell, ss->ss_maxdwell); + } +#endif /* IEEE80211_DEBUG */ + + return 0; +} + +/* + * Restart a bg scan. + */ +static int +ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + return 0; +} + +/* + * Cancel an ongoing scan. + */ +static int +ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + return 0; +} + +/* + * Record max rssi on channel. + */ +static int +ap_add(struct ieee80211_scan_state *ss, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise, int rstamp) +{ + struct ap_state *as = ss->ss_priv; + struct ieee80211com *ic = ss->ss_ic; + int chan; + + chan = ieee80211_chan2ieee(ic, ic->ic_curchan); + /* XXX better quantification of channel use? */ + /* XXX count bss's? */ + if (rssi > as->as_maxrssi[chan]) + as->as_maxrssi[chan] = rssi; + /* XXX interference, turbo requirements */ + return 1; +} + +/* + * Pick a quiet channel to use for ap operation. + */ +static int +ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct ap_state *as = ss->ss_priv; + int i, chan, bestchan, bestchanix; + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP, + ("wrong opmode %u", ic->ic_opmode)); + /* XXX select channel more intelligently, e.g. channel spread, power */ + bestchan = -1; + bestchanix = 0; /* NB: silence compiler */ + /* NB: use scan list order to preserve channel preference */ + for (i = 0; i < ss->ss_last; i++) { + /* + * If the channel is unoccupied the max rssi + * should be zero; just take it. Otherwise + * track the channel with the lowest rssi and + * use that when all channels appear occupied. + */ + /* XXX channel have interference? */ + chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n", + __func__, chan, as->as_maxrssi[chan], + bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0); + + if (as->as_maxrssi[chan] == 0) { + bestchan = chan; + bestchanix = i; + /* XXX use other considerations */ + break; + } + if (bestchan == -1 || + as->as_maxrssi[chan] < as->as_maxrssi[bestchan]) + bestchan = chan; + } + if (bestchan == -1) { + /* no suitable channel, should not happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no suitable channel! (should not happen)\n", __func__); + /* XXX print something? */ + return 0; /* restart scan */ + } else { + struct ieee80211_channel *c; + + /* XXX notify all vap's? */ + /* + * If this is a dynamic turbo frequency, + * start with normal mode first. + */ + c = ss->ss_chans[bestchanix]; + if (IEEE80211_IS_CHAN_TURBO(c) && + !IEEE80211_IS_CHAN_STURBO(c)) { + c = ieee80211_find_channel(ic, c->ic_freq, + c->ic_flags & ~IEEE80211_CHAN_TURBO); + if (c == NULL) { + /* should never happen ?? */ + return 0; + } + } + ieee80211_create_ibss(ic, c); + return 1; + } +} + +static void +ap_age(struct ieee80211_scan_state *ss) +{ + /* XXX is there anything meaningful to do? */ +} + +static void +ap_iterate(struct ieee80211_scan_state *ss, + ieee80211_scan_iter_func *f, void *arg) +{ + /* NB: nothing meaningful we can do */ +} + +static void +ap_assoc_success(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + /* should not be called */ +} + +static void +ap_assoc_fail(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason) +{ + /* should not be called */ +} + +static const struct ieee80211_scanner ap_default = { + .scan_name = "default", + .scan_attach = ap_attach, + .scan_detach = ap_detach, + .scan_start = ap_start, + .scan_restart = ap_restart, + .scan_cancel = ap_cancel, + .scan_end = ap_end, + .scan_flush = ap_flush, + .scan_add = ap_add, + .scan_age = ap_age, + .scan_iterate = ap_iterate, + .scan_assoc_success = ap_assoc_success, + .scan_assoc_fail = ap_assoc_fail, +}; + +/* + * Module glue. + */ +static int +wlan_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default); + return 0; + case MOD_UNLOAD: + case MOD_QUIESCE: + if (nrefs) { + printf("wlan_scan_ap: still in use (%u dynamic refs)\n", + nrefs); + return EBUSY; + } + if (type == MOD_UNLOAD) + ieee80211_scanner_unregister_all(&ap_default); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_mod = { + "wlan_scan_ap", + wlan_modevent, + 0 +}; +DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_scan_ap, 1); +MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c new file mode 100644 index 000000000000..7bd04c83ec43 --- /dev/null +++ b/sys/net80211/ieee80211_scan_sta.c @@ -0,0 +1,1438 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 station scanning support. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +/* + * Parameters for managing cache entries: + * + * o a station with STA_FAILS_MAX failures is not considered + * when picking a candidate + * o a station that hasn't had an update in STA_PURGE_SCANS + * (background) scans is discarded + * o after STA_FAILS_AGE seconds we clear the failure count + */ +#define STA_FAILS_MAX 2 /* assoc failures before ignored */ +#define STA_FAILS_AGE (2*60) /* time before clearing fails (secs) */ +#define STA_PURGE_SCANS 2 /* age for purging entries (scans) */ + +/* XXX tunable */ +#define STA_RSSI_MIN 8 /* min acceptable rssi */ +#define STA_RSSI_MAX 40 /* max rssi for comparison */ + +#define RSSI_LPF_LEN 10 +#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ +#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER) +#define LPF_RSSI(x, y, len) (((x) * ((len) - 1) + (y)) / (len)) +#define RSSI_LPF(x, y) do { \ + if ((y) >= -20) \ + x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \ +} while (0) +#define EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul)) +#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER) + +struct sta_entry { + struct ieee80211_scan_entry base; + TAILQ_ENTRY(sta_entry) se_list; + LIST_ENTRY(sta_entry) se_hash; + uint8_t se_fails; /* failure to associate count */ + uint8_t se_seen; /* seen during current scan */ + uint8_t se_notseen; /* not seen in previous scans */ + uint8_t se_flags; +#define STA_SSID_MATCH 0x01 +#define STA_BSSID_MATCH 0x02 + uint32_t se_avgrssi; /* LPF rssi state */ + unsigned long se_lastupdate; /* time of last update */ + unsigned long se_lastfail; /* time of last failure */ + unsigned long se_lastassoc; /* time of last association */ + u_int se_scangen; /* iterator scan gen# */ +}; + +#define STA_HASHSIZE 32 +/* simple hash is enough for variation of macaddr */ +#define STA_HASH(addr) \ + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % STA_HASHSIZE) + +struct sta_table { + struct mtx st_lock; /* on scan table */ + TAILQ_HEAD(, sta_entry) st_entry; /* all entries */ + LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE]; + struct mtx st_scanlock; /* on st_scangen */ + u_int st_scangen; /* gen# for iterator */ + int st_newscan; +}; + +static void sta_flush_table(struct sta_table *); +static int match_bss(struct ieee80211com *, + const struct ieee80211_scan_state *, struct sta_entry *, int); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +/* + * Attach prior to any scanning work. + */ +static int +sta_attach(struct ieee80211_scan_state *ss) +{ + struct sta_table *st; + + MALLOC(st, struct sta_table *, sizeof(struct sta_table), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (st == NULL) + return 0; + mtx_init(&st->st_lock, "scantable", "802.11 scan table", MTX_DEF); + mtx_init(&st->st_scanlock, "scangen", "802.11 scangen", MTX_DEF); + TAILQ_INIT(&st->st_entry); + ss->ss_priv = st; + nrefs++; /* NB: we assume caller locking */ + return 1; +} + +/* + * Cleanup any private state. + */ +static int +sta_detach(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + + if (st != NULL) { + sta_flush_table(st); + mtx_destroy(&st->st_lock); + mtx_destroy(&st->st_scanlock); + FREE(st, M_80211_SCAN); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ + } + return 1; +} + +/* + * Flush all per-scan state. + */ +static int +sta_flush(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + + mtx_lock(&st->st_lock); + sta_flush_table(st); + mtx_unlock(&st->st_lock); + ss->ss_last = 0; + return 0; +} + +/* + * Flush all entries in the scan cache. + */ +static void +sta_flush_table(struct sta_table *st) +{ + struct sta_entry *se, *next; + + TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { + TAILQ_REMOVE(&st->st_entry, se, se_list); + LIST_REMOVE(se, se_hash); + FREE(se, M_80211_SCAN); + } +} + +static void +saveie(uint8_t **iep, const uint8_t *ie) +{ + + if (ie == NULL) + *iep = NULL; + else + ieee80211_saveie(iep, ie); +} + +/* + * Process a beacon or probe response frame; create an + * entry in the scan cache or update any previous entry. + */ +static int +sta_add(struct ieee80211_scan_state *ss, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise, int rstamp) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define PICK1ST(_ss) \ + ((ss->ss_flags & (IEEE80211_SCAN_PICK1ST | IEEE80211_SCAN_GOTPICK)) == \ + IEEE80211_SCAN_PICK1ST) + struct sta_table *st = ss->ss_priv; + const uint8_t *macaddr = wh->i_addr2; + struct ieee80211com *ic = ss->ss_ic; + struct sta_entry *se; + struct ieee80211_scan_entry *ise; + int hash; + + hash = STA_HASH(macaddr); + + mtx_lock(&st->st_lock); + LIST_FOREACH(se, &st->st_hash[hash], se_hash) + if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) + goto found; + MALLOC(se, struct sta_entry *, sizeof(struct sta_entry), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (se == NULL) { + mtx_unlock(&st->st_lock); + return 0; + } + se->se_scangen = st->st_scangen-1; + IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr); + TAILQ_INSERT_TAIL(&st->st_entry, se, se_list); + LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash); +found: + ise = &se->base; + /* XXX ap beaconing multiple ssid w/ same bssid */ + if (sp->ssid[1] != 0 && + (ISPROBE(subtype) || ise->se_ssid[1] == 0)) + memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]); + KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE, + ("rate set too large: %u", sp->rates[1])); + memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]); + if (sp->xrates != NULL) { + /* XXX validate xrates[1] */ + KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE, + ("xrate set too large: %u", sp->xrates[1])); + memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]); + } else + ise->se_xrates[1] = 0; + IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3); + /* + * Record rssi data using extended precision LPF filter. + */ + if (se->se_lastupdate == 0) /* first sample */ + se->se_avgrssi = RSSI_IN(rssi); + else /* avg w/ previous samples */ + RSSI_LPF(se->se_avgrssi, rssi); + se->base.se_rssi = RSSI_GET(se->se_avgrssi); + se->base.se_noise = noise; + ise->se_rstamp = rstamp; + memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp)); + ise->se_intval = sp->bintval; + ise->se_capinfo = sp->capinfo; + ise->se_chan = ic->ic_curchan; + ise->se_fhdwell = sp->fhdwell; + ise->se_fhindex = sp->fhindex; + ise->se_erp = sp->erp; + ise->se_timoff = sp->timoff; + if (sp->tim != NULL) { + const struct ieee80211_tim_ie *tim = + (const struct ieee80211_tim_ie *) sp->tim; + ise->se_dtimperiod = tim->tim_period; + } + saveie(&ise->se_wme_ie, sp->wme); + saveie(&ise->se_wpa_ie, sp->wpa); + saveie(&ise->se_rsn_ie, sp->rsn); + saveie(&ise->se_ath_ie, sp->ath); + saveie(&ise->se_htcap_ie, sp->htcap); + saveie(&ise->se_htinfo_ie, sp->htinfo); + + /* clear failure count after STA_FAIL_AGE passes */ + if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) { + se->se_fails = 0; + IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr, + "%s: fails %u", __func__, se->se_fails); + } + + se->se_lastupdate = ticks; /* update time */ + se->se_seen = 1; + se->se_notseen = 0; + + mtx_unlock(&st->st_lock); + + /* + * If looking for a quick choice and nothing's + * been found check here. + */ + if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0) + ss->ss_flags |= IEEE80211_SCAN_GOTPICK; + + return 1; +#undef PICK1ST +#undef ISPROBE +} + +/* + * Check if a channel is excluded by user request. + */ +static int +isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c) +{ + return (isclr(ic->ic_chan_active, c->ic_ieee) || + (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + c->ic_freq != ic->ic_des_chan->ic_freq)); +} + +static struct ieee80211_channel * +find11gchannel(struct ieee80211com *ic, int i, int freq) +{ + struct ieee80211_channel *c; + int j; + + /* + * The normal ordering in the channel list is b channel + * immediately followed by g so optimize the search for + * this. We'll still do a full search just in case. + */ + for (j = i+1; j < ic->ic_nchans; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return c; + } + for (j = 0; j < i; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return c; + } + return NULL; +} +static const u_int chanflags[IEEE80211_MODE_MAX] = { + IEEE80211_CHAN_B, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_TURBO_A (check base channel)*/ + IEEE80211_CHAN_G, /* IEEE80211_MODE_TURBO_G */ + IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA (check legacy) */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG (check legacy) */ +}; + +static void +add_channels(struct ieee80211com *ic, + struct ieee80211_scan_state *ss, + enum ieee80211_phymode mode, const uint16_t freq[], int nfreq) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + struct ieee80211_channel *c, *cg; + u_int modeflags; + int i; + + KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); + modeflags = chanflags[mode]; + for (i = 0; i < nfreq; i++) { + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + + c = ieee80211_find_channel(ic, freq[i], modeflags); + if (c == NULL || isexcluded(ic, c)) + continue; + if (mode == IEEE80211_MODE_AUTO) { + /* + * XXX special-case 11b/g channels so we select + * the g channel if both are present. + */ + if (IEEE80211_IS_CHAN_B(c) && + (cg = find11gchannel(ic, i, c->ic_freq)) != NULL) + c = cg; + } + ss->ss_chans[ss->ss_last++] = c; + } +#undef N +} + +static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */ +{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 }; +static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */ +{ 5170, 5190, 5210, 5230 }; +static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */ +{ 2412, 2437, 2462, 2442, 2472 }; +static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */ +{ 5745, 5765, 5785, 5805, 5825 }; +static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */ +{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 }; +static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */ +{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 }; +static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */ +{ 2484 }; +static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */ +{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 }; +static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */ +{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 }; +#ifdef ATH_TURBO_SCAN +static const uint16_t rcl5[] = /* 3 static turbo channels */ +{ 5210, 5250, 5290 }; +static const uint16_t rcl6[] = /* 2 static turbo channels */ +{ 5760, 5800 }; +static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */ +{ 5540, 5580, 5620, 5660 }; +static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */ +{ 2437 }; +static const uint16_t rcl13[] = /* dynamic Turbo channels */ +{ 5200, 5240, 5280, 5765, 5805 }; +#endif /* ATH_TURBO_SCAN */ + +struct scanlist { + uint16_t mode; + uint16_t count; + const uint16_t *list; +}; + +#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a + +static const struct scanlist staScanTable[] = { + { IEEE80211_MODE_11B, X(rcl3) }, + { IEEE80211_MODE_11A, X(rcl1) }, + { IEEE80211_MODE_11A, X(rcl2) }, + { IEEE80211_MODE_11B, X(rcl8) }, + { IEEE80211_MODE_11B, X(rcl9) }, + { IEEE80211_MODE_11A, X(rcl4) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_STURBO_A, X(rcl5) }, + { IEEE80211_MODE_STURBO_A, X(rcl6) }, + { IEEE80211_MODE_TURBO_A, X(rcl6x) }, + { IEEE80211_MODE_TURBO_A, X(rcl13) }, +#endif /* ATH_TURBO_SCAN */ + { IEEE80211_MODE_11A, X(rcl7) }, + { IEEE80211_MODE_11B, X(rcl10) }, + { IEEE80211_MODE_11A, X(rcl11) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_TURBO_G, X(rcl12) }, +#endif /* ATH_TURBO_SCAN */ + { .list = NULL } +}; + +static int +checktable(const struct scanlist *scan, const struct ieee80211_channel *c) +{ + int i; + + for (; scan->list != NULL; scan++) { + for (i = 0; i < scan->count; i++) + if (scan->list[i] == c->ic_freq) + return 1; + } + return 0; +} + +/* + * Start a station-mode scan by populating the channel list. + */ +static int +sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct sta_table *st = ss->ss_priv; + const struct scanlist *scan; + enum ieee80211_phymode mode; + struct ieee80211_channel *c; + int i; + + ss->ss_last = 0; + /* + * Use the table of ordered channels to construct the list + * of channels for scanning. Any channels in the ordered + * list not in the master list will be discarded. + */ + for (scan = staScanTable; scan->list != NULL; scan++) { + mode = scan->mode; + if (ic->ic_des_mode != IEEE80211_MODE_AUTO) { + /* + * If a desired mode was specified, scan only + * channels that satisfy that constraint. + */ + if (ic->ic_des_mode != mode) { + /* + * The scan table marks 2.4Ghz channels as b + * so if the desired mode is 11g, then use + * the 11b channel list but upgrade the mode. + */ + if (ic->ic_des_mode != IEEE80211_MODE_11G || + mode != IEEE80211_MODE_11B) + continue; + mode = IEEE80211_MODE_11G; /* upgrade */ + } + } else { + /* + * This lets add_channels upgrade an 11b channel + * to 11g if available. + */ + if (mode == IEEE80211_MODE_11B) + mode = IEEE80211_MODE_AUTO; + } +#ifdef IEEE80211_F_XR + /* XR does not operate on turbo channels */ + if ((ic->ic_flags & IEEE80211_F_XR) && + (mode == IEEE80211_MODE_TURBO_A || + mode == IEEE80211_MODE_TURBO_G || + mode == IEEE80211_MODE_STURBO_A)) + continue; +#endif + /* + * Add the list of the channels; any that are not + * in the master channel list will be discarded. + */ + add_channels(ic, ss, mode, scan->list, scan->count); + } + + /* + * Add the channels from the ic (from HAL) that are not present + * in the staScanTable. + */ + for (i = 0; i < ic->ic_nchans; i++) { + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + + c = &ic->ic_channels[i]; + /* + * Ignore dynamic turbo channels; we scan them + * in normal mode (i.e. not boosted). Likewise + * for HT channels, they get scanned using + * legacy rates. + */ + if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c)) + continue; + + /* + * If a desired mode was specified, scan only + * channels that satisfy that constraint. + */ + if (ic->ic_des_mode != IEEE80211_MODE_AUTO && + ic->ic_des_mode != ieee80211_chan2mode(c)) + continue; + + /* + * Skip channels excluded by user request. + */ + if (isexcluded(ic, c)) + continue; + + /* + * Add the channel unless it is listed in the + * fixed scan order tables. This insures we + * don't sweep back in channels we filtered out + * above. + */ + if (checktable(staScanTable, c)) + continue; + + /* Add channel to scanning list. */ + ss->ss_chans[ss->ss_last++] = c; + } + + ss->ss_next = 0; + /* XXX tunables */ + ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */ + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + if_printf(ic->ic_ifp, "scan set "); + ieee80211_scan_dump_channels(ss); + printf(" dwell min %ld max %ld\n", + ss->ss_mindwell, ss->ss_maxdwell); + } +#endif /* IEEE80211_DEBUG */ + + st->st_newscan = 1; + + return 0; +#undef N +} + +/* + * Restart a bg scan. + */ +static int +sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct sta_table *st = ss->ss_priv; + + st->st_newscan = 1; + return 0; +} + +/* + * Cancel an ongoing scan. + */ +static int +sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + return 0; +} + +static uint8_t +maxrate(const struct ieee80211_scan_entry *se) +{ + uint8_t rmax, r; + int i; + + rmax = 0; + for (i = 0; i < se->se_rates[1]; i++) { + r = se->se_rates[2+i] & IEEE80211_RATE_VAL; + if (r > rmax) + rmax = r; + } + for (i = 0; i < se->se_xrates[1]; i++) { + r = se->se_xrates[2+i] & IEEE80211_RATE_VAL; + if (r > rmax) + rmax = r; + } + return rmax; +} + +/* + * Compare the capabilities of two entries and decide which is + * more desirable (return >0 if a is considered better). Note + * that we assume compatibility/usability has already been checked + * so we don't need to (e.g. validate whether privacy is supported). + * Used to select the best scan candidate for association in a BSS. + */ +static int +sta_compare(const struct sta_entry *a, const struct sta_entry *b) +{ +#define PREFER(_a,_b,_what) do { \ + if (((_a) ^ (_b)) & (_what)) \ + return ((_a) & (_what)) ? 1 : -1; \ +} while (0) + uint8_t maxa, maxb; + int8_t rssia, rssib; + int weight; + + /* desired bssid */ + PREFER(a->se_flags, b->se_flags, STA_BSSID_MATCH); + /* desired ssid */ + PREFER(a->se_flags, b->se_flags, STA_SSID_MATCH); + /* privacy support */ + PREFER(a->base.se_capinfo, b->base.se_capinfo, + IEEE80211_CAPINFO_PRIVACY); + + /* compare count of previous failures */ + weight = b->se_fails - a->se_fails; + if (abs(weight) > 1) + return weight; + + /* + * Compare rssi. If the two are considered equivalent + * then fallback to other criteria. We threshold the + * comparisons to avoid selecting an ap purely by rssi + * when both values may be good but one ap is otherwise + * more desirable (e.g. an 11b-only ap with stronger + * signal than an 11g ap). + */ + rssia = MIN(a->base.se_rssi, STA_RSSI_MAX); + rssib = MIN(b->base.se_rssi, STA_RSSI_MAX); + if (abs(rssib - rssia) < 5) { + /* best/max rate preferred if signal level close enough XXX */ + maxa = maxrate(&a->base); + maxb = maxrate(&b->base); + if (maxa != maxb) + return maxa - maxb; + /* XXX use freq for channel preference */ + /* for now just prefer 5Ghz band to all other bands */ + if (IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) && + !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) + return 1; + if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) && + IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) + return -1; + } + /* all things being equal, use signal level */ + return a->base.se_rssi - b->base.se_rssi; +#undef PREFER +} + +/* + * Check rate set suitability and return the best supported rate. + */ +static int +check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + const struct ieee80211_rateset *srs; + int i, j, nrs, r, okrate, badrate, fixedrate; + const uint8_t *rs; + + okrate = badrate = fixedrate = 0; + + srs = ieee80211_get_suprates(ic, se->se_chan); + nrs = se->se_rates[1]; + rs = se->se_rates+2; + fixedrate = IEEE80211_FIXED_RATE_NONE; +again: + for (i = 0; i < nrs; i++) { + r = RV(rs[i]); + badrate = r; + /* + * Check any fixed rate is included. + */ + if (r == ic->ic_fixed_rate) + fixedrate = r; + /* + * Check against our supported rates. + */ + for (j = 0; j < srs->rs_nrates; j++) + if (r == RV(srs->rs_rates[j])) { + if (r > okrate) /* NB: track max */ + okrate = r; + break; + } + + if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) { + /* + * Don't try joining a BSS, if we don't support + * one of its basic rates. + */ + okrate = 0; + goto back; + } + } + if (rs == se->se_rates+2) { + /* scan xrates too; sort of an algol68-style for loop */ + nrs = se->se_xrates[1]; + rs = se->se_xrates+2; + goto again; + } + +back: + if (okrate == 0 || ic->ic_fixed_rate != fixedrate) + return badrate | IEEE80211_RATE_BASIC; + else + return RV(okrate); +#undef RV +} + +static int +match_ssid(const uint8_t *ie, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + int i; + + for (i = 0; i < nssid; i++) { + if (ie[1] == ssids[i].len && + memcmp(ie+2, ssids[i].ssid, ie[1]) == 0) + return 1; + } + return 0; +} + +/* + * Test a scan candidate for suitability/compatibility. + */ +static int +match_bss(struct ieee80211com *ic, + const struct ieee80211_scan_state *ss, struct sta_entry *se0, + int debug) +{ + struct ieee80211_scan_entry *se = &se0->base; + uint8_t rate; + int fail; + + fail = 0; + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) + fail |= 0x01; + /* + * NB: normally the desired mode is used to construct + * the channel list, but it's possible for the scan + * cache to include entries for stations outside this + * list so we check the desired mode here to weed them + * out. + */ + if (ic->ic_des_mode != IEEE80211_MODE_AUTO && + (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) != + chanflags[ic->ic_des_mode]) + fail |= 0x01; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + fail |= 0x02; + } else { + if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0) + fail |= 0x02; + } + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + fail |= 0x04; + } else { + /* XXX does this mean privacy is supported or required? */ + if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) + fail |= 0x04; + } + rate = check_rate(ic, se); + if (rate & IEEE80211_RATE_BASIC) + fail |= 0x08; + if (ss->ss_nssid != 0 && + match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) + se0->se_flags |= STA_SSID_MATCH; + else + se0->se_flags &= ~STA_SSID_MATCH; + if ((ic->ic_flags & IEEE80211_F_DESBSSID) && + IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid)) + se0->se_flags |= STA_BSSID_MATCH; + else + se0->se_flags &= ~STA_BSSID_MATCH; + if (se0->se_fails >= STA_FAILS_MAX) + fail |= 0x40; + if (se0->se_notseen >= STA_PURGE_SCANS) + fail |= 0x80; + if (se->se_rssi < STA_RSSI_MIN) + fail |= 0x100; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg(ic, debug)) { + printf(" %c %s", + fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+', + ether_sprintf(se->se_macaddr)); + printf(" %s%c", ether_sprintf(se->se_bssid), + se0->se_flags & STA_BSSID_MATCH ? '*' : ' '); + printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan), + fail & 0x01 ? '!' : ' '); + printf(" %+4d%c", se->se_rssi, fail & 0x100 ? '!' : ' '); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & 0x08 ? '!' : ' '); + printf(" %4s%c", + (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : + "????", + fail & 0x02 ? '!' : ' '); + printf(" %3s%c ", + (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ? + "wep" : "no", + fail & 0x04 ? '!' : ' '); + ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]); + printf("%s\n", se0->se_flags & STA_SSID_MATCH ? "*" : ""); + } +#endif + return fail; +} + +static void +sta_update_notseen(struct sta_table *st) +{ + struct sta_entry *se; + + mtx_lock(&st->st_lock); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + /* + * If seen the reset and don't bump the count; + * otherwise bump the ``not seen'' count. Note + * that this insures that stations for which we + * see frames while not scanning but not during + * this scan will not be penalized. + */ + if (se->se_seen) + se->se_seen = 0; + else + se->se_notseen++; + } + mtx_unlock(&st->st_lock); +} + +static void +sta_dec_fails(struct sta_table *st) +{ + struct sta_entry *se; + + mtx_lock(&st->st_lock); + TAILQ_FOREACH(se, &st->st_entry, se_list) + if (se->se_fails) + se->se_fails--; + mtx_unlock(&st->st_lock); +} + +static struct sta_entry * +select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *selbs = NULL; + + IEEE80211_DPRINTF(ic, debug, " %s\n", + "macaddr bssid chan rssi rate flag wep essid"); + mtx_lock(&st->st_lock); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + if (match_bss(ic, ss, se, debug) == 0) { + if (selbs == NULL) + selbs = se; + else if (sta_compare(se, selbs) > 0) + selbs = se; + } + } + mtx_unlock(&st->st_lock); + + return selbs; +} + +/* + * Pick an ap or ibss network to join or find a channel + * to use to start an ibss network. + */ +static int +sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *selbs; + + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("wrong mode %u", ic->ic_opmode)); + + if (st->st_newscan) { + sta_update_notseen(st); + st->st_newscan = 0; + } + if (ss->ss_flags & IEEE80211_SCAN_NOPICK) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&st->st_entry) == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); +notfound: + /* + * If nothing suitable was found decrement + * the failure counts so entries will be + * reconsidered the next time around. We + * really want to do this only for sta's + * where we've previously had some success. + */ + sta_dec_fails(st); + st->st_newscan = 1; + return 0; /* restart scan */ + } + selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN); + if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base)) + goto notfound; + return 1; /* terminate scan */ +} + +/* + * Lookup an entry in the scan cache. We assume we're + * called from the bottom half or such that we don't need + * to block the bottom half so that it's safe to return + * a reference to an entry w/o holding the lock on the table. + */ +static struct sta_entry * +sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct sta_entry *se; + int hash = STA_HASH(macaddr); + + mtx_lock(&st->st_lock); + LIST_FOREACH(se, &st->st_hash[hash], se_hash) + if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) + break; + mtx_unlock(&st->st_lock); + + return se; /* NB: unlocked */ +} + +static void +sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct ieee80211_node *ni = ic->ic_bss; + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *selbs; + uint8_t roamRate, curRate; + int8_t roamRssi, curRssi; + + se = sta_lookup(st, ni->ni_macaddr); + if (se == NULL) { + /* XXX something is wrong */ + return; + } + + /* XXX do we need 11g too? */ + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { + roamRate = ic->ic_roam.rate11b; + roamRssi = ic->ic_roam.rssi11b; + } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) { + roamRate = ic->ic_roam.rate11bOnly; + roamRssi = ic->ic_roam.rssi11bOnly; + } else { + roamRate = ic->ic_roam.rate11a; + roamRssi = ic->ic_roam.rssi11a; + } + /* NB: the most up to date rssi is in the node, not the scan cache */ + curRssi = ic->ic_node_getrssi(ni); + if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { + curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM, + "%s: currssi %d currate %u roamrssi %d roamrate %u\n", + __func__, curRssi, curRate, roamRssi, roamRate); + } else { + curRate = roamRate; /* NB: insure compare below fails */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM, + "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); + } + /* + * Check if a new ap should be used and switch. + * XXX deauth current ap + */ + if (curRate < roamRate || curRssi < roamRssi) { + if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) { + /* + * Scan cache contents are too old; force a scan now + * if possible so we have current state to make a + * decision with. We don't kick off a bg scan if + * we're using dynamic turbo and boosted or if the + * channel is busy. + * XXX force immediate switch on scan complete + */ + if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && + time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)) + ieee80211_bg_scan(ic); + return; + } + se->base.se_rssi = curRssi; + selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM); + if (selbs != NULL && selbs != se) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG, + "%s: ROAM: curRate %u, roamRate %u, " + "curRssi %d, roamRssi %d\n", __func__, + curRate, roamRate, curRssi, roamRssi); + ieee80211_sta_join(ic, &selbs->base); + } + } +} + +/* + * Age entries in the scan cache. + * XXX also do roaming since it's convenient + */ +static void +sta_age(struct ieee80211_scan_state *ss) +{ + struct ieee80211com *ic = ss->ss_ic; + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *next; + + mtx_lock(&st->st_lock); + TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { + if (se->se_notseen > STA_PURGE_SCANS) { + TAILQ_REMOVE(&st->st_entry, se, se_list); + LIST_REMOVE(se, se_hash); + FREE(se, M_80211_SCAN); + } + } + mtx_unlock(&st->st_lock); + /* + * If rate control is enabled check periodically to see if + * we should roam from our current connection to one that + * might be better. This only applies when we're operating + * in sta mode and automatic roaming is set. + * XXX defer if busy + * XXX repeater station + * XXX do when !bgscan? + */ + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("wrong mode %u", ic->ic_opmode)); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO && + (ic->ic_flags & IEEE80211_F_BGSCAN) && + ic->ic_state >= IEEE80211_S_RUN) + /* XXX vap is implicit */ + sta_roam_check(ss, ic); +} + +/* + * Iterate over the entries in the scan cache, invoking + * the callback function on each one. + */ +static void +sta_iterate(struct ieee80211_scan_state *ss, + ieee80211_scan_iter_func *f, void *arg) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + u_int gen; + + mtx_lock(&st->st_scanlock); + gen = st->st_scangen++; +restart: + mtx_lock(&st->st_lock); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + if (se->se_scangen != gen) { + se->se_scangen = gen; + /* update public state */ + se->base.se_age = ticks - se->se_lastupdate; + mtx_unlock(&st->st_lock); + (*f)(arg, &se->base); + goto restart; + } + } + mtx_unlock(&st->st_lock); + + mtx_unlock(&st->st_scanlock); +} + +static void +sta_assoc_fail(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + + se = sta_lookup(st, macaddr); + if (se != NULL) { + se->se_fails++; + se->se_lastfail = ticks; + IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN, + macaddr, "%s: reason %u fails %u", + __func__, reason, se->se_fails); + } +} + +static void +sta_assoc_success(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + + se = sta_lookup(st, macaddr); + if (se != NULL) { +#if 0 + se->se_fails = 0; + IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN, + macaddr, "%s: fails %u", + __func__, se->se_fails); +#endif + se->se_lastassoc = ticks; + } +} + +static const struct ieee80211_scanner sta_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = sta_start, + .scan_restart = sta_restart, + .scan_cancel = sta_cancel, + .scan_end = sta_pick_bss, + .scan_flush = sta_flush, + .scan_add = sta_add, + .scan_age = sta_age, + .scan_iterate = sta_iterate, + .scan_assoc_fail = sta_assoc_fail, + .scan_assoc_success = sta_assoc_success, +}; + +/* + * Adhoc mode-specific support. + */ + +static const uint16_t adhocWorld[] = /* 36, 40, 44, 48 */ +{ 5180, 5200, 5220, 5240 }; +static const uint16_t adhocFcc3[] = /* 36, 40, 44, 48 145, 149, 153, 157, 161, 165 */ +{ 5180, 5200, 5220, 5240, 5725, 5745, 5765, 5785, 5805, 5825 }; +static const uint16_t adhocMkk[] = /* 34, 38, 42, 46 */ +{ 5170, 5190, 5210, 5230 }; +static const uint16_t adhoc11b[] = /* 10, 11 */ +{ 2457, 2462 }; + +static const struct scanlist adhocScanTable[] = { + { IEEE80211_MODE_11B, X(adhoc11b) }, + { IEEE80211_MODE_11A, X(adhocWorld) }, + { IEEE80211_MODE_11A, X(adhocFcc3) }, + { IEEE80211_MODE_11B, X(adhocMkk) }, + { .list = NULL } +}; +#undef X + +/* + * Start an adhoc-mode scan by populating the channel list. + */ +static int +adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct sta_table *st = ss->ss_priv; + const struct scanlist *scan; + enum ieee80211_phymode mode; + + ss->ss_last = 0; + /* + * Use the table of ordered channels to construct the list + * of channels for scanning. Any channels in the ordered + * list not in the master list will be discarded. + */ + for (scan = adhocScanTable; scan->list != NULL; scan++) { + mode = scan->mode; + if (ic->ic_des_mode != IEEE80211_MODE_AUTO) { + /* + * If a desired mode was specified, scan only + * channels that satisfy that constraint. + */ + if (ic->ic_des_mode != mode) { + /* + * The scan table marks 2.4Ghz channels as b + * so if the desired mode is 11g, then use + * the 11b channel list but upgrade the mode. + */ + if (ic->ic_des_mode != IEEE80211_MODE_11G || + mode != IEEE80211_MODE_11B) + continue; + mode = IEEE80211_MODE_11G; /* upgrade */ + } + } else { + /* + * This lets add_channels upgrade an 11b channel + * to 11g if available. + */ + if (mode == IEEE80211_MODE_11B) + mode = IEEE80211_MODE_AUTO; + } +#ifdef IEEE80211_F_XR + /* XR does not operate on turbo channels */ + if ((ic->ic_flags & IEEE80211_F_XR) && + (mode == IEEE80211_MODE_TURBO_A || + mode == IEEE80211_MODE_TURBO_G)) + continue; +#endif + /* + * Add the list of the channels; any that are not + * in the master channel list will be discarded. + */ + add_channels(ic, ss, mode, scan->list, scan->count); + } + ss->ss_next = 0; + /* XXX tunables */ + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + if_printf(ic->ic_ifp, "scan set "); + ieee80211_scan_dump_channels(ss); + printf(" dwell min %ld max %ld\n", + ss->ss_mindwell, ss->ss_maxdwell); + } +#endif /* IEEE80211_DEBUG */ + + st->st_newscan = 1; + + return 0; +#undef N +} + +/* + * Select a channel to start an adhoc network on. + * The channel list was populated with appropriate + * channels so select one that looks least occupied. + * XXX need regulatory domain constraints + */ +static struct ieee80211_channel * +adhoc_pick_channel(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + struct ieee80211_channel *c, *bestchan; + int i, bestrssi, maxrssi; + + bestchan = NULL; + bestrssi = -1; + + mtx_lock(&st->st_lock); + for (i = 0; i < ss->ss_last; i++) { + c = ss->ss_chans[i]; + maxrssi = 0; + TAILQ_FOREACH(se, &st->st_entry, se_list) { + if (se->base.se_chan != c) + continue; + if (se->base.se_rssi > maxrssi) + maxrssi = se->base.se_rssi; + } + if (bestchan == NULL || maxrssi < bestrssi) + bestchan = c; + } + mtx_unlock(&st->st_lock); + + return bestchan; +} + +/* + * Pick an ibss network to join or find a channel + * to use to start an ibss network. + */ +static int +adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *selbs; + struct ieee80211_channel *chan; + + KASSERT(ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO, + ("wrong opmode %u", ic->ic_opmode)); + + if (st->st_newscan) { + sta_update_notseen(st); + st->st_newscan = 0; + } + if (ss->ss_flags & IEEE80211_SCAN_NOPICK) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&st->st_entry) == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); +notfound: + if (ic->ic_des_nssid) { + /* + * No existing adhoc network to join and we have + * an ssid; start one up. If no channel was + * specified, try to select a channel. + */ + if (ic->ic_des_chan == IEEE80211_CHAN_ANYC) + chan = adhoc_pick_channel(ss); + else + chan = ic->ic_des_chan; + if (chan != NULL) { + ieee80211_create_ibss(ic, chan); + return 1; + } + } + /* + * If nothing suitable was found decrement + * the failure counts so entries will be + * reconsidered the next time around. We + * really want to do this only for sta's + * where we've previously had some success. + */ + sta_dec_fails(st); + st->st_newscan = 1; + return 0; /* restart scan */ + } + selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN); + if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base)) + goto notfound; + return 1; /* terminate scan */ +} + +/* + * Age entries in the scan cache. + */ +static void +adhoc_age(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *next; + + mtx_lock(&st->st_lock); + TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { + if (se->se_notseen > STA_PURGE_SCANS) { + TAILQ_REMOVE(&st->st_entry, se, se_list); + LIST_REMOVE(se, se_hash); + FREE(se, M_80211_SCAN); + } + } + mtx_unlock(&st->st_lock); +} + +static const struct ieee80211_scanner adhoc_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = adhoc_start, + .scan_restart = sta_restart, + .scan_cancel = sta_cancel, + .scan_end = adhoc_pick_bss, + .scan_flush = sta_flush, + .scan_add = sta_add, + .scan_age = adhoc_age, + .scan_iterate = sta_iterate, + .scan_assoc_fail = sta_assoc_fail, + .scan_assoc_success = sta_assoc_success, +}; + +/* + * Module glue. + */ +static int +wlan_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_scanner_register(IEEE80211_M_STA, &sta_default); + ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default); + ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default); + return 0; + case MOD_UNLOAD: + case MOD_QUIESCE: + if (nrefs) { + printf("wlan_scan_sta: still in use (%u dynamic refs)\n", + nrefs); + return EBUSY; + } + if (type == MOD_UNLOAD) { + ieee80211_scanner_unregister_all(&sta_default); + ieee80211_scanner_unregister_all(&adhoc_default); + } + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_mod = { + "wlan_scan_sta", + wlan_modevent, + 0 +}; +DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_scan_sta, 1); +MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index b95579f0621a..0b1e70c73e40 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -50,7 +50,9 @@ #include <net80211/ieee80211_crypto.h> #include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */ #include <net80211/ieee80211_node.h> +#include <net80211/ieee80211_power.h> #include <net80211/ieee80211_proto.h> +#include <net80211/ieee80211_scan.h> #define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ #define IEEE80211_TXPOWER_MIN 0 /* kill radio */ @@ -61,6 +63,15 @@ #define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */ #define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */ +#define IEEE80211_BGSCAN_INTVAL_MIN 15 /* min bg scan intvl (secs) */ +#define IEEE80211_BGSCAN_INTVAL_DEFAULT (5*60) /* default bg scan intvl */ + +#define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */ +#define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */ + +#define IEEE80211_SCAN_VALID_MIN 10 /* min scan valid time (secs) */ +#define IEEE80211_SCAN_VALID_DEFAULT 60 /* default scan valid time */ + #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ @@ -80,110 +91,132 @@ struct sysctl_ctx_list; struct ieee80211com { SLIST_ENTRY(ieee80211com) ic_next; struct ifnet *ic_ifp; /* associated device */ + ieee80211_com_lock_t ic_comlock; /* state update lock */ + ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */ struct ieee80211_stats ic_stats; /* statistics */ struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */ - u_int32_t ic_debug; /* debug msg flags */ + uint32_t ic_debug; /* debug msg flags */ int ic_vap; /* virtual AP index */ - ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */ + int ic_headroom; /* driver tx headroom needs */ + enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ + enum ieee80211_opmode ic_opmode; /* operation mode */ + struct ifmedia ic_media; /* interface media config */ + uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; - int (*ic_reset)(struct ifnet *); - void (*ic_recv_mgmt)(struct ieee80211com *, - struct mbuf *, struct ieee80211_node *, - int, int, u_int32_t); - int (*ic_send_mgmt)(struct ieee80211com *, - struct ieee80211_node *, int, int); - int (*ic_newstate)(struct ieee80211com *, - enum ieee80211_state, int); - void (*ic_newassoc)(struct ieee80211_node *, int); - void (*ic_updateslot)(struct ifnet *); - void (*ic_set_tim)(struct ieee80211_node *, int); - int (*ic_raw_xmit)(struct ieee80211_node *, - struct mbuf *, - const struct ieee80211_bpf_params *); - u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; + uint32_t ic_flags; /* state flags */ + uint32_t ic_flags_ext; /* extended state flags */ + uint32_t ic_caps; /* capabilities */ + uint8_t ic_modecaps[2]; /* set of mode capabilities */ + uint16_t ic_curmode; /* current mode */ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; + uint16_t ic_bintval; /* beacon interval */ + uint16_t ic_lintval; /* listen interval */ + uint16_t ic_holdover; /* PM hold over duration */ + uint16_t ic_txpowlimit; /* global tx power limit */ + uint32_t ic_htcaps; /* HT capabilities */ + int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */ + int ic_ampdu_density;/* A-MPDU density */ + int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */ + int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */ + + /* + * Channel state: + * + * ic_channels is the set of available channels for the device; + * it is setup by the driver + * ic_nchans is the number of valid entries in ic_channels + * ic_chan_avail is a bit vector of these channels used to check + * whether a channel is available w/o searching the channel table. + * ic_chan_active is a (potentially) constrained subset of + * ic_chan_avail that reflects any mode setting or user-specified + * limit on the set of channels to use/scan + * ic_curchan is the current channel the device is set to; it may + * be different from ic_bsschan when we are off-channel scanning + * or otherwise doing background work + * ic_bsschan is the channel selected for operation; it may + * be undefined (IEEE80211_CHAN_ANYC) + * ic_prevchan is a cached ``previous channel'' used to optimize + * lookups when switching back+forth between two channels + * (e.g. for dynamic turbo) + */ + int ic_nchans; /* # entries in ic_channels */ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; - u_int8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; - u_int8_t ic_chan_active[IEEE80211_CHAN_BYTES]; - u_int8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; - struct ieee80211_node_table ic_scan; /* scan candidates */ - struct ifqueue ic_mgtq; - u_int32_t ic_flags; /* state flags */ - u_int32_t ic_flags_ext; /* extended state flags */ - u_int32_t ic_caps; /* capabilities */ - u_int8_t ic_modecaps[2]; /* set of mode capabilities */ - u_int16_t ic_curmode; /* current mode */ - enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ - enum ieee80211_opmode ic_opmode; /* operation mode */ - enum ieee80211_state ic_state; /* 802.11 state */ - enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; + struct ieee80211_channel *ic_curchan; /* current channel */ + struct ieee80211_channel *ic_bsschan; /* bss channel */ + struct ieee80211_channel *ic_prevchan; /* previous channel */ + int ic_countrycode; /* ISO country code */ + uint16_t ic_regdomain; /* regulatory domain */ + uint8_t ic_location; /* unknown, indoor, outdoor */ + + struct ieee80211_scan_state *ic_scan; /* scan state */ enum ieee80211_roamingmode ic_roaming; /* roaming mode */ + int ic_lastdata; /* time of last data frame */ + int ic_lastscan; /* time last scan completed */ + int ic_des_nssid; /* # desired ssids */ + struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */ + uint8_t ic_des_bssid[IEEE80211_ADDR_LEN]; + struct ieee80211_channel *ic_des_chan; /* desired channel */ + int ic_des_mode; /* desired phymode */ + u_int ic_bgscanidle; /* bg scan idle threshold */ + u_int ic_bgscanintvl; /* bg scan min interval */ + u_int ic_scanvalid; /* scan cache valid threshold */ + struct ieee80211_roam ic_roam; /* sta-mode roaming state */ + struct ieee80211_node_table ic_sta; /* stations/neighbors */ - u_int32_t *ic_aid_bitmap; /* association id map */ - u_int16_t ic_max_aid; - u_int16_t ic_sta_assoc; /* stations associated */ - u_int16_t ic_ps_sta; /* stations in power save */ - u_int16_t ic_ps_pending; /* ps sta's w/ pending frames */ - u_int8_t *ic_tim_bitmap; /* power-save stations w/ data*/ - u_int16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */ - u_int8_t ic_dtim_period; /* DTIM period */ - u_int8_t ic_dtim_count; /* DTIM count for last bcn */ - struct ifmedia ic_media; /* interface media config */ + + struct ieee80211_wme_state ic_wme; /* WME/WMM state */ + const struct ieee80211_aclator *ic_acl; /* aclator glue */ + void *ic_as; /* private aclator state */ + + enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + uint16_t ic_nonerpsta; /* # non-ERP stations */ + uint16_t ic_longslotsta; /* # long slot time stations */ + uint16_t ic_sta_assoc; /* stations associated */ + + struct ifqueue ic_mgtq; + enum ieee80211_state ic_state; /* 802.11 state */ + struct callout ic_mgtsend; /* mgmt frame response timer */ + uint32_t *ic_aid_bitmap; /* association id map */ + uint16_t ic_max_aid; + uint16_t ic_ps_sta; /* stations in power save */ + uint16_t ic_ps_pending; /* ps sta's w/ pending frames */ + uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/ + uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */ + uint8_t ic_dtim_period; /* DTIM period */ + uint8_t ic_dtim_count; /* DTIM count for last bcn */ struct bpf_if *ic_rawbpf; /* packet filter structure */ struct ieee80211_node *ic_bss; /* information for this node */ - struct ieee80211_channel *ic_ibss_chan; - struct ieee80211_channel *ic_curchan; /* current channel */ - int ic_fixed_rate; /* index to ic_sup_rates[] */ + int ic_fixed_rate; /* 802.11 rate or -1 */ int ic_mcast_rate; /* rate for mcast frames */ - u_int16_t ic_rtsthreshold; - u_int16_t ic_fragthreshold; - u_int8_t ic_bmissthreshold; - u_int8_t ic_bmiss_count; /* current beacon miss count */ + uint16_t ic_rtsthreshold; + uint16_t ic_fragthreshold; + uint8_t ic_bmissthreshold; + uint8_t ic_bmiss_count; /* current beacon miss count */ int ic_bmiss_max; /* max bmiss before scan */ - u_int16_t ic_swbmiss_count;/* beacons in last period */ - u_int16_t ic_swbmiss_period;/* s/w bmiss period */ + uint16_t ic_swbmiss_count;/* beacons in last period */ + uint16_t ic_swbmiss_period;/* s/w bmiss period */ struct callout ic_swbmiss; /* s/w beacon miss timer */ - struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); - void (*ic_node_free)(struct ieee80211_node *); - void (*ic_node_cleanup)(struct ieee80211_node *); - u_int8_t (*ic_node_getrssi)(const struct ieee80211_node*); - u_int16_t ic_lintval; /* listen interval */ - u_int16_t ic_bintval; /* beacon interval */ - u_int16_t ic_holdover; /* PM hold over duration */ - u_int16_t ic_txmin; /* min tx retry count */ - u_int16_t ic_txmax; /* max tx retry count */ - u_int16_t ic_txlifetime; /* tx lifetime */ - u_int16_t ic_txpowlimit; /* global tx power limit */ - u_int16_t ic_nonerpsta; /* # non-ERP stations */ - u_int16_t ic_longslotsta; /* # long slot time stations */ - int ic_mgt_timer; /* mgmt timeout */ - int ic_inact_timer; /* inactivity timer wait */ - int ic_des_esslen; - u_int8_t ic_des_essid[IEEE80211_NWID_LEN]; - struct ieee80211_channel *ic_des_chan; /* desired channel */ - u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; + + uint16_t ic_txmin; /* min tx retry count */ + uint16_t ic_txmax; /* max tx retry count */ + uint16_t ic_txlifetime; /* tx lifetime */ + struct callout ic_inact; /* inactivity timer wait */ void *ic_opt_ie; /* user-specified IE's */ - u_int16_t ic_opt_ie_len; /* length of ni_opt_ie */ - /* - * Inactivity timer settings for nodes. - */ + uint16_t ic_opt_ie_len; /* length of ni_opt_ie */ int ic_inact_init; /* initial setting */ int ic_inact_auth; /* auth but not assoc setting */ int ic_inact_run; /* authorized setting */ int ic_inact_probe; /* inactive probe time */ /* - * WME/WMM state. - */ - struct ieee80211_wme_state ic_wme; - - /* * Cipher state/configuration. */ struct ieee80211_crypto_state ic_crypto; #define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */ #define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */ - /* * 802.1x glue. When an authenticator attaches it * fills in this section. We assume that when ic_ec @@ -192,13 +225,64 @@ struct ieee80211com { const struct ieee80211_authenticator *ic_auth; struct eapolcom *ic_ec; + /* send/recv 802.11 management frame */ + int (*ic_send_mgmt)(struct ieee80211com *, + struct ieee80211_node *, int, int); + void (*ic_recv_mgmt)(struct ieee80211com *, + struct mbuf *, struct ieee80211_node *, + int, int, int, uint32_t); + /* send raw 802.11 frame */ + int (*ic_raw_xmit)(struct ieee80211_node *, + struct mbuf *, + const struct ieee80211_bpf_params *); + /* reset device state after 802.11 parameter/state change */ + int (*ic_reset)(struct ifnet *); + /* update device state for 802.11 slot time change */ + void (*ic_updateslot)(struct ifnet *); + /* new station association callback/notification */ + void (*ic_newassoc)(struct ieee80211_node *, int); + /* node state management */ + struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); + void (*ic_node_free)(struct ieee80211_node *); + void (*ic_node_cleanup)(struct ieee80211_node *); + int8_t (*ic_node_getrssi)(const struct ieee80211_node*); + void (*ic_node_getsignal)(const struct ieee80211_node*, + int8_t *, int8_t *); + /* scanning support */ + void (*ic_scan_start)(struct ieee80211com *); + void (*ic_scan_end)(struct ieee80211com *); + void (*ic_set_channel)(struct ieee80211com *); + void (*ic_scan_curchan)(struct ieee80211com *, + unsigned long); + void (*ic_scan_mindwell)(struct ieee80211com *); + /* per-vap eventually... */ + int (*ic_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*ic_set_tim)(struct ieee80211_node *, int); + /* - * Access control glue. When a control agent attaches - * it fills in this section. We assume that when ic_ac - * is setup that the methods are safe to call. + * 802.11n ADDBA support. A simple/generic implementation + * of A-MPDU tx aggregation is provided; the driver may + * override these methods to provide their own support. + * A-MPDU rx re-ordering happens automatically if the + * driver passes out-of-order frames to ieee80211_input + * from an assocated HT station. */ - const struct ieee80211_aclator *ic_acl; - void *ic_as; + void (*ic_recv_action)(struct ieee80211_node *, + const uint8_t *frm, const uint8_t *efrm); + int (*ic_send_action)(struct ieee80211_node *, + int category, int action, + uint16_t args[4]); + /* start/stop doing A-MPDU tx aggregation for a station */ + int (*ic_addba_request)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, + int dialogtoken, int baparamset, + int batimeout); + int (*ic_addba_response)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, + int status, int baparamset, int batimeout); + void (*ic_addba_stop)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); }; #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) @@ -206,9 +290,10 @@ struct ieee80211com { /* ic_flags */ /* NB: bits 0x4c available */ -#define IEEE80211_F_FF 0x00000001 /* CONF: ATH FF enabled */ -#define IEEE80211_F_TURBOP 0x00000002 /* CONF: ATH Turbo enabled*/ -#define IEEE80211_F_BURST 0x00000004 /* CONF: bursting enabled */ +#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/ +#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */ +#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */ +#define IEEE80211_F_BURST 0x00000008 /* CONF: bursting enabled */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ #define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ #define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */ @@ -237,14 +322,32 @@ struct ieee80211com { #define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ #define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ #define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */ +#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */ + +/* Atheros protocol-specific flags */ +#define IEEE80211_F_ATHEROS \ + (IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP) +/* Check if an Atheros capability was negotiated for use */ +#define IEEE80211_ATH_CAP(ic, ni, bit) \ + ((ic)->ic_flags & (ni)->ni_ath_flags & (bit)) /* ic_flags_ext */ -#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */ +#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */ /* 0x00000006 reserved */ -#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: enable full bgscan completion */ +#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */ #define IEEE80211_FEXT_ERPUPDATE 0x00000200 /* STATUS: update ERP element */ -#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ +#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ #define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ +#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */ +#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */ +#define IEEE80211_FEXT_AMPDU_RX 0x00200000 /* CONF: A-MPDU tx supported */ +#define IEEE80211_FEXT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */ +#define IEEE80211_FEXT_AMSDU_RX 0x00800000 /* CONF: A-MSDU tx supported */ +#define IEEE80211_FEXT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */ +#define IEEE80211_FEXT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */ +#define IEEE80211_FEXT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */ +#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */ +#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */ /* ic_caps */ #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ @@ -277,29 +380,40 @@ struct ieee80211com { #define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ +/* + * ic_htcaps: HT-specific device/driver capabilities + * + * NB: the low 16-bits are the 802.11 definitions, the upper + * 16-bits are used to define s/w/driver capabilities. + */ +#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */ +#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */ + void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); -const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *, +const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *); void ieee80211_announce(struct ieee80211com *); +void ieee80211_announce_channels(struct ieee80211com *); void ieee80211_media_init(struct ieee80211com *, ifm_change_cb_t, ifm_stat_cb_t); -struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]); +struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t); int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t); int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t); -void ieee80211_watchdog(struct ieee80211com *); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); int ieee80211_mhz2ieee(u_int, u_int); -int ieee80211_chan2ieee(struct ieee80211com *, const struct ieee80211_channel *); +int ieee80211_chan2ieee(struct ieee80211com *, + const struct ieee80211_channel *); u_int ieee80211_ieee2mhz(u_int, u_int); +struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *, + int freq, int flags); int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); -enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, - const struct ieee80211_channel *); +enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *); /* * Key update synchronization methods. XXX should not be visible. @@ -329,7 +443,7 @@ ieee80211_hdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_hdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) - size = roundup(size, sizeof(u_int32_t)); + size = roundup(size, sizeof(uint32_t)); return size; } @@ -341,10 +455,11 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_anyhdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) - size = roundup(size, sizeof(u_int32_t)); + size = roundup(size, sizeof(uint32_t)); return size; } +#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */ #define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ #define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ #define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ @@ -371,6 +486,7 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) #define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */ #define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */ #define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */ +#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */ #define IEEE80211_MSG_ANY 0xffffffff /* anything */ @@ -394,7 +510,7 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) } while (0) void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...); void ieee80211_note_mac(struct ieee80211com *ic, - const u_int8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...); + const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...); void ieee80211_note_frame(struct ieee80211com *ic, const struct ieee80211_frame *wh, const char *fmt, ...); #define ieee80211_msg_debug(_ic) \ @@ -438,10 +554,11 @@ void ieee80211_discard_frame(struct ieee80211com *, void ieee80211_discard_ie(struct ieee80211com *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); void ieee80211_discard_mac(struct ieee80211com *, - const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type, + const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...); #else #define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) +#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) #define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) #define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) #define ieee80211_msg_dumppkts(_ic) 0 diff --git a/sys/sys/param.h b/sys/sys/param.h index 42f935bb11e8..23faf5eabb9c 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -57,7 +57,7 @@ * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 700045 /* Master, propagated to newvers */ +#define __FreeBSD_version 700046 /* Master, propagated to newvers */ #ifndef LOCORE #include <sys/types.h> |