aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/NOTES2
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/ath/ath_rate/amrr/amrr.c20
-rw-r--r--sys/dev/ath/ath_rate/onoe/onoe.c20
-rw-r--r--sys/dev/ath/ath_rate/sample/sample.c21
-rw-r--r--sys/dev/ath/if_ath.c1089
-rw-r--r--sys/dev/ath/if_athioctl.h38
-rw-r--r--sys/dev/ath/if_athvar.h56
-rw-r--r--sys/dev/awi/awi.c55
-rw-r--r--sys/dev/awi/awivar.h2
-rw-r--r--sys/dev/if_ndis/if_ndis.c35
-rw-r--r--sys/dev/ipw/if_ipw.c37
-rw-r--r--sys/dev/iwi/if_iwi.c866
-rw-r--r--sys/dev/iwi/if_iwireg.h6
-rw-r--r--sys/dev/iwi/if_iwivar.h74
-rw-r--r--sys/dev/ral/if_ral_pci.c16
-rw-r--r--sys/dev/ral/rt2560.c318
-rw-r--r--sys/dev/ral/rt2560reg.h4
-rw-r--r--sys/dev/ral/rt2560var.h13
-rw-r--r--sys/dev/ral/rt2661.c304
-rw-r--r--sys/dev/ral/rt2661reg.h2
-rw-r--r--sys/dev/ral/rt2661var.h10
-rw-r--r--sys/dev/usb/if_rum.c272
-rw-r--r--sys/dev/usb/if_rumreg.h2
-rw-r--r--sys/dev/usb/if_rumvar.h7
-rw-r--r--sys/dev/usb/if_ural.c230
-rw-r--r--sys/dev/usb/if_uralreg.h3
-rw-r--r--sys/dev/usb/if_uralvar.h9
-rw-r--r--sys/dev/wi/if_wi.c370
-rw-r--r--sys/dev/wi/if_wivar.h10
-rw-r--r--sys/kern/subr_witness.c12
-rw-r--r--sys/modules/Makefile2
-rw-r--r--sys/modules/wlan_scan_ap/Makefile8
-rw-r--r--sys/modules/wlan_scan_sta/Makefile8
-rw-r--r--sys/net/if_media.h10
-rw-r--r--sys/net80211/_ieee80211.h125
-rw-r--r--sys/net80211/ieee80211.c845
-rw-r--r--sys/net80211/ieee80211.h621
-rw-r--r--sys/net80211/ieee80211_acl.c12
-rw-r--r--sys/net80211/ieee80211_amrr.c6
-rw-r--r--sys/net80211/ieee80211_crypto.c16
-rw-r--r--sys/net80211/ieee80211_crypto.h24
-rw-r--r--sys/net80211/ieee80211_crypto_ccmp.c48
-rw-r--r--sys/net80211/ieee80211_crypto_none.c6
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c38
-rw-r--r--sys/net80211/ieee80211_crypto_wep.c52
-rw-r--r--sys/net80211/ieee80211_freebsd.c62
-rw-r--r--sys/net80211/ieee80211_freebsd.h68
-rw-r--r--sys/net80211/ieee80211_ht.c1472
-rw-r--r--sys/net80211/ieee80211_ht.h113
-rw-r--r--sys/net80211/ieee80211_input.c967
-rw-r--r--sys/net80211/ieee80211_ioctl.c1902
-rw-r--r--sys/net80211/ieee80211_ioctl.h424
-rw-r--r--sys/net80211/ieee80211_node.c1145
-rw-r--r--sys/net80211/ieee80211_node.h220
-rw-r--r--sys/net80211/ieee80211_output.c850
-rw-r--r--sys/net80211/ieee80211_power.c328
-rw-r--r--sys/net80211/ieee80211_power.h43
-rw-r--r--sys/net80211/ieee80211_proto.c525
-rw-r--r--sys/net80211/ieee80211_proto.h79
-rw-r--r--sys/net80211/ieee80211_radiotap.h69
-rw-r--r--sys/net80211/ieee80211_regdomain.c337
-rw-r--r--sys/net80211/ieee80211_regdomain.h175
-rw-r--r--sys/net80211/ieee80211_scan.c990
-rw-r--r--sys/net80211/ieee80211_scan.h218
-rw-r--r--sys/net80211/ieee80211_scan_ap.c407
-rw-r--r--sys/net80211/ieee80211_scan_sta.c1438
-rw-r--r--sys/net80211/ieee80211_var.h317
-rw-r--r--sys/sys/param.h2
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>