summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2007-06-11 03:36:55 +0000
committersam <sam@FreeBSD.org>2007-06-11 03:36:55 +0000
commit6a8b18f11591df29764d02a686661d87559bf7f6 (patch)
treee13543d4810ca316d27ca22651054eea880c1d34 /sys
parenta9a2aaf8ad63975b1a3595ae20df675a7c6336c6 (diff)
downloadFreeBSD-src-6a8b18f11591df29764d02a686661d87559bf7f6.zip
FreeBSD-src-6a8b18f11591df29764d02a686661d87559bf7f6.tar.gz
Update 802.11 wireless support:
o major overhaul of the way channels are handled: channels are now fully enumerated and uniquely identify the operating characteristics; these changes are visible to user applications which require changes o make scanning support independent of the state machine to enable background scanning and roaming o move scanning support into loadable modules based on the operating mode to enable different policies and reduce the memory footprint on systems w/ constrained resources o add background scanning in station mode (no support for adhoc/ibss mode yet) o significantly speedup sta mode scanning with a variety of techniques o add roaming support when background scanning is supported; for now we use a simple algorithm to trigger a roam: we threshold the rssi and tx rate, if either drops too low we try to roam to a new ap o add tx fragmentation support o add first cut at 802.11n support: this code works with forthcoming drivers but is incomplete; it's included now to establish a baseline for other drivers to be developed and for user applications o adjust max_linkhdr et. al. to reflect 802.11 requirements; this eliminates prepending mbufs for traffic generated locally o add support for Atheros protocol extensions; mainly the fast frames encapsulation (note this can be used with any card that can tx+rx large frames correctly) o add sta support for ap's that beacon both WPA1+2 support o change all data types from bsd-style to posix-style o propagate noise floor data from drivers to net80211 and on to user apps o correct various issues in the sta mode state machine related to handling authentication and association failures o enable the addition of sta mode power save support for drivers that need net80211 support (not in this commit) o remove old WI compatibility ioctls (wicontrol is officially dead) o change the data structures returned for get sta info and get scan results so future additions will not break user apps o fixed tx rate is now maintained internally as an ieee rate and not an index into the rate set; this needs to be extended to deal with multi-mode operation o add extended channel specifications to radiotap to enable 11n sniffing Drivers: o ath: add support for bg scanning, tx fragmentation, fast frames, dynamic turbo (lightly tested), 11n (sniffing only and needs new hal) o awi: compile tested only o ndis: lightly tested o ipw: lightly tested o iwi: add support for bg scanning (well tested but may have some rough edges) o ral, ural, rum: add suppoort for bg scanning, calibrate rssi data o wi: lightly tested This work is based on contributions by Atheros, kmacy, sephe, thompsa, mlaier, kevlo, and others. Much of the scanning work was supported by Atheros. The 11n work was supported by Marvell.
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 454543b..08ea41d 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 fde91d2..c08878a 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 fc72abf..da564a8 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 b682a47..281e4f1 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 6cf564e..180ef82 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 caaa939..2841e8e 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 9f48ac2..ccd7220 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 7fd700e..af1045d 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 6b14f46..33f0a90 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 f805b8f..762d1da 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 0366398..f4a3e8c 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 974bcc9..2dca3cd 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 c694c92..ae9aeb6 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 5c7435e..fb56fa5 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 f652c4d..a1c3915 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 3d9976d..2ceafe4 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 36a849d..f09c4f3 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 6b1cf0d..3871ed3 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 50112b5..c01abb1 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 8f13eb6..224da32 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 d79f926..b4325b0 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 fab2892..9f12a15 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 2f22117..16ed1ad 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 7827775..75a51bc 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 1577c6c..e7d6d15 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 46e2f58..4ca311b 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 70e06f1..428089f 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 18b5cdf..665b05b 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 ebe35d2..aeb8a4e 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 f7159af..88238a3 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 2310016..31f306e 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 748a92e..e0522ba 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 0000000..117a6bc
--- /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 0000000..8d96875
--- /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 89bfe4f..eb77d2b 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 053de92..8747bfc 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 1dd4ce4..cc570f3 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 6783a21..c697c5b 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 9d771a5..c5305d0 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 b54fd2a..5b34768 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 3d6d22f..83d7c3f 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 ed12b06..ee99caa 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 0965de8..525fe9a 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 c3be2e4..7fbb53d 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 05c1532..327306a 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 f6888b2..81d15cc 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 c51bb69..d75b2d2 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 dc4cd95..4b9fc70 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 0000000..9b6223c
--- /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 0000000..fc87f1f
--- /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 7219668..a5b5341 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 17befd2..5929edd 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 28dba03..2e17000 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 9394c0f..690e54b 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 ed09ae5..cb960d1 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 d9e92a3..5b08f68 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 0000000..2064807
--- /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 0000000..c8461f6
--- /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 458c168..40fef21 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 f650cf5..b00c8ec 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 fea0bbe..a52f6a3 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 0000000..257884f
--- /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 0000000..9c1345e
--- /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 0000000..6099744
--- /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 0000000..4881aea
--- /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 0000000..9aa09ed
--- /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 0000000..7bd04c8
--- /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 b95579f..0b1e70c 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 42f935b..23faf5e 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>
OpenPOWER on IntegriCloud