aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c379
1 files changed, 200 insertions, 179 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 20c680bfc3ae..b3b3c264ff66 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -171,122 +171,64 @@ static int ecw2cw(int ecw)
return (1 << ecw) - 1;
}
-/*
- * ieee80211_enable_ht should be called only after the operating band
- * has been determined as ht configuration depends on the hw's
- * HT abilities for a specific band.
- */
-static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_ht_info *hti,
- const u8 *bssid, u16 ap_ht_cap_flags,
- bool beacon_htcap_ie)
+static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_operation *ht_oper,
+ const u8 *bssid, bool reconfig)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
u32 changed = 0;
- int hti_cfreq;
u16 ht_opmode;
- bool enable_ht = true;
- enum nl80211_channel_type prev_chantype;
- enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
- enum nl80211_channel_type tx_channel_type;
+ bool disable_40 = false;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- prev_chantype = sdata->vif.bss_conf.channel_type;
-
- hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan,
- sband->band);
- /* check that channel matches the right operating channel */
- if (local->hw.conf.channel->center_freq != hti_cfreq) {
- /* Some APs mess this up, evidently.
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel, for instance.
- */
- printk(KERN_DEBUG
- "%s: Wrong control channel in association"
- " response: configured center-freq: %d"
- " hti-cfreq: %d hti->control_chan: %d"
- " band: %d. Disabling HT.\n",
- sdata->name,
- local->hw.conf.channel->center_freq,
- hti_cfreq, hti->control_chan,
- sband->band);
- enable_ht = false;
- }
-
- if (enable_ht) {
- rx_channel_type = NL80211_CHAN_HT20;
-
- if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- !ieee80111_cfg_override_disables_ht40(sdata) &&
- (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
- (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
- switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rx_channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rx_channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ switch (sdata->vif.bss_conf.channel_type) {
+ case NL80211_CHAN_HT40PLUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ disable_40 = true;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ disable_40 = true;
+ break;
+ default:
+ break;
}
- tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
+ /* This can change during the lifetime of the BSS */
+ if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
+ disable_40 = true;
- if (local->tmp_channel)
- local->tmp_channel_type = rx_channel_type;
-
- if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- rx_channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- rx_channel_type));
- }
-
- if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) {
- /*
- * Whenever the AP announces the HT mode change that can be
- * 40MHz intolerant or etc., it would be safer to stop tx
- * queues before doing hw config to avoid buffer overflow.
- */
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, bssid);
- /* flush out all packets */
- synchronize_net();
+ WARN_ON_ONCE(!sta);
- drv_flush(local, false);
- }
+ if (sta && !sta->supports_40mhz)
+ disable_40 = true;
- /* channel_type change automatically detected */
- ieee80211_hw_config(local, 0);
+ if (sta && (!reconfig ||
+ (disable_40 != !(sta->sta.ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
- if (prev_chantype != tx_channel_type) {
- rcu_read_lock();
- sta = sta_info_get(sdata, bssid);
- if (sta)
- rate_control_rate_update(local, sband, sta,
- IEEE80211_RC_HT_CHANGED,
- tx_channel_type);
- rcu_read_unlock();
+ if (disable_40)
+ sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- if (beacon_htcap_ie)
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ rate_control_rate_update(local, sband, sta,
+ IEEE80211_RC_BW_CHANGED);
}
+ mutex_unlock(&local->sta_mtx);
- ht_opmode = le16_to_cpu(hti->operation_mode);
+ ht_opmode = le16_to_cpu(ht_oper->operation_mode);
/* if bss configuration changed store the new one */
- if (sdata->ht_opmode_valid != enable_ht ||
- sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
- prev_chantype != rx_channel_type) {
+ if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
- sdata->ht_opmode_valid = enable_ht;
}
return changed;
@@ -316,12 +258,12 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
}
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, const u8 *ht_info_ie,
+ struct sk_buff *skb, const u8 *ht_oper_ie,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
enum ieee80211_smps_mode smps)
{
- struct ieee80211_ht_info *ht_info;
+ struct ieee80211_ht_operation *ht_oper;
u8 *pos;
u32 flags = channel->flags;
u16 cap;
@@ -329,21 +271,21 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
- if (!ht_info_ie)
+ if (!ht_oper_ie)
return;
- if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
+ if (ht_oper_ie[1] < sizeof(struct ieee80211_ht_operation))
return;
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
- ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
+ ht_oper = (struct ieee80211_ht_operation *)(ht_oper_ie + 2);
/* determine capability flags */
cap = ht_cap.cap;
- switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -358,6 +300,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
break;
}
+ /*
+ * If 40 MHz was disabled associate as though we weren't
+ * capable of 40 MHz -- some broken APs will never fall
+ * back to trying to transmit in 20 MHz.
+ */
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) {
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
+
/* set SM PS mode properly */
cap &= ~IEEE80211_HT_CAP_SM_PS;
switch (smps) {
@@ -557,7 +509,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie,
+ ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie,
sband, local->oper_channel, ifmgd->ap_smps);
/* if present, add any custom non-vendor IEs that go after HT */
@@ -1182,7 +1134,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
if (!local->ops->conf_tx)
return;
- if (local->hw.queues < 4)
+ if (local->hw.queues < IEEE80211_NUM_ACS)
return;
if (!wmm_param)
@@ -1435,7 +1387,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.assoc = false;
/* on the next assoc, re-program HT parameters */
- sdata->ht_opmode_valid = false;
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
@@ -1496,19 +1447,24 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
+ mutex_lock(&local->mtx);
if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
- IEEE80211_STA_CONNECTION_POLL)))
- return;
+ IEEE80211_STA_CONNECTION_POLL))) {
+ mutex_unlock(&local->mtx);
+ return;
+ }
ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
- mutex_lock(&sdata->local->iflist_mtx);
- ieee80211_recalc_ps(sdata->local, -1);
- mutex_unlock(&sdata->local->iflist_mtx);
+
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_ps(local, -1);
+ mutex_unlock(&local->iflist_mtx);
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
- return;
+ goto out;
/*
* We've received a probe response, but are not sure whether
@@ -1520,6 +1476,9 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
mod_timer(&ifmgd->conn_mon_timer,
round_jiffies_up(jiffies +
IEEE80211_CONNECTION_IDLE_TIME));
+out:
+ ieee80211_run_deferred_scan(local);
+ mutex_unlock(&local->mtx);
}
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1567,14 +1526,23 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ifmgd->nullfunc_failed = false;
ieee80211_send_nullfunc(sdata->local, sdata, 0);
} else {
+ int ssid_len;
+
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
- (u32) -1, true, false);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
+ ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ 0, (u32) -1, true, false);
}
ifmgd->probe_send_count++;
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(ifmgd, ifmgd->probe_timeout);
+ if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ drv_flush(sdata->local, false);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -1586,21 +1554,22 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_sdata_running(sdata))
return;
- if (sdata->local->scanning)
- return;
-
- if (sdata->local->tmp_channel)
- return;
-
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated)
goto out;
+ mutex_lock(&sdata->local->mtx);
+
+ if (sdata->local->tmp_channel || sdata->local->scanning) {
+ mutex_unlock(&sdata->local->mtx);
+ goto out;
+ }
+
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (beacon && net_ratelimit())
- printk(KERN_DEBUG "%s: detected beacon loss from AP "
- "- sending probe request\n", sdata->name);
+ if (beacon)
+ net_dbg_ratelimited("%s: detected beacon loss from AP - sending probe request\n",
+ sdata->name);
#endif
/*
@@ -1623,6 +1592,8 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
else
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
+ mutex_unlock(&sdata->local->mtx);
+
if (already)
goto out;
@@ -1643,6 +1614,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
const u8 *ssid;
+ int ssid_len;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return NULL;
@@ -1653,8 +1625,13 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
return NULL;
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
- (u32) -1, ssid + 2, ssid[1],
+ (u32) -1, ssid + 2, ssid_len,
NULL, 0, true);
return skb;
@@ -1799,7 +1776,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
- if (compare_ether_addr(bssid, mgmt->bssid))
+ if (!ether_addr_equal(bssid, mgmt->bssid))
return RX_MGMT_NONE;
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
@@ -1876,7 +1853,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_NONE;
if (!ifmgd->associated ||
- compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid))
+ !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
return RX_MGMT_NONE;
bssid = ifmgd->associated->bssid;
@@ -1909,7 +1886,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_NONE;
if (!ifmgd->associated ||
- compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid))
+ !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
return RX_MGMT_NONE;
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
@@ -2000,7 +1977,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u32 changed = 0;
int err;
- u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure */
@@ -2051,7 +2027,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
+ sta->supports_40mhz =
+ sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
rate_control_rate_init(sta);
@@ -2092,11 +2069,10 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_set_wmm_default(sdata, false);
changed |= BSS_CHANGED_QOS;
- if (elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- cbss->bssid, ap_ht_cap_flags,
- false);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ cbss->bssid, false);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
@@ -2137,7 +2113,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!assoc_data)
return RX_MGMT_NONE;
- if (compare_ether_addr(assoc_data->bss->bssid, mgmt->bssid))
+ if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
return RX_MGMT_NONE;
/*
@@ -2217,8 +2193,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
bool need_ps = false;
if (sdata->u.mgd.associated &&
- compare_ether_addr(mgmt->bssid, sdata->u.mgd.associated->bssid)
- == 0) {
+ ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) {
bss = (void *)sdata->u.mgd.associated->priv;
/* not previously set so we may need to recalc */
need_ps = !bss->dtim_period;
@@ -2273,7 +2248,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ASSERT_MGD_MTX(ifmgd);
- if (compare_ether_addr(mgmt->da, sdata->vif.addr))
+ if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
return; /* ignore ProbeResp to foreign address */
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
@@ -2286,12 +2261,11 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
if (ifmgd->associated &&
- compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid) == 0)
+ ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
ieee80211_reset_ap_probe(sdata);
if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
- compare_ether_addr(mgmt->bssid, ifmgd->auth_data->bss->bssid)
- == 0) {
+ ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) {
/* got probe response, continue with auth */
printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
ifmgd->auth_data->tries = 0;
@@ -2319,7 +2293,7 @@ static const u64 care_about_ies =
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
(1ULL << WLAN_EID_HT_CAPABILITY) |
- (1ULL << WLAN_EID_HT_INFORMATION);
+ (1ULL << WLAN_EID_HT_OPERATION);
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -2348,8 +2322,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
return;
if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
- compare_ether_addr(mgmt->bssid, ifmgd->assoc_data->bss->bssid)
- == 0) {
+ ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
ieee802_11_parse_elems(mgmt->u.beacon.variable,
len - baselen, &elems);
@@ -2364,7 +2337,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
if (!ifmgd->associated ||
- compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid))
+ !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
return;
bssid = ifmgd->associated->bssid;
@@ -2431,10 +2404,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: cancelling probereq poll due "
- "to a received beacon\n", sdata->name);
- }
+ net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n",
+ sdata->name);
#endif
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
mutex_lock(&local->iflist_mtx);
@@ -2468,11 +2439,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
- local->hw.conf.flags &= ~IEEE80211_CONF_PS;
- ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_PS);
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
ieee80211_send_nullfunc(local, sdata, 0);
- } else {
+ } else if (!local->pspolling && sdata->u.mgd.powersave) {
local->pspolling = true;
/*
@@ -2504,31 +2477,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
- struct sta_info *sta;
struct ieee80211_supported_band *sband;
- u16 ap_ht_cap_flags;
-
- rcu_read_lock();
-
- sta = sta_info_get(sdata, bssid);
- if (WARN_ON(!sta)) {
- rcu_read_unlock();
- return;
- }
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
- elems.ht_cap_elem, &sta->sta.ht_cap);
-
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
-
- rcu_read_unlock();
-
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- bssid, ap_ht_cap_flags, true);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ bssid, true);
}
/* Note: country IE parsing is done for us by cfg80211 */
@@ -3060,6 +3016,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta;
bool have_sta = false;
int err;
+ int ht_cfreq;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ const u8 *ht_oper_ie;
+ const struct ieee80211_ht_operation *ht_oper = NULL;
+ struct ieee80211_supported_band *sband;
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
@@ -3081,17 +3042,76 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->mtx);
/* switch to the right channel */
+ sband = local->hw.wiphy->bands[cbss->channel->band];
+
+ ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+
+ if (sband->ht_cap.ht_supported) {
+ ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
+ ht_oper = (void *)(ht_oper_ie + 2);
+ }
+
+ if (ht_oper) {
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ cbss->channel->band);
+ /* check that channel matches the right operating channel */
+ if (cbss->channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ printk(KERN_DEBUG
+ "%s: Wrong control channel: center-freq: %d"
+ " ht-cfreq: %d ht->primary_chan: %d"
+ " band: %d. Disabling HT.\n",
+ sdata->name, cbss->channel->center_freq,
+ ht_cfreq, ht_oper->primary_chan,
+ cbss->channel->band);
+ ht_oper = NULL;
+ }
+ }
+
+ if (ht_oper) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ /* can only fail due to HT40+/- mismatch */
+ channel_type = NL80211_CHAN_HT20;
+ printk(KERN_DEBUG
+ "%s: disabling 40 MHz due to multi-vif mismatch\n",
+ sdata->name);
+ ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ channel_type));
+ }
+
local->oper_channel = cbss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_hw_config(local, 0);
if (!have_sta) {
- struct ieee80211_supported_band *sband;
u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1;
- sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
-
ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
@@ -3141,7 +3161,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
return err;
}
} else
- WARN_ON_ONCE(compare_ether_addr(ifmgd->bssid, cbss->bssid));
+ WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
return 0;
}
@@ -3281,7 +3301,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
bool match;
/* keep sta info, bssid if matching */
- match = compare_ether_addr(ifmgd->bssid, req->bss->bssid) == 0;
+ match = ether_addr_equal(ifmgd->bssid, req->bss->bssid);
ieee80211_destroy_auth_data(sdata, match);
}
@@ -3311,7 +3331,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* Also disable HT if we don't support it or the AP doesn't use WMM */
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
- local->hw.queues < 4 || !bss->wmm_used)
+ local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used)
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
@@ -3334,11 +3354,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->ap_smps = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
- assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4);
+ assoc_data->wmm = bss->wmm_used &&
+ (local->hw.queues >= IEEE80211_NUM_ACS);
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
- assoc_data->ht_information_ie =
- ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
+ assoc_data->ht_operation_ie =
+ ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3440,7 +3461,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
sdata->name, req->bssid, req->reason_code);
if (ifmgd->associated &&
- compare_ether_addr(ifmgd->associated->bssid, req->bssid) == 0)
+ ether_addr_equal(ifmgd->associated->bssid, req->bssid))
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, true, frame_buf);
else