summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c4
-rw-r--r--drivers/net/wireless/ath/carl9170/main.c4
-rw-r--r--drivers/net/wireless/ath/regd.c19
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c7
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c4
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c4
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c30
-rw-r--r--drivers/net/wireless/mwl8k.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c4
-rw-r--r--drivers/net/wireless/rtlwifi/core.c4
-rw-r--r--drivers/net/wireless/rtlwifi/regd.c17
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c4
-rw-r--r--include/linux/ieee80211.h100
-rw-r--r--include/net/cfg80211.h32
-rw-r--r--include/net/mac80211.h33
-rw-r--r--include/net/regulatory.h4
-rw-r--r--include/uapi/linux/nl80211.h16
-rw-r--r--net/mac80211/agg-tx.c253
-rw-r--r--net/mac80211/cfg.c139
-rw-r--r--net/mac80211/chan.c41
-rw-r--r--net/mac80211/driver-ops.h23
-rw-r--r--net/mac80211/ht.c33
-rw-r--r--net/mac80211/ibss.c66
-rw-r--r--net/mac80211/ieee80211_i.h39
-rw-r--r--net/mac80211/iface.c76
-rw-r--r--net/mac80211/main.c70
-rw-r--r--net/mac80211/mesh.c42
-rw-r--r--net/mac80211/mesh.h2
-rw-r--r--net/mac80211/mlme.c143
-rw-r--r--net/mac80211/offchannel.c15
-rw-r--r--net/mac80211/pm.c64
-rw-r--r--net/mac80211/rx.c31
-rw-r--r--net/mac80211/scan.c55
-rw-r--r--net/mac80211/sta_info.c66
-rw-r--r--net/mac80211/sta_info.h45
-rw-r--r--net/mac80211/util.c18
-rw-r--r--net/wireless/ap.c62
-rw-r--r--net/wireless/chan.c4
-rw-r--r--net/wireless/core.c14
-rw-r--r--net/wireless/core.h18
-rw-r--r--net/wireless/mlme.c62
-rw-r--r--net/wireless/nl80211.c114
-rw-r--r--net/wireless/reg.c1111
-rw-r--r--net/wireless/reg.h7
-rw-r--r--net/wireless/sme.c6
47 files changed, 1580 insertions, 1337 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 9c07a8fa5134..a8016d70088a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
if (!ret)
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index be30a9af1528..e1fa70596e61 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
ath9k_ps_restore(sc);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ath9k_ps_wakeup(sc);
ath_tx_aggr_stop(sc, sta, tid);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index 25a1e2f4f738..9d2051aeb782 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
rcu_read_lock();
tid_info = rcu_dereference(sta_info->agg[tid]);
if (tid_info) {
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index d81698015bf7..7a6c79e1f819 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -195,8 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
const struct ieee80211_reg_rule *reg_rule;
struct ieee80211_channel *ch;
unsigned int i;
- u32 bandwidth = 0;
- int r;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
@@ -214,11 +212,8 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- r = freq_reg_info(wiphy,
- ch->center_freq,
- bandwidth,
- &reg_rule);
- if (r)
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (IS_ERR(reg_rule))
continue;
/*
* If 11d had a rule for this channel ensure
@@ -254,8 +249,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
const struct ieee80211_reg_rule *reg_rule;
- u32 bandwidth = 0;
- int r;
sband = wiphy->bands[IEEE80211_BAND_2GHZ];
if (!sband)
@@ -283,16 +276,16 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
*/
ch = &sband->channels[11]; /* CH 12 */
- r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
- if (!r) {
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
}
ch = &sband->channels[12]; /* CH 13 */
- r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
- if (!r) {
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index a90b72202ec5..4eb3f0d52105 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -670,7 +670,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
const struct ieee80211_reg_rule *rule;
- int band, i, ret;
+ int band, i;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
@@ -685,9 +685,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- ret = freq_reg_info(wiphy, ch->center_freq,
- 0, &rule);
- if (ret)
+ rule = freq_reg_info(wiphy, ch->center_freq);
+ if (IS_ERR(rule))
continue;
if (!(rule->flags & NL80211_RRF_NO_IBSS))
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 1fbd8ecbe2ea..f0fc8cd4d5df 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
spin_lock_bh(&wl->lock);
brcms_c_ampdu_flush(wl->wlc, sta, tid);
spin_unlock_bh(&wl->lock);
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index c3fbf6717564..6a86ed45835d 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
D_HT("start Tx\n");
ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
D_HT("stop Tx\n");
ret = il4965_tx_agg_stop(il, vif, sta, tid);
if (test_bit(S_EXIT_PENDING, &il->status))
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 1ed4bc694d29..6660effc563f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
IWL_DEBUG_HT(priv, "start Tx\n");
ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
if ((ret == 0) && (priv->agg_tids_count > 0)) {
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index ff9085502bea..d248a4cc6656 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -48,6 +48,10 @@ static int channels = 1;
module_param(channels, int, 0444);
MODULE_PARM_DESC(channels, "Number of concurrent channels");
+static bool paged_rx = false;
+module_param(paged_rx, bool, 0644);
+MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones");
+
/**
* enum hwsim_regtest - the type of regulatory tests we offer
*
@@ -755,9 +759,25 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
* reserve some space for our vendor and the normal
* radiotap header, since we're copying anyway
*/
- nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC);
- if (nskb == NULL)
- continue;
+ if (skb->len < PAGE_SIZE && paged_rx) {
+ struct page *page = alloc_page(GFP_ATOMIC);
+
+ if (!page)
+ continue;
+
+ nskb = dev_alloc_skb(128);
+ if (!nskb) {
+ __free_page(page);
+ continue;
+ }
+
+ memcpy(page_address(page), skb->data, skb->len);
+ skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len);
+ } else {
+ nskb = skb_copy(skb, GFP_ATOMIC);
+ if (!nskb)
+ continue;
+ }
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
@@ -1292,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_START:
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index f221b95b90b3..19b46fdf9f0f 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
if (stream) {
if (stream->state == AMPDU_STREAM_ACTIVE) {
spin_unlock(&priv->stream_lock);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 197b4466a5d2..12f93e42e160 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_START:
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index be33aa14c8af..d3ce9fbef00e 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
"IEEE80211_AMPDU_TX_START: TID:%d\n", tid);
return rtl_tx_agg_start(hw, sta, tid, ssn);
break;
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
"IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid);
return rtl_tx_agg_stop(hw, sta, tid);
diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c
index c1608cddc529..7e3ead774fb9 100644
--- a/drivers/net/wireless/rtlwifi/regd.c
+++ b/drivers/net/wireless/rtlwifi/regd.c
@@ -158,8 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
const struct ieee80211_reg_rule *reg_rule;
struct ieee80211_channel *ch;
unsigned int i;
- u32 bandwidth = 0;
- int r;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
@@ -174,9 +172,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
(ch->flags & IEEE80211_CHAN_RADAR))
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- r = freq_reg_info(wiphy, ch->center_freq,
- bandwidth, &reg_rule);
- if (r)
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (IS_ERR(reg_rule))
continue;
/*
@@ -211,8 +208,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
const struct ieee80211_reg_rule *reg_rule;
- u32 bandwidth = 0;
- int r;
if (!wiphy->bands[IEEE80211_BAND_2GHZ])
return;
@@ -240,16 +235,16 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
*/
ch = &sband->channels[11]; /* CH 12 */
- r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
- if (!r) {
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
}
ch = &sband->channels[12]; /* CH 13 */
- r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
- if (!r) {
+ reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index ea9d8e011bc9..d7de06359ae1 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
* Falling break here on purpose for all TX APDU commands.
*/
case IEEE80211_AMPDU_TX_START:
- case IEEE80211_AMPDU_TX_STOP:
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_OPERATIONAL:
ret = -EINVAL;
break;
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index f0859cc73861..ccf9ee1dca8c 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -180,7 +180,7 @@ struct ieee80211_hdr {
u8 addr3[6];
__le16 seq_ctrl;
u8 addr4[6];
-} __attribute__ ((packed));
+} __packed;
struct ieee80211_hdr_3addr {
__le16 frame_control;
@@ -189,7 +189,7 @@ struct ieee80211_hdr_3addr {
u8 addr2[6];
u8 addr3[6];
__le16 seq_ctrl;
-} __attribute__ ((packed));
+} __packed;
struct ieee80211_qos_hdr {
__le16 frame_control;
@@ -199,7 +199,7 @@ struct ieee80211_qos_hdr {
u8 addr3[6];
__le16 seq_ctrl;
__le16 qos_ctrl;
-} __attribute__ ((packed));
+} __packed;
/**
* ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
@@ -576,7 +576,7 @@ struct ieee80211s_hdr {
__le32 seqnum;
u8 eaddr1[6];
u8 eaddr2[6];
-} __attribute__ ((packed));
+} __packed;
/* Mesh flags */
#define MESH_FLAGS_AE_A4 0x1
@@ -614,7 +614,7 @@ struct ieee80211_quiet_ie {
u8 period;
__le16 duration;
__le16 offset;
-} __attribute__ ((packed));
+} __packed;
/**
* struct ieee80211_msrment_ie
@@ -626,7 +626,7 @@ struct ieee80211_msrment_ie {
u8 mode;
u8 type;
u8 request[0];
-} __attribute__ ((packed));
+} __packed;
/**
* struct ieee80211_channel_sw_ie
@@ -637,7 +637,7 @@ struct ieee80211_channel_sw_ie {
u8 mode;
u8 new_ch_num;
u8 count;
-} __attribute__ ((packed));
+} __packed;
/**
* struct ieee80211_tim
@@ -650,7 +650,7 @@ struct ieee80211_tim_ie {
u8 bitmap_ctrl;
/* variable size: 1 - 251 bytes */
u8 virtual_map[1];
-} __attribute__ ((packed));
+} __packed;
/**
* struct ieee80211_meshconf_ie
@@ -665,7 +665,7 @@ struct ieee80211_meshconf_ie {
u8 meshconf_auth;
u8 meshconf_form;
u8 meshconf_cap;
-} __attribute__ ((packed));
+} __packed;
/**
* enum mesh_config_capab_flags - Mesh Configuration IE capability field flags
@@ -695,12 +695,17 @@ struct ieee80211_rann_ie {
__le32 rann_seq;
__le32 rann_interval;
__le32 rann_metric;
-} __attribute__ ((packed));
+} __packed;
enum ieee80211_rann_flags {
RANN_FLAG_IS_GATE = 1 << 0,
};
+enum ieee80211_ht_chanwidth_values {
+ IEEE80211_HT_CHANWIDTH_20MHZ = 0,
+ IEEE80211_HT_CHANWIDTH_ANY = 1,
+};
+
#define WLAN_SA_QUERY_TR_ID_LEN 2
struct ieee80211_mgmt {
@@ -717,33 +722,33 @@ struct ieee80211_mgmt {
__le16 status_code;
/* possibly followed by Challenge text */
u8 variable[0];
- } __attribute__ ((packed)) auth;
+ } __packed auth;
struct {
__le16 reason_code;
- } __attribute__ ((packed)) deauth;
+ } __packed deauth;
struct {
__le16 capab_info;
__le16 listen_interval;
/* followed by SSID and Supported rates */
u8 variable[0];
- } __attribute__ ((packed)) assoc_req;
+ } __packed assoc_req;
struct {
__le16 capab_info;
__le16 status_code;
__le16 aid;
/* followed by Supported rates */
u8 variable[0];
- } __attribute__ ((packed)) assoc_resp, reassoc_resp;
+ } __packed assoc_resp, reassoc_resp;
struct {
__le16 capab_info;
__le16 listen_interval;
u8 current_ap[6];
/* followed by SSID and Supported rates */
u8 variable[0];
- } __attribute__ ((packed)) reassoc_req;
+ } __packed reassoc_req;
struct {
__le16 reason_code;
- } __attribute__ ((packed)) disassoc;
+ } __packed disassoc;
struct {
__le64 timestamp;
__le16 beacon_int;
@@ -751,11 +756,11 @@ struct ieee80211_mgmt {
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params, TIM */
u8 variable[0];
- } __attribute__ ((packed)) beacon;
+ } __packed beacon;
struct {
/* only variable items: SSID, Supported rates */
u8 variable[0];
- } __attribute__ ((packed)) probe_req;
+ } __packed probe_req;
struct {
__le64 timestamp;
__le16 beacon_int;
@@ -763,7 +768,7 @@ struct ieee80211_mgmt {
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params */
u8 variable[0];
- } __attribute__ ((packed)) probe_resp;
+ } __packed probe_resp;
struct {
u8 category;
union {
@@ -772,55 +777,59 @@ struct ieee80211_mgmt {
u8 dialog_token;
u8 status_code;
u8 variable[0];
- } __attribute__ ((packed)) wme_action;
+ } __packed wme_action;
struct{
u8 action_code;
u8 element_id;
u8 length;
struct ieee80211_channel_sw_ie sw_elem;
- } __attribute__((packed)) chan_switch;
+ } __packed chan_switch;
struct{
u8 action_code;
u8 dialog_token;
u8 element_id;
u8 length;
struct ieee80211_msrment_ie msr_elem;
- } __attribute__((packed)) measurement;
+ } __packed measurement;
struct{
u8 action_code;
u8 dialog_token;
__le16 capab;
__le16 timeout;
__le16 start_seq_num;
- } __attribute__((packed)) addba_req;
+ } __packed addba_req;
struct{
u8 action_code;
u8 dialog_token;
__le16 status;
__le16 capab;
__le16 timeout;
- } __attribute__((packed)) addba_resp;
+ } __packed addba_resp;
struct{
u8 action_code;
__le16 params;
__le16 reason_code;
- } __attribute__((packed)) delba;
+ } __packed delba;
struct {
u8 action_code;
u8 variable[0];
- } __attribute__((packed)) self_prot;
+ } __packed self_prot;
struct{
u8 action_code;
u8 variable[0];
- } __attribute__((packed)) mesh_action;
+ } __packed mesh_action;
struct {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
- } __attribute__ ((packed)) sa_query;
+ } __packed sa_query;
struct {
u8 action;
u8 smps_control;
- } __attribute__ ((packed)) ht_smps;
+ } __packed ht_smps;
+ struct {
+ u8 action_code;
+ u8 chanwidth;
+ } __packed ht_notify_cw;
struct {
u8 action_code;
u8 dialog_token;
@@ -828,9 +837,9 @@ struct ieee80211_mgmt {
u8 variable[0];
} __packed tdls_discover_resp;
} u;
- } __attribute__ ((packed)) action;
+ } __packed action;
} u;
-} __attribute__ ((packed));
+} __packed;
/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -846,7 +855,7 @@ struct ieee80211_mmie {
__le16 key_id;
u8 sequence_number[6];
u8 mic[8];
-} __attribute__ ((packed));
+} __packed;
struct ieee80211_vendor_ie {
u8 element_id;
@@ -861,20 +870,20 @@ struct ieee80211_rts {
__le16 duration;
u8 ra[6];
u8 ta[6];
-} __attribute__ ((packed));
+} __packed;
struct ieee80211_cts {
__le16 frame_control;
__le16 duration;
u8 ra[6];
-} __attribute__ ((packed));
+} __packed;
struct ieee80211_pspoll {
__le16 frame_control;
__le16 aid;
u8 bssid[6];
u8 ta[6];
-} __attribute__ ((packed));
+} __packed;
/* TDLS */
@@ -967,7 +976,7 @@ struct ieee80211_bar {
__u8 ta[6];
__le16 control;
__le16 start_seq_num;
-} __attribute__((packed));
+} __packed;
/* 802.11 BAR control masks */
#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
@@ -992,7 +1001,7 @@ struct ieee80211_mcs_info {
__le16 rx_highest;
u8 tx_params;
u8 reserved[3];
-} __attribute__((packed));
+} __packed;
/* 802.11n HT capability MSC set */
#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
@@ -1031,7 +1040,7 @@ struct ieee80211_ht_cap {
__le16 extended_ht_cap_info;
__le32 tx_BF_cap_info;
u8 antenna_selection_info;
-} __attribute__ ((packed));
+} __packed;
/* 802.11n HT capabilities masks (for cap_info) */
#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
@@ -1102,7 +1111,7 @@ struct ieee80211_ht_operation {
__le16 operation_mode;
__le16 stbc_param;
u8 basic_set[16];
-} __attribute__ ((packed));
+} __packed;
/* for ht_param */
#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03
@@ -1311,16 +1320,21 @@ struct ieee80211_vht_operation {
#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
#define WLAN_CAPABILITY_QOS (1<<9)
#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
+#define WLAN_CAPABILITY_APSD (1<<11)
+#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12)
#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
+#define WLAN_CAPABILITY_DEL_BACK (1<<14)
+#define WLAN_CAPABILITY_IMM_BACK (1<<15)
/* DMG (60gHz) 802.11ad */
/* type - bits 0..1 */
+#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0)
#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */
#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */
#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */
#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2)
-#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
+#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4)
#define WLAN_CAPABILITY_DMG_ECPAC (1<<5)
@@ -1834,14 +1848,14 @@ struct ieee80211_country_ie_triplet {
u8 first_channel;
u8 num_channels;
s8 max_power;
- } __attribute__ ((packed)) chans;
+ } __packed chans;
struct {
u8 reg_extension_id;
u8 reg_class;
u8 coverage_class;
- } __attribute__ ((packed)) ext;
+ } __packed ext;
};
-} __attribute__ ((packed));
+} __packed;
enum ieee80211_timeout_interval_type {
WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 8e6a6b73b9c9..e5f085c89221 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1256,7 +1256,7 @@ struct cfg80211_bss {
u8 bssid[ETH_ALEN];
- u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
+ u8 priv[0] __aligned(sizeof(void *));
};
/**
@@ -2369,7 +2369,7 @@ struct wiphy {
/* fields below are read-only, assigned by cfg80211 */
- const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain __rcu *regd;
/* the item in /sys/class/ieee80211/ points to this,
* you need use set_wiphy_dev() (see below) */
@@ -2392,7 +2392,7 @@ struct wiphy {
const struct iw_handler_def *wext;
#endif
- char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
+ char priv[0] __aligned(NETDEV_ALIGN);
};
static inline struct net *wiphy_net(struct wiphy *wiphy)
@@ -2938,28 +2938,22 @@ extern void wiphy_apply_custom_regulatory(
* freq_reg_info - get regulatory information for the given frequency
* @wiphy: the wiphy for which we want to process this rule for
* @center_freq: Frequency in KHz for which we want regulatory information for
- * @desired_bw_khz: the desired max bandwidth you want to use per
- * channel. Note that this is still 20 MHz if you want to use HT40
- * as HT40 makes use of two channels for its 40 MHz width bandwidth.
- * If set to 0 we'll assume you want the standard 20 MHz.
- * @reg_rule: the regulatory rule which we have for this frequency
*
* Use this function to get the regulatory rule for a specific frequency on
* a given wireless device. If the device has a specific regulatory domain
* it wants to follow we respect that unless a country IE has been received
* and processed already.
*
- * Returns 0 if it was able to find a valid regulatory rule which does
- * apply to the given center_freq otherwise it returns non-zero. It will
- * also return -ERANGE if we determine the given center_freq does not even have
- * a regulatory rule for a frequency range in the center_freq's band. See
- * freq_in_rule_band() for our current definition of a band -- this is purely
- * subjective and right now its 802.11 specific.
- */
-extern int freq_reg_info(struct wiphy *wiphy,
- u32 center_freq,
- u32 desired_bw_khz,
- const struct ieee80211_reg_rule **reg_rule);
+ * When an error occurs, for example if no rule can be found, the return value
+ * is encoded using ERR_PTR(). Use IS_ERR() to check and PTR_ERR() to obtain
+ * the numeric return value. The numeric return value will be -ERANGE if we
+ * determine the given center_freq does not even have a regulatory rule for a
+ * frequency range in the center_freq's band. See freq_in_rule_band() for our
+ * current definition of a band -- this is purely subjective and right now it's
+ * 802.11 specific.
+ */
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq);
/*
* callbacks for asynchronous cfg80211 methods, notification
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ee50c5eba50c..23daed3c78ed 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -173,7 +173,7 @@ struct ieee80211_chanctx_conf {
u8 rx_chains_static, rx_chains_dynamic;
- u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+ u8 drv_priv[0] __aligned(sizeof(void *));
};
/**
@@ -1059,7 +1059,7 @@ struct ieee80211_vif {
u32 driver_flags;
/* must be last */
- u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+ u8 drv_priv[0] __aligned(sizeof(void *));
};
static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
@@ -1209,7 +1209,7 @@ struct ieee80211_sta {
u8 max_sp;
/* must be last */
- u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+ u8 drv_priv[0] __aligned(sizeof(void *));
};
/**
@@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags {
* calling ieee80211_start_tx_ba_cb_irqsafe, because the peer
* might receive the addBA frame and send a delBA right away!
*
- * @IEEE80211_AMPDU_RX_START: start Rx aggregation
- * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
- * @IEEE80211_AMPDU_TX_START: start Tx aggregation
- * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
+ * @IEEE80211_AMPDU_RX_START: start RX aggregation
+ * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
+ * @IEEE80211_AMPDU_TX_START: start TX aggregation
* @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
+ * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting
+ * queued packets, now unaggregated. After all packets are transmitted the
+ * driver has to call ieee80211_stop_tx_ba_cb_irqsafe().
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets,
+ * called when the station is removed. There's no need or reason to call
+ * ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the
+ * session is gone and removes the station.
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped
+ * but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and
+ * now the connection is dropped and the station will be removed. Drivers
+ * should clean up and drop remaining packets when this is called.
*/
enum ieee80211_ampdu_mlme_action {
IEEE80211_AMPDU_RX_START,
IEEE80211_AMPDU_RX_STOP,
IEEE80211_AMPDU_TX_START,
- IEEE80211_AMPDU_TX_STOP,
+ IEEE80211_AMPDU_TX_STOP_CONT,
+ IEEE80211_AMPDU_TX_STOP_FLUSH,
+ IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
IEEE80211_AMPDU_TX_OPERATIONAL,
};
@@ -3754,6 +3766,11 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
* The iterator will not find a context that's being added (during
* the driver callback to add it) but will find it while it's being
* removed.
+ *
+ * Note that during hardware restart, all contexts that existed
+ * before the restart are considered already present so will be
+ * found while iterating, whether they've been re-added already
+ * or not.
*/
void ieee80211_iter_chan_contexts_atomic(
struct ieee80211_hw *hw,
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index 7dcaa2794fde..f17ed590d64a 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -18,6 +18,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/rcupdate.h>
/**
* enum environment_cap - Environment parsed from country IE
@@ -35,6 +36,7 @@ enum environment_cap {
/**
* struct regulatory_request - used to keep track of regulatory requests
*
+ * @rcu_head: RCU head struct used to free the request
* @wiphy_idx: this is set if this request's initiator is
* %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
* can be used by the wireless core to deal with conflicts
@@ -72,6 +74,7 @@ enum environment_cap {
* @list: used to insert into the reg_requests_list linked list
*/
struct regulatory_request {
+ struct rcu_head rcu_head;
int wiphy_idx;
enum nl80211_reg_initiator initiator;
enum nl80211_user_reg_hint_type user_reg_hint_type;
@@ -101,6 +104,7 @@ struct ieee80211_reg_rule {
};
struct ieee80211_regdomain {
+ struct rcu_head rcu_head;
u32 n_reg_rules;
char alpha2[2];
u8 dfs_region;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index e3e19f8b16f2..547017100a30 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1697,6 +1697,9 @@ enum nl80211_iftype {
* flag can't be changed, it is only valid while adding a station, and
* attempts to change it will silently be ignored (rather than rejected
* as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ * previously added station into associated state
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -1708,6 +1711,7 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_MFP,
NL80211_STA_FLAG_AUTHENTICATED,
NL80211_STA_FLAG_TDLS_PEER,
+ NL80211_STA_FLAG_ASSOCIATED,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
@@ -3140,6 +3144,17 @@ enum nl80211_ap_sme_features {
* setting
* @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
* powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ * transitions for AP clients. Without this flag (and if the driver
+ * doesn't have the AP SME in the device) the driver supports adding
+ * stations only when they're associated and adds them in associated
+ * state (to later be transitioned into authorized), with this flag
+ * they should be added before even sending the authentication reply
+ * and then transitioned into authenticated, associated and authorized
+ * states using station flags.
+ * Note that even for drivers that support this, the default is to add
+ * stations in authenticated/associated state, so to add unauthenticated
+ * stations the authenticated/associated bits have to be set in the mask.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@@ -3155,6 +3170,7 @@ enum nl80211_feature_flags {
NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13,
};
/**
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index eb9df22418f0..2f0ccbc5f13e 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -149,16 +149,133 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
}
+static inline int ieee80211_ac_from_tid(int tid)
+{
+ return ieee802_1d_to_ac[tid & 7];
+}
+
+/*
+ * When multiple aggregation sessions on multiple stations
+ * are being created/destroyed simultaneously, we need to
+ * refcount the global queue stop caused by that in order
+ * to not get into a situation where one of the aggregation
+ * setup or teardown re-enables queues before the other is
+ * ready to handle that.
+ *
+ * These two functions take care of this issue by keeping
+ * a global "agg_queue_stop" refcount.
+ */
+static void __acquires(agg_queue)
+ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
+{
+ int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+
+ if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
+ ieee80211_stop_queue_by_reason(
+ &sdata->local->hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ __acquire(agg_queue);
+}
+
+static void __releases(agg_queue)
+ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
+{
+ int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+
+ if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
+ ieee80211_wake_queue_by_reason(
+ &sdata->local->hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ __release(agg_queue);
+}
+
+/*
+ * splice packets from the STA's pending to the local pending,
+ * requires a call to ieee80211_agg_splice_finish later
+ */
+static void __acquires(agg_queue)
+ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata,
+ struct tid_ampdu_tx *tid_tx, u16 tid)
+{
+ struct ieee80211_local *local = sdata->local;
+ int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+ unsigned long flags;
+
+ ieee80211_stop_queue_agg(sdata, tid);
+
+ if (WARN(!tid_tx,
+ "TID %d gone but expected when splicing aggregates from the pending queue\n",
+ tid))
+ return;
+
+ if (!skb_queue_empty(&tid_tx->pending)) {
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ /* copy over remaining packets */
+ skb_queue_splice_tail_init(&tid_tx->pending,
+ &local->pending[queue]);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ }
+}
+
+static void __releases(agg_queue)
+ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid)
+{
+ ieee80211_wake_queue_agg(sdata, tid);
+}
+
+static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid)
+{
+ struct tid_ampdu_tx *tid_tx;
+
+ lockdep_assert_held(&sta->ampdu_mlme.mtx);
+ lockdep_assert_held(&sta->lock);
+
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+
+ /*
+ * When we get here, the TX path will not be lockless any more wrt.
+ * aggregation, since the OPERATIONAL bit has long been cleared.
+ * Thus it will block on getting the lock, if it occurs. So if we
+ * stop the queue now, we will not get any more packets, and any
+ * that might be being processed will wait for us here, thereby
+ * guaranteeing that no packets go to the tid_tx pending queue any
+ * more.
+ */
+
+ ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);
+
+ /* future packets must not find the tid_tx struct any more */
+ ieee80211_assign_tid_tx(sta, tid, NULL);
+
+ ieee80211_agg_splice_finish(sta->sdata, tid);
+
+ kfree_rcu(tid_tx, rcu_head);
+}
+
int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator,
- bool tx)
+ enum ieee80211_agg_stop_reason reason)
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_tx *tid_tx;
+ enum ieee80211_ampdu_mlme_action action;
int ret;
lockdep_assert_held(&sta->ampdu_mlme.mtx);
+ switch (reason) {
+ case AGG_STOP_DECLINED:
+ case AGG_STOP_LOCAL_REQUEST:
+ case AGG_STOP_PEER_REQUEST:
+ action = IEEE80211_AMPDU_TX_STOP_CONT;
+ break;
+ case AGG_STOP_DESTROY_STA:
+ action = IEEE80211_AMPDU_TX_STOP_FLUSH;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
spin_lock_bh(&sta->lock);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -167,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
return -ENOENT;
}
- /* if we're already stopping ignore any new requests to stop */
+ /*
+ * if we're already stopping ignore any new requests to stop
+ * unless we're destroying it in which case notify the driver
+ */
if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
spin_unlock_bh(&sta->lock);
- return -EALREADY;
+ if (reason != AGG_STOP_DESTROY_STA)
+ return -EALREADY;
+ ret = drv_ampdu_action(local, sta->sdata,
+ IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
+ &sta->sta, tid, NULL, 0);
+ WARN_ON_ONCE(ret);
+ goto remove_tid_tx;
}
if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
@@ -212,11 +338,12 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
*/
synchronize_net();
- tid_tx->stop_initiator = initiator;
- tid_tx->tx_stop = tx;
+ tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ?
+ WLAN_BACK_RECIPIENT :
+ WLAN_BACK_INITIATOR;
+ tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
- ret = drv_ampdu_action(local, sta->sdata,
- IEEE80211_AMPDU_TX_STOP,
+ ret = drv_ampdu_action(local, sta->sdata, action,
&sta->sta, tid, NULL, 0);
/* HW shall not deny going back to legacy */
@@ -227,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
*/
}
- return ret;
+ if (reason == AGG_STOP_DESTROY_STA) {
+ remove_tid_tx:
+ spin_lock_bh(&sta->lock);
+ ieee80211_remove_tid_tx(sta, tid);
+ spin_unlock_bh(&sta->lock);
+ }
+
+ return 0;
}
/*
@@ -264,80 +398,6 @@ static void sta_addba_resp_timer_expired(unsigned long data)
rcu_read_unlock();
}
-static inline int ieee80211_ac_from_tid(int tid)
-{
- return ieee802_1d_to_ac[tid & 7];
-}
-
-/*
- * When multiple aggregation sessions on multiple stations
- * are being created/destroyed simultaneously, we need to
- * refcount the global queue stop caused by that in order
- * to not get into a situation where one of the aggregation
- * setup or teardown re-enables queues before the other is
- * ready to handle that.
- *
- * These two functions take care of this issue by keeping
- * a global "agg_queue_stop" refcount.
- */
-static void __acquires(agg_queue)
-ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
-{
- int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
-
- if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
- ieee80211_stop_queue_by_reason(
- &sdata->local->hw, queue,
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- __acquire(agg_queue);
-}
-
-static void __releases(agg_queue)
-ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
-{
- int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
-
- if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
- ieee80211_wake_queue_by_reason(
- &sdata->local->hw, queue,
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- __release(agg_queue);
-}
-
-/*
- * splice packets from the STA's pending to the local pending,
- * requires a call to ieee80211_agg_splice_finish later
- */
-static void __acquires(agg_queue)
-ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata,
- struct tid_ampdu_tx *tid_tx, u16 tid)
-{
- struct ieee80211_local *local = sdata->local;
- int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
- unsigned long flags;
-
- ieee80211_stop_queue_agg(sdata, tid);
-
- if (WARN(!tid_tx,
- "TID %d gone but expected when splicing aggregates from the pending queue\n",
- tid))
- return;
-
- if (!skb_queue_empty(&tid_tx->pending)) {
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- /* copy over remaining packets */
- skb_queue_splice_tail_init(&tid_tx->pending,
- &local->pending[queue]);
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- }
-}
-
-static void __releases(agg_queue)
-ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid)
-{
- ieee80211_wake_queue_agg(sdata, tid);
-}
-
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
{
struct tid_ampdu_tx *tid_tx;
@@ -660,14 +720,13 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator,
- bool tx)
+ enum ieee80211_agg_stop_reason reason)
{
int ret;
mutex_lock(&sta->ampdu_mlme.mtx);
- ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx);
+ ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason);
mutex_unlock(&sta->ampdu_mlme.mtx);
@@ -751,24 +810,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
- /*
- * When we get here, the TX path will not be lockless any more wrt.
- * aggregation, since the OPERATIONAL bit has long been cleared.
- * Thus it will block on getting the lock, if it occurs. So if we
- * stop the queue now, we will not get any more packets, and any
- * that might be being processed will wait for us here, thereby
- * guaranteeing that no packets go to the tid_tx pending queue any
- * more.
- */
-
- ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);
-
- /* future packets must not find the tid_tx struct any more */
- ieee80211_assign_tid_tx(sta, tid, NULL);
-
- ieee80211_agg_splice_finish(sta->sdata, tid);
-
- kfree_rcu(tid_tx, rcu_head);
+ ieee80211_remove_tid_tx(sta, tid);
unlock_sta:
spin_unlock_bh(&sta->lock);
@@ -868,8 +910,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
}
} else {
- ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
- false);
+ ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED);
}
out:
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5c61677487cf..7d290bce0927 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -510,6 +510,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_TDLS_PEER);
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -521,6 +522,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
if (test_sta_flag(sta, WLAN_STA_AUTH))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (test_sta_flag(sta, WLAN_STA_ASSOC))
+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
}
@@ -930,6 +933,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->vif.bss_conf.dtim_period = params->dtim_period;
+ sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.ssid_len = params->ssid_len;
if (params->ssid_len)
@@ -1009,7 +1013,16 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
if (old_probe_resp)
kfree_rcu(old_probe_resp, rcu_head);
- sta_info_flush(local, sdata);
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ sta_info_flush_defer(vlan);
+ sta_info_flush_defer(sdata);
+ rcu_barrier();
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ sta_info_flush_cleanup(vlan);
+ sta_info_flush_cleanup(sdata);
+
+ sdata->vif.bss_conf.enable_beacon = false;
+ clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
drv_stop_ap(sdata->local, sdata);
@@ -1067,6 +1080,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
netif_rx_ni(skb);
}
+static int sta_apply_auth_flags(struct ieee80211_local *local,
+ struct sta_info *sta,
+ u32 mask, u32 set)
+{
+ int ret;
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ !test_sta_flag(sta, WLAN_STA_AUTH)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ !test_sta_flag(sta, WLAN_STA_ASSOC)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+ if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ else
+ ret = 0;
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+ test_sta_flag(sta, WLAN_STA_ASSOC)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
+ test_sta_flag(sta, WLAN_STA_AUTH)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
@@ -1084,52 +1149,20 @@ static int sta_apply_parameters(struct ieee80211_local *local,
mask = params->sta_flags_mask;
set = params->sta_flags_set;
- /*
- * In mesh mode, we can clear AUTHENTICATED flag but must
- * also make ASSOCIATED follow appropriately for the driver
- * API. See also below, after AUTHORIZED changes.
- */
- if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
- /* cfg80211 should not allow this in non-mesh modes */
- if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
- return -EINVAL;
-
- if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
- !test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
- if (ret)
- return ret;
- ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- if (ret)
- return ret;
- }
- }
-
- if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
- if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
- else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- if (ret)
- return ret;
- }
-
- if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
- /* cfg80211 should not allow this in non-mesh modes */
- if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
- return -EINVAL;
-
- if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
- test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
- if (ret)
- return ret;
- ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
- if (ret)
- return ret;
- }
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /*
+ * In mesh mode, ASSOCIATED isn't part of the nl80211
+ * API but must follow AUTHENTICATED for driver state.
+ */
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+ mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+ set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
}
+ ret = sta_apply_auth_flags(local, sta, mask, set);
+ if (ret)
+ return ret;
if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1175,10 +1208,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->sta.aid = params->aid;
/*
- * FIXME: updating the following information is racy when this
- * function is called from ieee80211_change_station().
- * However, all this information should be static so
- * maybe we should just reject attemps to change it.
+ * Some of the following updates would be racy if called on an
+ * existing station, via ieee80211_change_station(). However,
+ * all such changes are rejected by cfg80211 except for updates
+ * changing the supported rates on an existing but not yet used
+ * TDLS peer.
*/
if (params->listen_interval >= 0)
@@ -1263,6 +1297,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
+ /*
+ * defaults -- if userspace wants something else we'll
+ * change it accordingly in sta_apply_parameters()
+ */
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
@@ -1299,7 +1337,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1307,7 +1344,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
if (mac)
return sta_info_destroy_addr_bss(sdata, mac);
- sta_info_flush(local, sdata);
+ sta_info_flush(sdata);
return 0;
}
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 53f03120db55..1bfe0a8b19d2 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -4,6 +4,7 @@
#include <linux/nl80211.h>
#include <linux/export.h>
+#include <linux/rtnetlink.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -197,6 +198,15 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
ctx = container_of(conf, struct ieee80211_chanctx, conf);
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct ieee80211_sub_if_data *vlan;
+
+ /* for the VLAN list */
+ ASSERT_RTNL();
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ rcu_assign_pointer(vlan->vif.chanctx_conf, NULL);
+ }
+
ieee80211_unassign_vif_chanctx(sdata, ctx);
if (ctx->refcount == 0)
ieee80211_free_chanctx(local, ctx);
@@ -316,6 +326,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
goto out;
}
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct ieee80211_sub_if_data *vlan;
+
+ /* for the VLAN list */
+ ASSERT_RTNL();
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf);
+ }
+
ieee80211_recalc_smps_chanctx(local, ctx);
out:
mutex_unlock(&local->chanctx_mtx);
@@ -331,6 +350,25 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&sdata->local->chanctx_mtx);
}
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *ap;
+ struct ieee80211_chanctx_conf *conf;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
+ return;
+
+ ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
+
+ mutex_lock(&local->chanctx_mtx);
+
+ conf = rcu_dereference_protected(ap->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+ mutex_unlock(&local->chanctx_mtx);
+}
+
void ieee80211_iter_chan_contexts_atomic(
struct ieee80211_hw *hw,
void (*iter)(struct ieee80211_hw *hw,
@@ -343,7 +381,8 @@ void ieee80211_iter_chan_contexts_atomic(
rcu_read_lock();
list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
- iter(hw, &ctx->conf, iter_data);
+ if (ctx->driver_present)
+ iter(hw, &ctx->conf, iter_data);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 698dc7e6f309..0c07f94c5378 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -207,6 +207,14 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
{
might_sleep();
+ WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED) &&
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT);
+ WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE &&
+ changed & ~BSS_CHANGED_IDLE);
+
check_sdata_in_driver(sdata);
trace_drv_bss_info_changed(local, sdata, info, changed);
@@ -913,6 +921,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
if (local->ops->add_chanctx)
ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
trace_drv_return_int(local, ret);
+ if (!ret)
+ ctx->driver_present = true;
return ret;
}
@@ -924,6 +934,7 @@ static inline void drv_remove_chanctx(struct ieee80211_local *local,
if (local->ops->remove_chanctx)
local->ops->remove_chanctx(&local->hw, &ctx->conf);
trace_drv_return_void(local);
+ ctx->driver_present = false;
}
static inline void drv_change_chanctx(struct ieee80211_local *local,
@@ -931,8 +942,10 @@ static inline void drv_change_chanctx(struct ieee80211_local *local,
u32 changed)
{
trace_drv_change_chanctx(local, ctx, changed);
- if (local->ops->change_chanctx)
+ if (local->ops->change_chanctx) {
+ WARN_ON_ONCE(!ctx->driver_present);
local->ops->change_chanctx(&local->hw, &ctx->conf, changed);
+ }
trace_drv_return_void(local);
}
@@ -945,10 +958,12 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
check_sdata_in_driver(sdata);
trace_drv_assign_vif_chanctx(local, sdata, ctx);
- if (local->ops->assign_vif_chanctx)
+ if (local->ops->assign_vif_chanctx) {
+ WARN_ON_ONCE(!ctx->driver_present);
ret = local->ops->assign_vif_chanctx(&local->hw,
&sdata->vif,
&ctx->conf);
+ }
trace_drv_return_int(local, ret);
return ret;
@@ -961,10 +976,12 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
check_sdata_in_driver(sdata);
trace_drv_unassign_vif_chanctx(local, sdata, ctx);
- if (local->ops->unassign_vif_chanctx)
+ if (local->ops->unassign_vif_chanctx) {
+ WARN_ON_ONCE(!ctx->driver_present);
local->ops->unassign_vif_chanctx(&local->hw,
&sdata->vif,
&ctx->conf);
+ }
trace_drv_return_void(local);
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index a71d891794a4..61ac7c48ac0c 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -62,6 +62,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+ /* Allow user to disable SGI-20 (SGI-40 is handled above) */
+ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+
/* Allow user to disable the max-AMSDU bit. */
__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
@@ -117,6 +120,21 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40));
+
+ /* Unset 40 MHz if we're not using a 40 MHz channel */
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40;
+ ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ break;
+ }
+
/*
* The STBC bits are asymmetric -- if we don't have
* TX then mask out the peer's RX and vice versa.
@@ -179,16 +197,19 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
ieee80211_apply_htcap_overrides(sdata, ht_cap);
}
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+ enum ieee80211_agg_stop_reason reason)
{
int i;
cancel_work_sync(&sta->ampdu_mlme.work);
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
- __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
+ __ieee80211_stop_tx_ba_session(sta, i, reason);
__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_LEAVE_QBSS, tx);
+ WLAN_REASON_QSTA_LEAVE_QBSS,
+ reason != AGG_STOP_DESTROY_STA &&
+ reason != AGG_STOP_PEER_REQUEST);
}
}
@@ -245,8 +266,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
&tid_tx->state))
___ieee80211_stop_tx_ba_session(sta, tid,
- WLAN_BACK_INITIATOR,
- true);
+ AGG_STOP_LOCAL_REQUEST);
}
mutex_unlock(&sta->ampdu_mlme.mtx);
}
@@ -314,8 +334,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
true);
else
- __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
- true);
+ __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
}
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 8881fc77fb13..b4b866f41919 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -67,7 +67,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
skb_reserve(skb, sdata->local->hw.extra_tx_headroom);
if (!ether_addr_equal(ifibss->bssid, bssid))
- sta_info_flush(sdata->local, sdata);
+ sta_info_flush(sdata);
/* if merging, indicate to driver that we leave the old IBSS */
if (sdata->vif.bss_conf.ibss_joined) {
@@ -191,6 +191,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(ifibss->presp, skb);
+ sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.beacon_int = beacon_int;
sdata->vif.bss_conf.basic_rates = basic_rates;
bss_change = BSS_CHANGED_BEACON_INT;
@@ -425,11 +426,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
+ struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
- struct ieee802_11_elems *elems,
- bool beacon)
+ struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
int freq;
@@ -530,7 +529,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
}
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
- channel, beacon);
+ channel);
if (!bss)
return;
@@ -703,8 +702,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
sdata_info(sdata,
"No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
- ieee80211_request_internal_scan(sdata,
- ifibss->ssid, ifibss->ssid_len, NULL);
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
+ NULL);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -802,9 +801,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
IEEE80211_SCAN_INTERVAL)) {
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
- ieee80211_request_internal_scan(sdata,
- ifibss->ssid, ifibss->ssid_len,
- ifibss->fixed_channel ? ifibss->channel : NULL);
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, chan);
} else {
int interval = IEEE80211_SCAN_INTERVAL;
@@ -878,14 +876,21 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb);
}
-static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+static
+void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status)
{
size_t baselen;
struct ieee802_11_elems elems;
+ BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
+ offsetof(typeof(mgmt->u.beacon), variable));
+
+ /*
+ * either beacon or probe_resp but the variable field is at the
+ * same offset
+ */
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
if (baselen > len)
return;
@@ -893,25 +898,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems);
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
-}
-
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- size_t baselen;
- struct ieee802_11_elems elems;
-
- /* Process beacon from the current BSS */
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
-
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
}
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -935,12 +922,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_mgmt_probe_req(sdata, skb);
break;
case IEEE80211_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
- rx_status);
- break;
case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
- rx_status);
+ ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len,
+ rx_status);
break;
case IEEE80211_STYPE_AUTH:
ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
@@ -1183,7 +1167,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
memset(ifibss->bssid, 0, ETH_ALEN);
ifibss->ssid_len = 0;
- sta_info_flush(sdata->local, sdata);
+ sta_info_flush(sdata);
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
@@ -1206,6 +1190,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
+ sdata->vif.bss_conf.enable_beacon = false;
+ clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
synchronize_rcu();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 42d0d0267730..0fa44a965ad9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -92,8 +92,6 @@ struct ieee80211_bss {
u32 device_ts;
- u8 dtim_period;
-
bool wmm_used;
bool uapsd_supported;
@@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags {
/**
* enum ieee80211_valid_data_flags - BSS valid data flags
- * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE
@@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags {
* beacon/probe response.
*/
enum ieee80211_bss_valid_data_flags {
- IEEE80211_BSS_VALID_DTIM = BIT(0),
IEEE80211_BSS_VALID_WMM = BIT(1),
IEEE80211_BSS_VALID_RATES = BIT(2),
IEEE80211_BSS_VALID_ERP = BIT(3)
@@ -409,6 +405,8 @@ struct ieee80211_mgd_assoc_data {
u8 ap_ht_param;
+ struct ieee80211_vht_cap ap_vht_cap;
+
size_t ie_len;
u8 ie[];
};
@@ -440,6 +438,7 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
bool broken_ap; /* AP is broken -- turn off powersave */
+ u8 dtim_period;
enum ieee80211_smps_mode req_smps, /* requested smps mode */
driver_smps_mode; /* smps mode request */
@@ -662,10 +661,13 @@ enum ieee80211_sub_if_data_flags {
* change handling while the interface is up
* @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel
* mode, so queues are stopped
+ * @SDATA_STATE_OFFCHANNEL_BEACON_STOPPED: Beaconing was stopped due
+ * to offchannel, reset when offchannel returns
*/
enum ieee80211_sdata_state_bits {
SDATA_STATE_RUNNING,
SDATA_STATE_OFFCHANNEL,
+ SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
};
/**
@@ -688,6 +690,7 @@ struct ieee80211_chanctx {
enum ieee80211_chanctx_mode mode;
int refcount;
+ bool driver_present;
struct ieee80211_chanctx_conf conf;
};
@@ -773,6 +776,10 @@ struct ieee80211_sub_if_data {
u32 mntr_flags;
} u;
+ spinlock_t cleanup_stations_lock;
+ struct list_head cleanup_stations;
+ struct work_struct cleanup_stations_wk;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *dir;
@@ -782,6 +789,11 @@ struct ieee80211_sub_if_data {
struct dentry *default_mgmt_key;
} debugfs;
#endif
+
+#ifdef CONFIG_PM
+ struct ieee80211_bss_conf suspend_bss_conf;
+#endif
+
/* must be last, dynamically sized area in this! */
struct ieee80211_vif vif;
};
@@ -1329,9 +1341,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
/* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work);
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
- const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan);
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+ const u8 *ssid, u8 ssid_len,
+ struct ieee80211_channel *chan);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1345,8 +1357,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee802_11_elems *elems,
- struct ieee80211_channel *channel,
- bool beacon);
+ struct ieee80211_channel *channel);
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss);
@@ -1421,7 +1432,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason, bool stop);
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason, bool stop);
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx);
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+ enum ieee80211_agg_stop_reason reason);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt, size_t len);
@@ -1435,11 +1447,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
size_t len);
int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator,
- bool tx);
+ enum ieee80211_agg_stop_reason reason);
int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator,
- bool tx);
+ enum ieee80211_agg_stop_reason reason);
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
void ieee80211_ba_session_work(struct work_struct *work);
@@ -1628,6 +1638,7 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 09a80b55cf5a..06fac2991d40 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -207,17 +207,8 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
- int meshhdrlen;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0;
-
- /* FIX: what would be proper limits for MTU?
- * This interface uses 802.3 frames. */
- if (new_mtu < 256 ||
- new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
+ if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN)
return -EINVAL;
- }
dev->mtu = new_mtu;
return 0;
@@ -586,11 +577,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- /* no need to tell driver, but set carrier */
- if (rtnl_dereference(sdata->bss->beacon))
+ /* no need to tell driver, but set carrier and chanctx */
+ if (rtnl_dereference(sdata->bss->beacon)) {
+ ieee80211_vif_vlan_copy_chanctx(sdata);
netif_carrier_on(dev);
- else
+ } else {
netif_carrier_off(dev);
+ }
break;
case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
@@ -754,7 +747,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
unsigned long flags;
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
- int i;
+ int i, flushed;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -779,11 +772,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
* (because if we remove a STA after ops->remove_interface()
* the driver will have removed the vif info already!)
*
- * This is relevant only in AP, WDS and mesh modes, since in
- * all other modes we've already removed all stations when
- * disconnecting etc.
+ * This is relevant only in WDS mode, in all other modes we've
+ * already removed all stations when disconnecting or similar,
+ * so warn otherwise.
+ *
+ * We call sta_info_flush_cleanup() later, to combine RCU waits.
*/
- sta_info_flush(local, sdata);
+ flushed = sta_info_flush_defer(sdata);
+ WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
+ (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1));
/*
* Don't count this interface for promisc/allmulti while it
@@ -839,6 +836,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
list_del(&sdata->u.vlan.list);
+ rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
@@ -865,19 +863,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&sdata->work);
/*
* When we get here, the interface is marked down.
- * Call rcu_barrier() to wait both for the RX path
- * should it be using the interface and enqueuing
- * frames at this very time on another CPU, and
- * for the sta free call_rcu callbacks.
+ *
+ * sta_info_flush_cleanup() requires rcu_barrier()
+ * first to wait for the station call_rcu() calls
+ * to complete, here we need at least sychronize_rcu()
+ * it to wait for the RX path in case it is using the
+ * interface and enqueuing frames at this very time on
+ * another CPU.
*/
rcu_barrier();
-
- /*
- * free_sta_rcu() enqueues a work for the actual
- * sta cleanup, so we need to flush it while
- * sdata is still valid.
- */
- flush_workqueue(local->workqueue);
+ sta_info_flush_cleanup(sdata);
skb_queue_purge(&sdata->skb_queue);
@@ -976,7 +971,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
*/
static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = sdata->local;
int flushed;
int i;
@@ -992,7 +986,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rmc_free(sdata);
- flushed = sta_info_flush(local, sdata);
+ flushed = sta_info_flush(sdata);
WARN_ON(flushed);
}
@@ -1233,6 +1227,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
INIT_LIST_HEAD(&sdata->u.ap.vlans);
+ sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
case NL80211_IFTYPE_P2P_CLIENT:
type = NL80211_IFTYPE_STATION;
@@ -1240,9 +1235,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.p2p = true;
/* fall through */
case NL80211_IFTYPE_STATION:
+ sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
ieee80211_sta_setup_sdata(sdata);
break;
case NL80211_IFTYPE_ADHOC:
+ sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
ieee80211_ibss_setup_sdata(sdata);
break;
case NL80211_IFTYPE_MESH_POINT:
@@ -1256,8 +1253,12 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
MONITOR_FLAG_OTHER_BSS;
break;
case NL80211_IFTYPE_WDS:
+ sdata->vif.bss_conf.bssid = NULL;
+ break;
case NL80211_IFTYPE_AP_VLAN:
+ break;
case NL80211_IFTYPE_P2P_DEVICE:
+ sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
@@ -1498,6 +1499,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
}
+static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
+
+ ieee80211_cleanup_sdata_stas(sdata);
+}
+
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
@@ -1573,6 +1583,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
INIT_LIST_HEAD(&sdata->key_list);
+ spin_lock_init(&sdata->cleanup_stations_lock);
+ INIT_LIST_HEAD(&sdata->cleanup_stations);
+ INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[i];
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 1b087fff93e7..39cfe8f10ad2 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -207,76 +207,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed)
{
struct ieee80211_local *local = sdata->local;
- static const u8 zero[ETH_ALEN] = { 0 };
if (!changed)
return;
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
- sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
- else if (sdata->vif.type == NL80211_IFTYPE_AP)
- sdata->vif.bss_conf.bssid = sdata->vif.addr;
- else if (sdata->vif.type == NL80211_IFTYPE_WDS)
- sdata->vif.bss_conf.bssid = NULL;
- else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- sdata->vif.bss_conf.bssid = zero;
- } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) {
- sdata->vif.bss_conf.bssid = sdata->vif.addr;
- WARN_ONCE(changed & ~(BSS_CHANGED_IDLE),
- "P2P Device BSS changed %#x", changed);
- } else {
- WARN_ON(1);
- return;
- }
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_MESH_POINT:
- break;
- default:
- /* do not warn to simplify caller in scan.c */
- changed &= ~BSS_CHANGED_BEACON_ENABLED;
- if (WARN_ON(changed & BSS_CHANGED_BEACON))
- return;
- break;
- }
-
- if (changed & BSS_CHANGED_BEACON_ENABLED) {
- if (local->quiescing || !ieee80211_sdata_running(sdata) ||
- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) {
- sdata->vif.bss_conf.enable_beacon = false;
- } else {
- /*
- * Beacon should be enabled, but AP mode must
- * check whether there is a beacon configured.
- */
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- sdata->vif.bss_conf.enable_beacon =
- !!sdata->u.ap.beacon;
- break;
- case NL80211_IFTYPE_ADHOC:
- sdata->vif.bss_conf.enable_beacon =
- !!sdata->u.ibss.presp;
- break;
-#ifdef CONFIG_MAC80211_MESH
- case NL80211_IFTYPE_MESH_POINT:
- sdata->vif.bss_conf.enable_beacon =
- !!sdata->u.mesh.mesh_id_len;
- break;
-#endif
- default:
- /* not reached */
- WARN_ON(1);
- break;
- }
- }
- }
-
drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
}
@@ -537,6 +471,7 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
.cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_MAX_AMSDU |
+ IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40),
.mcs = {
.rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -606,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS |
- NL80211_FEATURE_VIF_TXPOWER;
+ NL80211_FEATURE_VIF_TXPOWER |
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 1bf03f9ff3ba..245885841c8d 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -20,16 +20,11 @@
int mesh_allocated;
static struct kmem_cache *rm_cache;
-#ifdef CONFIG_MAC80211_MESH
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
{
return (mgmt->u.action.u.mesh_action.action_code ==
WLAN_MESH_ACTION_HWMP_PATH_SELECTION);
}
-#else
-bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
-{ return false; }
-#endif
void ieee80211s_init(void)
{
@@ -163,7 +158,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
return -ENOMEM;
sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
for (i = 0; i < RMC_BUCKETS; i++)
- INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list);
+ INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
return 0;
}
@@ -177,7 +172,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
return;
for (i = 0; i < RMC_BUCKETS; i++)
- list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) {
+ list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
list_del(&p->list);
kmem_cache_free(rm_cache, p);
}
@@ -210,7 +205,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
/* Don't care about endianness since only match matters */
memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
- list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) {
+ list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
++entries;
if (time_after(jiffies, p->exp_time) ||
(entries == RMC_QUEUE_MAX_LEN)) {
@@ -229,7 +224,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
p->seqnum = seqnum;
p->exp_time = jiffies + RMC_TIMEOUT;
memcpy(p->sa, sa, ETH_ALEN);
- list_add(&p->list, &rmc->bucket[idx].list);
+ list_add(&p->list, &rmc->bucket[idx]);
return 0;
}
@@ -607,6 +602,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
+ u32 changed = BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED |
+ BSS_CHANGED_HT |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_BEACON_INT;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
local->fif_other_bss++;
/* mesh ifaces must set allmulti to forward mcast traffic */
@@ -625,14 +626,16 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.ht_operation_mode =
ifmsh->mshcfg.ht_opmode;
sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
+ sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.basic_rates =
- ieee80211_mandatory_rates(sdata->local,
- ieee80211_get_sdata_band(sdata));
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
- BSS_CHANGED_BEACON_ENABLED |
- BSS_CHANGED_HT |
- BSS_CHANGED_BASIC_RATES |
- BSS_CHANGED_BEACON_INT);
+ ieee80211_mandatory_rates(local, band);
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ sdata->vif.bss_conf.use_short_slot = true;
+ changed |= BSS_CHANGED_ERP_SLOT;
+ }
+
+ ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(sdata->dev);
}
@@ -646,10 +649,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
/* stop the beacon */
ifmsh->mesh_id_len = 0;
+ sdata->vif.bss_conf.enable_beacon = false;
+ clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
/* flush STAs and mpaths on this iface */
- sta_info_flush(sdata->local, sdata);
+ sta_info_flush(sdata);
mesh_path_flush_by_iface(sdata);
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
@@ -805,6 +810,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ static u8 zero_addr[ETH_ALEN] = {};
setup_timer(&ifmsh->housekeeping_timer,
ieee80211_mesh_housekeeping_timer,
@@ -830,4 +836,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
spin_lock_init(&ifmsh->sync_offset_lock);
+
+ sdata->vif.bss_conf.bssid = zero_addr;
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 7c9215fb2ac8..84c28c6101cd 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -184,7 +184,7 @@ struct rmc_entry {
};
struct mesh_rmc {
- struct rmc_entry bucket[RMC_BUCKETS];
+ struct list_head bucket[RMC_BUCKETS];
u32 idx_mask;
};
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7753a9ca98a6..d90c07b1795f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
- struct ieee80211_supported_band *sband)
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_vht_cap *ap_vht_cap)
{
u8 *pos;
u32 cap;
struct ieee80211_sta_vht_cap vht_cap;
+ int i;
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
@@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
+ /*
+ * Some APs apparently get confused if our capabilities are better
+ * than theirs, so restrict what we advertise in the assoc request.
+ */
+ if (!(ap_vht_cap->vht_cap_info &
+ cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
+ cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+
+ if (!(ap_vht_cap->vht_cap_info &
+ cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC)))
+ cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_RXSTBC_3 |
+ IEEE80211_VHT_CAP_RXSTBC_4);
+
+ for (i = 0; i < 8; i++) {
+ int shift = i * 2;
+ u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift;
+ u16 ap_mcs, our_mcs;
+
+ ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) &
+ mask) >> shift;
+ our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
+ mask) >> shift;
+
+ switch (ap_mcs) {
+ default:
+ if (our_mcs <= ap_mcs)
+ break;
+ /* fall through */
+ case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+ vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask);
+ vht_cap.vht_mcs.rx_mcs_map |=
+ cpu_to_le16(ap_mcs << shift);
+ }
+ }
+
/* reserve and fill IE */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
sband, chan, sdata->smps_mode);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
- ieee80211_add_vht_ie(sdata, skb, sband);
+ ieee80211_add_vht_ie(sdata, skb, sband,
+ &assoc_data->ap_vht_cap);
/* if present, add any custom non-vendor IEs that go after HT */
if (assoc_data->ie_len && assoc_data->ie) {
@@ -1074,12 +1113,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
if (beaconint_us > latency) {
local->ps_sdata = NULL;
} else {
- struct ieee80211_bss *bss;
int maxslp = 1;
- u8 dtimper;
-
- bss = (void *)found->u.mgd.associated->priv;
- dtimper = bss->dtim_period;
+ u8 dtimper = found->u.mgd.dtim_period;
/* If the TIM IE is invalid, pretend the value is 1 */
if (!dtimper)
@@ -1410,10 +1445,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_led_assoc(local, 1);
- if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
- bss_conf->dtim_period = bss->dtim_period;
- else
+ if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+ /*
+ * If the AP is buggy we may get here with no DTIM period
+ * known, so assume it's 1 which is the only safe assumption
+ * in that case, although if the TIM IE is broken powersave
+ * probably just won't work at all.
+ */
+ bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
+ } else {
bss_conf->dtim_period = 0;
+ }
bss_conf->assoc = 1;
@@ -1483,7 +1525,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(sdata, ifmgd->bssid);
if (sta) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, false);
+ ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
}
mutex_unlock(&local->sta_mtx);
@@ -1518,7 +1560,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(ifmgd->bssid, 0, ETH_ALEN);
/* remove AP and TDLS peers */
- sta_info_flush(local, sdata);
+ sta_info_flush_defer(sdata);
/* finally reset all BSS / config parameters */
changed |= ieee80211_reset_erp_info(sdata);
@@ -1562,6 +1604,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.timers_running = 0;
+ sdata->vif.bss_conf.dtim_period = 0;
+
ifmgd->flags = 0;
ieee80211_vif_release_channel(sdata);
}
@@ -2364,8 +2408,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
- struct ieee802_11_elems *elems,
- bool beacon)
+ struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
int freq;
@@ -2373,11 +2416,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel;
bool need_ps = false;
- if (sdata->u.mgd.associated &&
- ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) {
- bss = (void *)sdata->u.mgd.associated->priv;
+ if ((sdata->u.mgd.associated &&
+ ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
+ (sdata->u.mgd.assoc_data &&
+ ether_addr_equal(mgmt->bssid,
+ sdata->u.mgd.assoc_data->bss->bssid))) {
/* not previously set so we may need to recalc */
- need_ps = !bss->dtim_period;
+ need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
+
+ if (elems->tim && !elems->parse_error) {
+ struct ieee80211_tim_ie *tim_ie = elems->tim;
+ sdata->u.mgd.dtim_period = tim_ie->dtim_period;
+ }
}
if (elems->ds_params && elems->ds_params_len == 1)
@@ -2392,7 +2442,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
return;
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
- channel, beacon);
+ channel);
if (bss)
ieee80211_rx_bss_put(local, bss);
@@ -2435,7 +2485,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems);
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
if (ifmgd->associated &&
ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
@@ -2516,8 +2566,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.beacon.variable,
len - baselen, &elems);
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
- false);
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ifmgd->assoc_data->have_beacon = true;
ifmgd->assoc_data->sent_assoc = false;
/* continue assoc process */
@@ -2670,8 +2719,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true;
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
- true);
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
elems.wmm_param_len))
@@ -3744,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_mgd_assoc_data *assoc_data;
struct ieee80211_supported_band *sband;
- const u8 *ssidie, *ht_ie;
+ const u8 *ssidie, *ht_ie, *vht_ie;
int i, err;
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
@@ -3863,6 +3911,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+ vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
+ if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
+ memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
+ sizeof(struct ieee80211_vht_cap));
+ else
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported &&
@@ -3896,20 +3950,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* kick off associate process */
ifmgd->assoc_data = assoc_data;
+ ifmgd->dtim_period = 0;
err = ieee80211_prep_connection(sdata, req->bss, true);
if (err)
goto err_clear;
- if (!bss->dtim_period &&
- sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
- /*
- * Wait up to one beacon interval ...
- * should this be more if we miss one?
- */
- sdata_info(sdata, "waiting for beacon from %pM\n",
- ifmgd->bssid);
- assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
+ if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+ const struct cfg80211_bss_ies *beacon_ies;
+
+ rcu_read_lock();
+ beacon_ies = rcu_dereference(req->bss->beacon_ies);
+ if (!beacon_ies) {
+ /*
+ * Wait up to one beacon interval ...
+ * should this be more if we miss one?
+ */
+ sdata_info(sdata, "waiting for beacon from %pM\n",
+ ifmgd->bssid);
+ assoc_data->timeout =
+ TU_TO_EXP_TIME(req->bss->beacon_interval);
+ } else {
+ const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
+ beacon_ies->data,
+ beacon_ies->len);
+ if (tim_ie && tim_ie[1] >=
+ sizeof(struct ieee80211_tim_ie)) {
+ const struct ieee80211_tim_ie *tim;
+ tim = (void *)(tim_ie + 2);
+ ifmgd->dtim_period = tim->dtim_period;
+ }
+ assoc_data->have_beacon = true;
+ assoc_data->sent_assoc = false;
+ assoc_data->timeout = jiffies;
+ }
+ rcu_read_unlock();
} else {
assoc_data->have_beacon = true;
assoc_data->sent_assoc = false;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index a5379aea7d09..1430b48600fc 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -126,11 +126,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
/* Check to see if we should disable beaconing. */
- if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC ||
- sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+ if (sdata->vif.bss_conf.enable_beacon) {
+ set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
+ &sdata->state);
+ sdata->vif.bss_conf.enable_beacon = false;
ieee80211_bss_info_change_notify(
sdata, BSS_CHANGED_BEACON_ENABLED);
+ }
if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
netif_tx_stop_all_queues(sdata->dev);
@@ -183,11 +185,12 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
netif_tx_wake_all_queues(sdata->dev);
}
- if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC ||
- sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+ if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
+ &sdata->state)) {
+ sdata->vif.bss_conf.enable_beacon = true;
ieee80211_bss_info_change_notify(
sdata, BSS_CHANGED_BEACON_ENABLED);
+ }
}
mutex_unlock(&local->iflist_mtx);
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 79a48f37d409..e45b83610e85 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -7,25 +7,23 @@
#include "led.h"
/* return value indicates whether the driver should be further notified */
-static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
{
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ieee80211_sta_quiesce(sdata);
- return true;
+ break;
case NL80211_IFTYPE_ADHOC:
ieee80211_ibss_quiesce(sdata);
- return true;
+ break;
case NL80211_IFTYPE_MESH_POINT:
ieee80211_mesh_quiesce(sdata);
- return true;
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_MONITOR:
- /* don't tell driver about this */
- return false;
+ break;
default:
- return true;
+ break;
}
+
+ cancel_work_sync(&sdata->work);
}
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
@@ -44,7 +42,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, true);
+ ieee80211_sta_tear_down_BA_sessions(
+ sta, AGG_STOP_LOCAL_REQUEST);
}
mutex_unlock(&local->sta_mtx);
}
@@ -94,10 +93,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
WARN_ON(err != 1);
local->wowlan = false;
} else {
- list_for_each_entry(sdata, &local->interfaces, list) {
- cancel_work_sync(&sdata->work);
- ieee80211_quiesce(sdata);
- }
+ list_for_each_entry(sdata, &local->interfaces, list)
+ if (ieee80211_sdata_running(sdata))
+ ieee80211_quiesce(sdata);
goto suspend;
}
}
@@ -124,17 +122,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
- cancel_work_sync(&sdata->work);
+ static u8 zero_addr[ETH_ALEN] = {};
+ u32 changed = 0;
- if (!ieee80211_quiesce(sdata))
+ if (!ieee80211_sdata_running(sdata))
continue;
- if (!ieee80211_sdata_running(sdata))
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* skip these */
continue;
+ case NL80211_IFTYPE_STATION:
+ if (sdata->vif.bss_conf.assoc)
+ changed = BSS_CHANGED_ASSOC |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_IDLE;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ if (sdata->vif.bss_conf.enable_beacon)
+ changed = BSS_CHANGED_BEACON_ENABLED;
+ break;
+ default:
+ break;
+ }
+
+ ieee80211_quiesce(sdata);
+
+ sdata->suspend_bss_conf = sdata->vif.bss_conf;
+ memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf));
+ sdata->vif.bss_conf.idle = true;
+ if (sdata->suspend_bss_conf.bssid)
+ sdata->vif.bss_conf.bssid = zero_addr;
- /* disable beaconing */
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_BEACON_ENABLED);
+ /* disable beaconing or remove association */
+ ieee80211_bss_info_change_notify(sdata, changed);
if (sdata->vif.type == NL80211_IFTYPE_AP &&
rcu_access_pointer(sdata->u.ap.beacon))
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 580704eba8b8..a19089565c4b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2353,7 +2353,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sdata->vif.type != NL80211_IFTYPE_ADHOC)
break;
- /* verify action & smps_control are present */
+ /* verify action & smps_control/chanwidth are present */
if (len < IEEE80211_MIN_ACTION_SIZE + 2)
goto invalid;
@@ -2392,6 +2392,35 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
IEEE80211_RC_SMPS_CHANGED);
goto handled;
}
+ case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
+ struct ieee80211_supported_band *sband;
+ u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
+ bool old_40mhz, new_40mhz;
+
+ /* If it doesn't support 40 MHz it can't change ... */
+ if (!rx->sta->supports_40mhz)
+ goto handled;
+
+ old_40mhz = rx->sta->sta.ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY;
+
+ if (old_40mhz == new_40mhz)
+ goto handled;
+
+ if (new_40mhz)
+ rx->sta->sta.ht_cap.cap |=
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ rx->sta->sta.ht_cap.cap &=
+ ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ sband = rx->local->hw.wiphy->bands[status->band];
+
+ rate_control_rate_update(local, sband, rx->sta,
+ IEEE80211_RC_BW_CHANGED);
+ goto handled;
+ }
default:
goto invalid;
}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 8ed83dcc149f..06cbe26892a8 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -65,12 +65,11 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)
struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
- struct ieee80211_mgmt *mgmt,
- size_t len,
+ struct ieee80211_mgmt *mgmt, size_t len,
struct ieee802_11_elems *elems,
- struct ieee80211_channel *channel,
- bool beacon)
+ struct ieee80211_channel *channel)
{
+ bool beacon = ieee80211_is_beacon(mgmt->frame_control);
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
int clen, srlen;
@@ -113,18 +112,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_ERP;
}
- if (elems->tim && (!elems->parse_error ||
- !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
- struct ieee80211_tim_ie *tim_ie = elems->tim;
- bss->dtim_period = tim_ie->dtim_period;
- if (!elems->parse_error)
- bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
- }
-
- /* If the beacon had no TIM IE, or it was invalid, use 1 */
- if (beacon && !bss->dtim_period)
- bss->dtim_period = 1;
-
/* replace old supported rates if we get new values */
if (!elems->parse_error ||
!(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
@@ -215,7 +202,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
bss = ieee80211_bss_info_update(local, rx_status,
mgmt, skb->len, &elems,
- channel, beacon);
+ channel);
if (bss)
ieee80211_rx_bss_put(local, bss);
}
@@ -832,9 +819,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
return res;
}
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
- const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan)
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+ const u8 *ssid, u8 ssid_len,
+ struct ieee80211_channel *chan)
{
struct ieee80211_local *local = sdata->local;
int ret = -EBUSY;
@@ -848,22 +835,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
/* fill internal scan request */
if (!chan) {
- int i, nchan = 0;
+ int i, max_n;
+ int n_ch = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band])
continue;
- for (i = 0;
- i < local->hw.wiphy->bands[band]->n_channels;
- i++) {
- local->int_scan_req->channels[nchan] =
+
+ max_n = local->hw.wiphy->bands[band]->n_channels;
+ for (i = 0; i < max_n; i++) {
+ struct ieee80211_channel *tmp_ch =
&local->hw.wiphy->bands[band]->channels[i];
- nchan++;
+
+ if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_DISABLED))
+ continue;
+
+ local->int_scan_req->channels[n_ch] = tmp_ch;
+ n_ch++;
}
}
- local->int_scan_req->n_channels = nchan;
+ if (WARN_ON_ONCE(n_ch == 0))
+ goto unlock;
+
+ local->int_scan_req->n_channels = n_ch;
} else {
+ if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_DISABLED)))
+ goto unlock;
+
local->int_scan_req->channels[0] = chan;
local->int_scan_req->n_channels = 1;
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f3e502502fee..9d864ed5f3da 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,9 +91,8 @@ static int sta_info_hash_del(struct ieee80211_local *local,
return -ENOENT;
}
-static void free_sta_work(struct work_struct *wk)
+static void cleanup_single_sta(struct sta_info *sta)
{
- struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
int ac, i;
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -105,6 +104,16 @@ static void free_sta_work(struct work_struct *wk)
* neither mac80211 nor the driver can reference this
* sta struct any more except by still existing timers
* associated with this station that we clean up below.
+ *
+ * Note though that this still uses the sdata and even
+ * calls the driver in AP and mesh mode, so interfaces
+ * of those types mush use call sta_info_flush_cleanup()
+ * (typically via sta_info_flush()) before deconfiguring
+ * the driver.
+ *
+ * In station mode, nothing happens here so it doesn't
+ * have to (and doesn't) do that, this is intentional to
+ * speed up roaming.
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
@@ -153,11 +162,35 @@ static void free_sta_work(struct work_struct *wk)
sta_info_free(local, sta);
}
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ while (!list_empty(&sdata->cleanup_stations)) {
+ sta = list_first_entry(&sdata->cleanup_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+
+ cleanup_single_sta(sta);
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ }
+
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+}
+
static void free_sta_rcu(struct rcu_head *h)
{
struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
- ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+ spin_lock(&sdata->cleanup_stations_lock);
+ list_add_tail(&sta->list, &sdata->cleanup_stations);
+ spin_unlock(&sdata->cleanup_stations_lock);
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
}
/* protected by RCU */
@@ -310,7 +343,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->lock);
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
- INIT_WORK(&sta->free_sta_wk, free_sta_work);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
@@ -752,7 +784,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
* will be sufficient.
*/
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, false);
+ ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
ret = sta_info_hash_del(local, sta);
if (ret)
@@ -862,21 +894,13 @@ void sta_info_init(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local)
{
- del_timer(&local->sta_cleanup);
- sta_info_flush(local, NULL);
+ del_timer_sync(&local->sta_cleanup);
}
-/**
- * sta_info_flush - flush matching STA entries from the STA table
- *
- * Returns the number of removed STA entries.
- *
- * @local: local interface data
- * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
- */
-int sta_info_flush(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+
+int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
int ret = 0;
@@ -884,7 +908,7 @@ int sta_info_flush(struct ieee80211_local *local,
mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
- if (!sdata || sdata == sta->sdata) {
+ if (sdata == sta->sdata) {
WARN_ON(__sta_info_destroy(sta));
ret++;
}
@@ -894,6 +918,12 @@ int sta_info_flush(struct ieee80211_local *local,
return ret;
}
+void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
+{
+ ieee80211_cleanup_sdata_stas(sdata);
+ cancel_work_sync(&sdata->cleanup_stations_wk);
+}
+
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time)
{
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 1489bca9ea97..af7d78aa5523 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -92,6 +92,13 @@ enum ieee80211_sta_info_flags {
#define HT_AGG_STATE_WANT_START 4
#define HT_AGG_STATE_WANT_STOP 5
+enum ieee80211_agg_stop_reason {
+ AGG_STOP_DECLINED,
+ AGG_STOP_LOCAL_REQUEST,
+ AGG_STOP_PEER_REQUEST,
+ AGG_STOP_DESTROY_STA,
+};
+
/**
* struct tid_ampdu_tx - TID aggregation information (Tx).
*
@@ -299,7 +306,6 @@ struct sta_info {
spinlock_t lock;
struct work_struct drv_unblock_wk;
- struct work_struct free_sta_wk;
u16 listen_interval;
@@ -549,8 +555,39 @@ void sta_info_recalc_tim(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
-int sta_info_flush(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata);
+int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata);
+
+/**
+ * sta_info_flush_cleanup - flush the sta_info cleanup queue
+ * @sdata: the interface
+ *
+ * Flushes the sta_info cleanup queue for a given interface;
+ * this is necessary before the interface is removed or, for
+ * AP/mesh interfaces, before it is deconfigured.
+ *
+ * Note an rcu_barrier() must precede the function, after all
+ * stations have been flushed/removed to ensure the call_rcu()
+ * calls that add stations to the cleanup queue have completed.
+ */
+void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);
+
+/**
+ * sta_info_flush - flush matching STA entries from the STA table
+ *
+ * Returns the number of removed STA entries.
+ *
+ * @sdata: sdata to remove all stations from
+ */
+static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)
+{
+ int ret = sta_info_flush_defer(sdata);
+
+ rcu_barrier();
+ sta_info_flush_cleanup(sdata);
+
+ return ret;
+}
+
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo);
@@ -563,4 +600,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
+
#endif /* STA_INFO_H */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index f11e8c540db4..1b9420730d8c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1526,6 +1526,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_IDLE |
BSS_CHANGED_TXPOWER;
+#ifdef CONFIG_PM
+ if (local->resuming)
+ sdata->vif.bss_conf = sdata->suspend_bss_conf;
+#endif
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
changed |= BSS_CHANGED_ASSOC |
@@ -1550,9 +1555,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
- changed |= BSS_CHANGED_BEACON |
- BSS_CHANGED_BEACON_ENABLED;
- ieee80211_bss_info_change_notify(sdata, changed);
+ if (sdata->vif.bss_conf.enable_beacon) {
+ changed |= BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED;
+ ieee80211_bss_info_change_notify(sdata, changed);
+ }
break;
case NL80211_IFTYPE_WDS:
break;
@@ -1632,7 +1639,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
- ieee80211_sta_tear_down_BA_sessions(sta, true);
+ ieee80211_sta_tear_down_BA_sessions(
+ sta, AGG_STOP_LOCAL_REQUEST);
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
}
@@ -1864,7 +1872,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
}
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
- u32 cap)
+ u32 cap)
{
__le32 tmp;
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 324e8d851dc4..a4a14e8f55cc 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -46,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
return err;
}
+
+void cfg80211_ch_switch_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_ch_switch_notify(dev, chandef);
+
+ wdev_lock(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO))
+ goto out;
+
+ wdev->channel = chandef->chan;
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+out:
+ wdev_unlock(wdev);
+ return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_spurious_frame(dev, addr);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+ trace_cfg80211_return_bool(false);
+ return false;
+ }
+ ret = nl80211_unexpected_frame(dev, addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
+
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+ trace_cfg80211_return_bool(false);
+ return false;
+ }
+ ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index a7990bb16529..396373f3ec26 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -76,6 +76,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
return false;
if (!chandef->center_freq2)
return false;
+ /* adjacent is not allowed -- that's a 160 MHz channel */
+ if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
+ chandef->center_freq2 - chandef->center_freq1 == 80)
+ return false;
break;
case NL80211_CHAN_WIDTH_80:
if (chandef->center_freq1 != control_freq + 30 &&
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 14d990400354..747dd9365a44 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -57,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
{
struct cfg80211_registered_device *result = NULL, *rdev;
- if (!wiphy_idx_valid(wiphy_idx))
- return NULL;
-
assert_cfg80211_lock();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
@@ -74,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
int get_wiphy_idx(struct wiphy *wiphy)
{
- struct cfg80211_registered_device *rdev;
- if (!wiphy)
- return WIPHY_IDX_STALE;
- rdev = wiphy_to_dev(wiphy);
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
return rdev->wiphy_idx;
}
@@ -86,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
{
struct cfg80211_registered_device *rdev;
- if (!wiphy_idx_valid(wiphy_idx))
- return NULL;
-
assert_cfg80211_lock();
rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
@@ -309,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy_idx = wiphy_counter++;
- if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) {
+ if (unlikely(rdev->wiphy_idx < 0)) {
wiphy_counter--;
mutex_unlock(&cfg80211_mutex);
/* ugh, wrapped! */
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 3563097169cb..f342267e3620 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -18,6 +18,9 @@
#include <net/cfg80211.h>
#include "reg.h"
+
+#define WIPHY_IDX_INVALID -1
+
struct cfg80211_registered_device {
const struct cfg80211_ops *ops;
struct list_head list;
@@ -86,7 +89,7 @@ struct cfg80211_registered_device {
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
- struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
+ struct wiphy wiphy __aligned(NETDEV_ALIGN);
};
static inline
@@ -96,13 +99,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}
-/* Note 0 is valid, hence phy0 */
-static inline
-bool wiphy_idx_valid(int wiphy_idx)
-{
- return wiphy_idx >= 0;
-}
-
static inline void
cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
{
@@ -126,12 +122,6 @@ static inline void assert_cfg80211_lock(void)
lockdep_assert_held(&cfg80211_mutex);
}
-/*
- * You can use this to mark a wiphy_idx as not having an associated wiphy.
- * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL
- */
-#define WIPHY_IDX_STALE -1
-
struct cfg80211_internal_bss {
struct list_head list;
struct rb_node rbn;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 5e8123ee63fd..461e692cdfec 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -987,65 +987,3 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-
-void cfg80211_ch_switch_notify(struct net_device *dev,
- struct cfg80211_chan_def *chandef)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_ch_switch_notify(dev, chandef);
-
- wdev_lock(wdev);
-
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO))
- goto out;
-
- wdev->channel = chandef->chan;
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
- wdev_unlock(wdev);
- return;
-}
-EXPORT_SYMBOL(cfg80211_ch_switch_notify);
-
-bool cfg80211_rx_spurious_frame(struct net_device *dev,
- const u8 *addr, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- bool ret;
-
- trace_cfg80211_rx_spurious_frame(dev, addr);
-
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
- trace_cfg80211_return_bool(false);
- return false;
- }
- ret = nl80211_unexpected_frame(dev, addr, gfp);
- trace_cfg80211_return_bool(ret);
- return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
-
-bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
- const u8 *addr, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- bool ret;
-
- trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
-
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO &&
- wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
- trace_cfg80211_return_bool(false);
- return false;
- }
- ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
- trace_cfg80211_return_bool(ret);
- return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f45706adaf34..9bd8340af999 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3188,13 +3188,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
}
- if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
- params.listen_interval =
- nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
-
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
- params.ht_capa =
- nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] ||
+ info->attrs[NL80211_ATTR_HT_CAPABILITY])
+ return -EINVAL;
if (!rdev->ops->change_station)
return -EOPNOTSUPP;
@@ -3231,11 +3227,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
/* accept only the listed bits */
if (params.sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP)))
return -EINVAL;
+ /* but authenticated/associated only if driver handles it */
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+ params.sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED)))
+ return -EINVAL;
+
+ /* reject other things that can't change */
+ if (params.supported_rates)
+ return -EINVAL;
+
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
@@ -3255,10 +3265,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
/* disallow things sta doesn't support */
if (params.plink_action)
return -EINVAL;
- if (params.ht_capa)
- return -EINVAL;
- if (params.listen_interval >= 0)
- return -EINVAL;
/* reject any changes other than AUTHORIZED */
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL;
@@ -3267,9 +3273,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
/* disallow things mesh doesn't support */
if (params.vlan)
return -EINVAL;
- if (params.ht_capa)
- return -EINVAL;
- if (params.listen_interval >= 0)
+ if (params.supported_rates)
return -EINVAL;
/*
* No special handling for TDLS here -- the userspace
@@ -3393,17 +3397,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* but don't bother the driver with it */
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ /* allow authenticated/associated only if driver handles it */
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+ params.sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED)))
+ return -EINVAL;
+
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
break;
case NL80211_IFTYPE_MESH_POINT:
+ /* associated is disallowed */
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+ return -EINVAL;
/* TDLS peers cannot be added */
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
return -EINVAL;
break;
case NL80211_IFTYPE_STATION:
+ /* associated is disallowed */
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+ return -EINVAL;
/* Only TDLS peers can be added */
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
return -EINVAL;
@@ -3787,12 +3805,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
* window between nl80211_init() and regulatory_init(), if that is
* even possible.
*/
- mutex_lock(&cfg80211_mutex);
- if (unlikely(!cfg80211_regdomain)) {
- mutex_unlock(&cfg80211_mutex);
+ if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
return -EINPROGRESS;
- }
- mutex_unlock(&cfg80211_mutex);
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
@@ -4152,6 +4166,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
{
+ const struct ieee80211_regdomain *regdom;
struct sk_buff *msg;
void *hdr = NULL;
struct nlattr *nl_reg_rules;
@@ -4174,35 +4189,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
if (!hdr)
goto put_failure;
- if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
- cfg80211_regdomain->alpha2) ||
- (cfg80211_regdomain->dfs_region &&
- nla_put_u8(msg, NL80211_ATTR_DFS_REGION,
- cfg80211_regdomain->dfs_region)))
- goto nla_put_failure;
-
if (reg_last_request_cell_base() &&
nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
NL80211_USER_REG_HINT_CELL_BASE))
goto nla_put_failure;
+ rcu_read_lock();
+ regdom = rcu_dereference(cfg80211_regdomain);
+
+ if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
+ (regdom->dfs_region &&
+ nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
+ goto nla_put_failure_rcu;
+
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
if (!nl_reg_rules)
- goto nla_put_failure;
+ goto nla_put_failure_rcu;
- for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
+ for (i = 0; i < regdom->n_reg_rules; i++) {
struct nlattr *nl_reg_rule;
const struct ieee80211_reg_rule *reg_rule;
const struct ieee80211_freq_range *freq_range;
const struct ieee80211_power_rule *power_rule;
- reg_rule = &cfg80211_regdomain->reg_rules[i];
+ reg_rule = &regdom->reg_rules[i];
freq_range = &reg_rule->freq_range;
power_rule = &reg_rule->power_rule;
nl_reg_rule = nla_nest_start(msg, i);
if (!nl_reg_rule)
- goto nla_put_failure;
+ goto nla_put_failure_rcu;
if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
reg_rule->flags) ||
@@ -4216,10 +4232,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
power_rule->max_antenna_gain) ||
nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
power_rule->max_eirp))
- goto nla_put_failure;
+ goto nla_put_failure_rcu;
nla_nest_end(msg, nl_reg_rule);
}
+ rcu_read_unlock();
nla_nest_end(msg, nl_reg_rules);
@@ -4227,6 +4244,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
err = genlmsg_reply(msg, info);
goto out;
+nla_put_failure_rcu:
+ rcu_read_unlock();
nla_put_failure:
genlmsg_cancel(msg, hdr);
put_failure:
@@ -4259,27 +4278,18 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
- rem_reg_rules) {
+ rem_reg_rules) {
num_rules++;
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
return -EINVAL;
}
- mutex_lock(&cfg80211_mutex);
-
- if (!reg_is_valid_request(alpha2)) {
- r = -EINVAL;
- goto bad_reg;
- }
-
size_of_regd = sizeof(struct ieee80211_regdomain) +
- (num_rules * sizeof(struct ieee80211_reg_rule));
+ num_rules * sizeof(struct ieee80211_reg_rule);
rd = kzalloc(size_of_regd, GFP_KERNEL);
- if (!rd) {
- r = -ENOMEM;
- goto bad_reg;
- }
+ if (!rd)
+ return -ENOMEM;
rd->n_reg_rules = num_rules;
rd->alpha2[0] = alpha2[0];
@@ -4293,10 +4303,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rd->dfs_region = dfs_region;
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
- rem_reg_rules) {
+ rem_reg_rules) {
nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
- nla_data(nl_reg_rule), nla_len(nl_reg_rule),
- reg_rule_policy);
+ nla_data(nl_reg_rule), nla_len(nl_reg_rule),
+ reg_rule_policy);
r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
if (r)
goto bad_reg;
@@ -4309,16 +4319,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
}
}
- BUG_ON(rule_idx != num_rules);
+ mutex_lock(&cfg80211_mutex);
r = set_regdom(rd);
-
+ /* set_regdom took ownership */
+ rd = NULL;
mutex_unlock(&cfg80211_mutex);
- return r;
-
bad_reg:
- mutex_unlock(&cfg80211_mutex);
kfree(rd);
return r;
}
@@ -8051,7 +8059,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
goto nla_put_failure;
}
- if (wiphy_idx_valid(request->wiphy_idx) &&
+ if (request->wiphy_idx != WIPHY_IDX_INVALID &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
goto nla_put_failure;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6e5308998e30..0b35de001937 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -48,7 +48,6 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/list.h>
-#include <linux/random.h>
#include <linux/ctype.h>
#include <linux/nl80211.h>
#include <linux/platform_device.h>
@@ -66,6 +65,13 @@
#define REG_DBG_PRINT(args...)
#endif
+enum reg_request_treatment {
+ REG_REQ_OK,
+ REG_REQ_IGNORE,
+ REG_REQ_INTERSECT,
+ REG_REQ_ALREADY_SET,
+};
+
static struct regulatory_request core_request_world = {
.initiator = NL80211_REGDOM_SET_BY_CORE,
.alpha2[0] = '0',
@@ -76,7 +82,8 @@ static struct regulatory_request core_request_world = {
};
/* Receipt of information from last regulatory request */
-static struct regulatory_request *last_request = &core_request_world;
+static struct regulatory_request __rcu *last_request =
+ (void __rcu *)&core_request_world;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
@@ -88,16 +95,16 @@ static struct device_type reg_device_type = {
/*
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
- * information to give us an alpha2
+ * information to give us an alpha2.
*/
-const struct ieee80211_regdomain *cfg80211_regdomain;
+const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
/*
* Protects static reg.c components:
- * - cfg80211_world_regdom
- * - cfg80211_regdom
- * - last_request
- * - reg_num_devs_support_basehint
+ * - cfg80211_regdomain (if not used with RCU)
+ * - cfg80211_world_regdom
+ * - last_request (if not used with RCU)
+ * - reg_num_devs_support_basehint
*/
static DEFINE_MUTEX(reg_mutex);
@@ -112,6 +119,31 @@ static inline void assert_reg_lock(void)
lockdep_assert_held(&reg_mutex);
}
+static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
+{
+ return rcu_dereference_protected(cfg80211_regdomain,
+ lockdep_is_held(&reg_mutex));
+}
+
+static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+{
+ return rcu_dereference_protected(wiphy->regd,
+ lockdep_is_held(&reg_mutex));
+}
+
+static void rcu_free_regdom(const struct ieee80211_regdomain *r)
+{
+ if (!r)
+ return;
+ kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);
+}
+
+static struct regulatory_request *get_last_request(void)
+{
+ return rcu_dereference_protected(last_request,
+ lockdep_is_held(&reg_mutex));
+}
+
/* Used to queue up regulatory hints */
static LIST_HEAD(reg_requests_list);
static spinlock_t reg_requests_lock;
@@ -177,28 +209,37 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
-static void reset_regdomains(bool full_reset)
+static void reset_regdomains(bool full_reset,
+ const struct ieee80211_regdomain *new_regdom)
{
+ const struct ieee80211_regdomain *r;
+ struct regulatory_request *lr;
+
+ assert_reg_lock();
+
+ r = get_cfg80211_regdom();
+
/* avoid freeing static information or freeing something twice */
- if (cfg80211_regdomain == cfg80211_world_regdom)
- cfg80211_regdomain = NULL;
+ if (r == cfg80211_world_regdom)
+ r = NULL;
if (cfg80211_world_regdom == &world_regdom)
cfg80211_world_regdom = NULL;
- if (cfg80211_regdomain == &world_regdom)
- cfg80211_regdomain = NULL;
+ if (r == &world_regdom)
+ r = NULL;
- kfree(cfg80211_regdomain);
- kfree(cfg80211_world_regdom);
+ rcu_free_regdom(r);
+ rcu_free_regdom(cfg80211_world_regdom);
cfg80211_world_regdom = &world_regdom;
- cfg80211_regdomain = NULL;
+ rcu_assign_pointer(cfg80211_regdomain, new_regdom);
if (!full_reset)
return;
- if (last_request != &core_request_world)
- kfree(last_request);
- last_request = &core_request_world;
+ lr = get_last_request();
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
+ rcu_assign_pointer(last_request, &core_request_world);
}
/*
@@ -207,30 +248,29 @@ static void reset_regdomains(bool full_reset)
*/
static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{
- BUG_ON(!last_request);
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ WARN_ON(!lr);
- reset_regdomains(false);
+ reset_regdomains(false, rd);
cfg80211_world_regdom = rd;
- cfg80211_regdomain = rd;
}
bool is_world_regdom(const char *alpha2)
{
if (!alpha2)
return false;
- if (alpha2[0] == '0' && alpha2[1] == '0')
- return true;
- return false;
+ return alpha2[0] == '0' && alpha2[1] == '0';
}
static bool is_alpha2_set(const char *alpha2)
{
if (!alpha2)
return false;
- if (alpha2[0] != 0 && alpha2[1] != 0)
- return true;
- return false;
+ return alpha2[0] && alpha2[1];
}
static bool is_unknown_alpha2(const char *alpha2)
@@ -241,9 +281,7 @@ static bool is_unknown_alpha2(const char *alpha2)
* Special case where regulatory domain was built by driver
* but a specific alpha2 cannot be determined
*/
- if (alpha2[0] == '9' && alpha2[1] == '9')
- return true;
- return false;
+ return alpha2[0] == '9' && alpha2[1] == '9';
}
static bool is_intersected_alpha2(const char *alpha2)
@@ -255,39 +293,30 @@ static bool is_intersected_alpha2(const char *alpha2)
* result of an intersection between two regulatory domain
* structures
*/
- if (alpha2[0] == '9' && alpha2[1] == '8')
- return true;
- return false;
+ return alpha2[0] == '9' && alpha2[1] == '8';
}
static bool is_an_alpha2(const char *alpha2)
{
if (!alpha2)
return false;
- if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
- return true;
- return false;
+ return isalpha(alpha2[0]) && isalpha(alpha2[1]);
}
static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
{
if (!alpha2_x || !alpha2_y)
return false;
- if (alpha2_x[0] == alpha2_y[0] &&
- alpha2_x[1] == alpha2_y[1])
- return true;
- return false;
+ return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];
}
static bool regdom_changes(const char *alpha2)
{
- assert_cfg80211_lock();
+ const struct ieee80211_regdomain *r = get_cfg80211_regdom();
- if (!cfg80211_regdomain)
+ if (!r)
return true;
- if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
- return false;
- return true;
+ return !alpha2_equal(r->alpha2, alpha2);
}
/*
@@ -301,38 +330,36 @@ static bool is_user_regdom_saved(void)
return false;
/* This would indicate a mistake on the design */
- if (WARN((!is_world_regdom(user_alpha2) &&
- !is_an_alpha2(user_alpha2)),
+ if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),
"Unexpected user alpha2: %c%c\n",
- user_alpha2[0],
- user_alpha2[1]))
+ user_alpha2[0], user_alpha2[1]))
return false;
return true;
}
-static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
- const struct ieee80211_regdomain *src_regd)
+static const struct ieee80211_regdomain *
+reg_copy_regd(const struct ieee80211_regdomain *src_regd)
{
struct ieee80211_regdomain *regd;
- int size_of_regd = 0;
+ int size_of_regd;
unsigned int i;
- size_of_regd = sizeof(struct ieee80211_regdomain) +
- ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
+ size_of_regd =
+ sizeof(struct ieee80211_regdomain) +
+ src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
regd = kzalloc(size_of_regd, GFP_KERNEL);
if (!regd)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
for (i = 0; i < src_regd->n_reg_rules; i++)
memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
- sizeof(struct ieee80211_reg_rule));
+ sizeof(struct ieee80211_reg_rule));
- *dst_regd = regd;
- return 0;
+ return regd;
}
#ifdef CONFIG_CFG80211_INTERNAL_REGDB
@@ -347,9 +374,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex);
static void reg_regdb_search(struct work_struct *work)
{
struct reg_regdb_search_request *request;
- const struct ieee80211_regdomain *curdom, *regdom;
- int i, r;
- bool set_reg = false;
+ const struct ieee80211_regdomain *curdom, *regdom = NULL;
+ int i;
mutex_lock(&cfg80211_mutex);
@@ -360,14 +386,11 @@ static void reg_regdb_search(struct work_struct *work)
list);
list_del(&request->list);
- for (i=0; i<reg_regdb_size; i++) {
+ for (i = 0; i < reg_regdb_size; i++) {
curdom = reg_regdb[i];
- if (!memcmp(request->alpha2, curdom->alpha2, 2)) {
- r = reg_copy_regd(&regdom, curdom);
- if (r)
- break;
- set_reg = true;
+ if (alpha2_equal(request->alpha2, curdom->alpha2)) {
+ regdom = reg_copy_regd(curdom);
break;
}
}
@@ -376,7 +399,7 @@ static void reg_regdb_search(struct work_struct *work)
}
mutex_unlock(&reg_regdb_search_mutex);
- if (set_reg)
+ if (!IS_ERR_OR_NULL(regdom))
set_regdom(regdom);
mutex_unlock(&cfg80211_mutex);
@@ -434,15 +457,14 @@ static int call_crda(const char *alpha2)
return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
}
-/* Used by nl80211 before kmalloc'ing our regulatory domain */
-bool reg_is_valid_request(const char *alpha2)
+static bool reg_is_valid_request(const char *alpha2)
{
- assert_cfg80211_lock();
+ struct regulatory_request *lr = get_last_request();
- if (!last_request)
+ if (!lr || lr->processed)
return false;
- return alpha2_equal(last_request->alpha2, alpha2);
+ return alpha2_equal(lr->alpha2, alpha2);
}
/* Sanity check on a regulatory rule */
@@ -460,7 +482,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
- freq_range->max_bandwidth_khz > freq_diff)
+ freq_range->max_bandwidth_khz > freq_diff)
return false;
return true;
@@ -487,8 +509,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
}
static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
- u32 center_freq_khz,
- u32 bw_khz)
+ u32 center_freq_khz, u32 bw_khz)
{
u32 start_freq_khz, end_freq_khz;
@@ -518,7 +539,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
* regulatory rule support for other "bands".
**/
static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
- u32 freq_khz)
+ u32 freq_khz)
{
#define ONE_GHZ_IN_KHZ 1000000
/*
@@ -540,10 +561,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
-static int reg_rules_intersect(
- const struct ieee80211_reg_rule *rule1,
- const struct ieee80211_reg_rule *rule2,
- struct ieee80211_reg_rule *intersected_rule)
+static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
+ const struct ieee80211_reg_rule *rule2,
+ struct ieee80211_reg_rule *intersected_rule)
{
const struct ieee80211_freq_range *freq_range1, *freq_range2;
struct ieee80211_freq_range *freq_range;
@@ -560,11 +580,11 @@ static int reg_rules_intersect(
power_rule = &intersected_rule->power_rule;
freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
- freq_range2->start_freq_khz);
+ freq_range2->start_freq_khz);
freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
- freq_range2->end_freq_khz);
+ freq_range2->end_freq_khz);
freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
- freq_range2->max_bandwidth_khz);
+ freq_range2->max_bandwidth_khz);
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
if (freq_range->max_bandwidth_khz > freq_diff)
@@ -575,7 +595,7 @@ static int reg_rules_intersect(
power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
power_rule2->max_antenna_gain);
- intersected_rule->flags = (rule1->flags | rule2->flags);
+ intersected_rule->flags = rule1->flags | rule2->flags;
if (!is_valid_reg_rule(intersected_rule))
return -EINVAL;
@@ -596,9 +616,9 @@ static int reg_rules_intersect(
* resulting intersection of rules between rd1 and rd2. We will
* kzalloc() this structure for you.
*/
-static struct ieee80211_regdomain *regdom_intersect(
- const struct ieee80211_regdomain *rd1,
- const struct ieee80211_regdomain *rd2)
+static struct ieee80211_regdomain *
+regdom_intersect(const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2)
{
int r, size_of_regd;
unsigned int x, y;
@@ -607,12 +627,7 @@ static struct ieee80211_regdomain *regdom_intersect(
struct ieee80211_reg_rule *intersected_rule;
struct ieee80211_regdomain *rd;
/* This is just a dummy holder to help us count */
- struct ieee80211_reg_rule irule;
-
- /* Uses the stack temporarily for counter arithmetic */
- intersected_rule = &irule;
-
- memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
+ struct ieee80211_reg_rule dummy_rule;
if (!rd1 || !rd2)
return NULL;
@@ -629,11 +644,8 @@ static struct ieee80211_regdomain *regdom_intersect(
rule1 = &rd1->reg_rules[x];
for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
- if (!reg_rules_intersect(rule1, rule2,
- intersected_rule))
+ if (!reg_rules_intersect(rule1, rule2, &dummy_rule))
num_rules++;
- memset(intersected_rule, 0,
- sizeof(struct ieee80211_reg_rule));
}
}
@@ -641,15 +653,15 @@ static struct ieee80211_regdomain *regdom_intersect(
return NULL;
size_of_regd = sizeof(struct ieee80211_regdomain) +
- ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
+ num_rules * sizeof(struct ieee80211_reg_rule);
rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd)
return NULL;
- for (x = 0; x < rd1->n_reg_rules; x++) {
+ for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) {
rule1 = &rd1->reg_rules[x];
- for (y = 0; y < rd2->n_reg_rules; y++) {
+ for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) {
rule2 = &rd2->reg_rules[y];
/*
* This time around instead of using the stack lets
@@ -657,8 +669,7 @@ static struct ieee80211_regdomain *regdom_intersect(
* a memcpy()
*/
intersected_rule = &rd->reg_rules[rule_idx];
- r = reg_rules_intersect(rule1, rule2,
- intersected_rule);
+ r = reg_rules_intersect(rule1, rule2, intersected_rule);
/*
* No need to memset here the intersected rule here as
* we're not using the stack anymore
@@ -699,34 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags)
return channel_flags;
}
-static int freq_reg_info_regd(struct wiphy *wiphy,
- u32 center_freq,
- u32 desired_bw_khz,
- const struct ieee80211_reg_rule **reg_rule,
- const struct ieee80211_regdomain *custom_regd)
+static const struct ieee80211_reg_rule *
+freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+ const struct ieee80211_regdomain *regd)
{
int i;
bool band_rule_found = false;
- const struct ieee80211_regdomain *regd;
bool bw_fits = false;
- if (!desired_bw_khz)
- desired_bw_khz = MHZ_TO_KHZ(20);
-
- regd = custom_regd ? custom_regd : cfg80211_regdomain;
-
- /*
- * Follow the driver's regulatory domain, if present, unless a country
- * IE has been processed or a user wants to help complaince further
- */
- if (!custom_regd &&
- last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- last_request->initiator != NL80211_REGDOM_SET_BY_USER &&
- wiphy->regd)
- regd = wiphy->regd;
-
if (!regd)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
for (i = 0; i < regd->n_reg_rules; i++) {
const struct ieee80211_reg_rule *rr;
@@ -743,33 +736,36 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
- bw_fits = reg_does_bw_fit(fr,
- center_freq,
- desired_bw_khz);
+ bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
- if (band_rule_found && bw_fits) {
- *reg_rule = rr;
- return 0;
- }
+ if (band_rule_found && bw_fits)
+ return rr;
}
if (!band_rule_found)
- return -ERANGE;
+ return ERR_PTR(-ERANGE);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
-int freq_reg_info(struct wiphy *wiphy,
- u32 center_freq,
- u32 desired_bw_khz,
- const struct ieee80211_reg_rule **reg_rule)
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq)
{
- assert_cfg80211_lock();
- return freq_reg_info_regd(wiphy,
- center_freq,
- desired_bw_khz,
- reg_rule,
- NULL);
+ const struct ieee80211_regdomain *regd;
+ struct regulatory_request *lr = get_last_request();
+
+ /*
+ * Follow the driver's regulatory domain, if present, unless a country
+ * IE has been processed or a user wants to help complaince further
+ */
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ lr->initiator != NL80211_REGDOM_SET_BY_USER &&
+ wiphy->regd)
+ regd = get_wiphy_regdom(wiphy);
+ else
+ regd = get_cfg80211_regdom();
+
+ return freq_reg_info_regd(wiphy, center_freq, regd);
}
EXPORT_SYMBOL(freq_reg_info);
@@ -792,7 +788,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
}
static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
- u32 desired_bw_khz,
const struct ieee80211_reg_rule *reg_rule)
{
const struct ieee80211_power_rule *power_rule;
@@ -807,21 +802,16 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
else
snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
- REG_DBG_PRINT("Updating information on frequency %d MHz "
- "for a %d MHz width channel with regulatory rule:\n",
- chan->center_freq,
- KHZ_TO_MHZ(desired_bw_khz));
+ REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
+ chan->center_freq);
REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
- freq_range->start_freq_khz,
- freq_range->end_freq_khz,
- freq_range->max_bandwidth_khz,
- max_antenna_gain,
+ freq_range->start_freq_khz, freq_range->end_freq_khz,
+ freq_range->max_bandwidth_khz, max_antenna_gain,
power_rule->max_eirp);
}
#else
static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
- u32 desired_bw_khz,
const struct ieee80211_reg_rule *reg_rule)
{
return;
@@ -831,43 +821,25 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel). To support
- * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
- * new ieee80211_channel.target_bw and re run the regulatory check
- * on the wiphy with the target_bw specified. Then we can simply use
- * that below for the desired_bw_khz below.
+ * per channel, the primary and the extension channel).
*/
static void handle_channel(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator,
- enum ieee80211_band band,
- unsigned int chan_idx)
+ struct ieee80211_channel *chan)
{
- int r;
u32 flags, bw_flags = 0;
- u32 desired_bw_khz = MHZ_TO_KHZ(20);
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *chan;
struct wiphy *request_wiphy = NULL;
+ struct regulatory_request *lr = get_last_request();
- assert_cfg80211_lock();
-
- request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
-
- sband = wiphy->bands[band];
- BUG_ON(chan_idx >= sband->n_channels);
- chan = &sband->channels[chan_idx];
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
flags = chan->orig_flags;
- r = freq_reg_info(wiphy,
- MHZ_TO_KHZ(chan->center_freq),
- desired_bw_khz,
- &reg_rule);
-
- if (r) {
+ reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+ if (IS_ERR(reg_rule)) {
/*
* We will disable all channels that do not match our
* received regulatory rule unless the hint is coming
@@ -879,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy,
* while 5 GHz is still supported.
*/
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- r == -ERANGE)
+ PTR_ERR(reg_rule) == -ERANGE)
return;
REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq);
@@ -887,7 +859,7 @@ static void handle_channel(struct wiphy *wiphy,
return;
}
- chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
+ chan_reg_rule_print_dbg(chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
@@ -895,7 +867,7 @@ static void handle_channel(struct wiphy *wiphy,
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
- if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
/*
@@ -914,8 +886,9 @@ static void handle_channel(struct wiphy *wiphy,
chan->beacon_found = false;
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
- chan->max_antenna_gain = min(chan->orig_mag,
- (int) MBI_TO_DBI(power_rule->max_antenna_gain));
+ chan->max_antenna_gain =
+ min_t(int, chan->orig_mag,
+ MBI_TO_DBI(power_rule->max_antenna_gain));
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
if (chan->orig_mpwr) {
/*
@@ -935,68 +908,65 @@ static void handle_channel(struct wiphy *wiphy,
}
static void handle_band(struct wiphy *wiphy,
- enum ieee80211_band band,
- enum nl80211_reg_initiator initiator)
+ enum nl80211_reg_initiator initiator,
+ struct ieee80211_supported_band *sband)
{
unsigned int i;
- struct ieee80211_supported_band *sband;
- BUG_ON(!wiphy->bands[band]);
- sband = wiphy->bands[band];
+ if (!sband)
+ return;
for (i = 0; i < sband->n_channels; i++)
- handle_channel(wiphy, initiator, band, i);
+ handle_channel(wiphy, initiator, &sband->channels[i]);
}
static bool reg_request_cell_base(struct regulatory_request *request)
{
if (request->initiator != NL80211_REGDOM_SET_BY_USER)
return false;
- if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
- return false;
- return true;
+ return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
}
bool reg_last_request_cell_base(void)
{
bool val;
- assert_cfg80211_lock();
mutex_lock(&reg_mutex);
- val = reg_request_cell_base(last_request);
+ val = reg_request_cell_base(get_last_request());
mutex_unlock(&reg_mutex);
+
return val;
}
#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
-
/* Core specific check */
-static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
+static enum reg_request_treatment
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
{
+ struct regulatory_request *lr = get_last_request();
+
if (!reg_num_devs_support_basehint)
- return -EOPNOTSUPP;
+ return REG_REQ_IGNORE;
- if (reg_request_cell_base(last_request)) {
- if (!regdom_changes(pending_request->alpha2))
- return -EALREADY;
- return 0;
- }
- return 0;
+ if (reg_request_cell_base(lr) &&
+ !regdom_changes(pending_request->alpha2))
+ return REG_REQ_ALREADY_SET;
+
+ return REG_REQ_OK;
}
/* Device specific check */
static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
{
- if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
- return true;
- return false;
+ return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);
}
#else
static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
{
- return -EOPNOTSUPP;
+ return REG_REQ_IGNORE;
}
-static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+
+static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
{
return true;
}
@@ -1006,18 +976,17 @@ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
static bool ignore_reg_update(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
- if (!last_request) {
- REG_DBG_PRINT("Ignoring regulatory request %s since "
- "last_request is not set\n",
+ struct regulatory_request *lr = get_last_request();
+
+ if (!lr) {
+ REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",
reg_initiator_name(initiator));
return true;
}
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
- REG_DBG_PRINT("Ignoring regulatory request %s "
- "since the driver uses its own custom "
- "regulatory domain\n",
+ REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",
reg_initiator_name(initiator));
return true;
}
@@ -1028,22 +997,35 @@ static bool ignore_reg_update(struct wiphy *wiphy,
*/
if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- !is_world_regdom(last_request->alpha2)) {
- REG_DBG_PRINT("Ignoring regulatory request %s "
- "since the driver requires its own regulatory "
- "domain to be set first\n",
+ !is_world_regdom(lr->alpha2)) {
+ REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",
reg_initiator_name(initiator));
return true;
}
- if (reg_request_cell_base(last_request))
+ if (reg_request_cell_base(lr))
return reg_dev_ignore_cell_hint(wiphy);
return false;
}
-static void handle_reg_beacon(struct wiphy *wiphy,
- unsigned int chan_idx,
+static bool reg_is_world_roaming(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *cr = get_cfg80211_regdom();
+ const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy);
+ struct regulatory_request *lr = get_last_request();
+
+ if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2)))
+ return true;
+
+ if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ return true;
+
+ return false;
+}
+
+static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
struct reg_beacon *reg_beacon)
{
struct ieee80211_supported_band *sband;
@@ -1051,8 +1033,6 @@ static void handle_reg_beacon(struct wiphy *wiphy,
bool channel_changed = false;
struct ieee80211_channel chan_before;
- assert_cfg80211_lock();
-
sband = wiphy->bands[reg_beacon->chan.band];
chan = &sband->channels[chan_idx];
@@ -1064,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy,
chan->beacon_found = true;
+ if (!reg_is_world_roaming(wiphy))
+ return;
+
if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
return;
@@ -1094,8 +1077,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy,
unsigned int i;
struct ieee80211_supported_band *sband;
- assert_cfg80211_lock();
-
if (!wiphy->bands[reg_beacon->chan.band])
return;
@@ -1114,11 +1095,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)
struct ieee80211_supported_band *sband;
struct reg_beacon *reg_beacon;
- assert_cfg80211_lock();
-
- if (list_empty(&reg_beacon_list))
- return;
-
list_for_each_entry(reg_beacon, &reg_beacon_list, list) {
if (!wiphy->bands[reg_beacon->chan.band])
continue;
@@ -1128,18 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)
}
}
-static bool reg_is_world_roaming(struct wiphy *wiphy)
-{
- if (is_world_regdom(cfg80211_regdomain->alpha2) ||
- (wiphy->regd && is_world_regdom(wiphy->regd->alpha2)))
- return true;
- if (last_request &&
- last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
- return true;
- return false;
-}
-
/* Reap the advantages of previously found beacons */
static void reg_process_beacons(struct wiphy *wiphy)
{
@@ -1149,39 +1113,27 @@ static void reg_process_beacons(struct wiphy *wiphy)
*/
if (!last_request)
return;
- if (!reg_is_world_roaming(wiphy))
- return;
wiphy_update_beacon_reg(wiphy);
}
-static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
+static bool is_ht40_allowed(struct ieee80211_channel *chan)
{
if (!chan)
- return true;
+ return false;
if (chan->flags & IEEE80211_CHAN_DISABLED)
- return true;
+ return false;
/* This would happen when regulatory rules disallow HT40 completely */
- if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
- return true;
- return false;
+ return !(chan->flags & IEEE80211_CHAN_NO_HT40);
}
static void reg_process_ht_flags_channel(struct wiphy *wiphy,
- enum ieee80211_band band,
- unsigned int chan_idx)
+ struct ieee80211_channel *channel)
{
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *channel;
+ struct ieee80211_supported_band *sband = wiphy->bands[channel->band];
struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
unsigned int i;
- assert_cfg80211_lock();
-
- sband = wiphy->bands[band];
- BUG_ON(chan_idx >= sband->n_channels);
- channel = &sband->channels[chan_idx];
-
- if (is_ht40_not_allowed(channel)) {
+ if (!is_ht40_allowed(channel)) {
channel->flags |= IEEE80211_CHAN_NO_HT40;
return;
}
@@ -1192,6 +1144,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,
*/
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *c = &sband->channels[i];
+
if (c->center_freq == (channel->center_freq - 20))
channel_before = c;
if (c->center_freq == (channel->center_freq + 20))
@@ -1203,28 +1156,27 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,
* if that ever changes we also need to change the below logic
* to include that as well.
*/
- if (is_ht40_not_allowed(channel_before))
+ if (!is_ht40_allowed(channel_before))
channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
else
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- if (is_ht40_not_allowed(channel_after))
+ if (!is_ht40_allowed(channel_after))
channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
else
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
}
static void reg_process_ht_flags_band(struct wiphy *wiphy,
- enum ieee80211_band band)
+ struct ieee80211_supported_band *sband)
{
unsigned int i;
- struct ieee80211_supported_band *sband;
- BUG_ON(!wiphy->bands[band]);
- sband = wiphy->bands[band];
+ if (!sband)
+ return;
for (i = 0; i < sband->n_channels; i++)
- reg_process_ht_flags_channel(wiphy, band, i);
+ reg_process_ht_flags_channel(wiphy, &sband->channels[i]);
}
static void reg_process_ht_flags(struct wiphy *wiphy)
@@ -1234,34 +1186,29 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
if (!wiphy)
return;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (wiphy->bands[band])
- reg_process_ht_flags_band(wiphy, band);
- }
-
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
}
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
enum ieee80211_band band;
-
- assert_reg_lock();
+ struct regulatory_request *lr = get_last_request();
if (ignore_reg_update(wiphy, initiator))
return;
- last_request->dfs_region = cfg80211_regdomain->dfs_region;
+ lr->dfs_region = get_cfg80211_regdom()->dfs_region;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (wiphy->bands[band])
- handle_band(wiphy, band, initiator);
- }
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ handle_band(wiphy, initiator, wiphy->bands[band]);
reg_process_beacons(wiphy);
reg_process_ht_flags(wiphy);
+
if (wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, last_request);
+ wiphy->reg_notifier(wiphy, lr);
}
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
@@ -1269,6 +1216,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
struct cfg80211_registered_device *rdev;
struct wiphy *wiphy;
+ assert_cfg80211_lock();
+
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
@@ -1280,47 +1229,30 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, last_request);
+ wiphy->reg_notifier(wiphy, get_last_request());
}
}
static void handle_channel_custom(struct wiphy *wiphy,
- enum ieee80211_band band,
- unsigned int chan_idx,
+ struct ieee80211_channel *chan,
const struct ieee80211_regdomain *regd)
{
- int r;
- u32 desired_bw_khz = MHZ_TO_KHZ(20);
u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *chan;
-
- assert_reg_lock();
- sband = wiphy->bands[band];
- BUG_ON(chan_idx >= sband->n_channels);
- chan = &sband->channels[chan_idx];
+ reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
+ regd);
- r = freq_reg_info_regd(wiphy,
- MHZ_TO_KHZ(chan->center_freq),
- desired_bw_khz,
- &reg_rule,
- regd);
-
- if (r) {
- REG_DBG_PRINT("Disabling freq %d MHz as custom "
- "regd has no rule that fits a %d MHz "
- "wide channel\n",
- chan->center_freq,
- KHZ_TO_MHZ(desired_bw_khz));
+ if (IS_ERR(reg_rule)) {
+ REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
+ chan->center_freq);
chan->flags = IEEE80211_CHAN_DISABLED;
return;
}
- chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
+ chan_reg_rule_print_dbg(chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
@@ -1334,17 +1266,17 @@ static void handle_channel_custom(struct wiphy *wiphy,
(int) MBM_TO_DBM(power_rule->max_eirp);
}
-static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band,
+static void handle_band_custom(struct wiphy *wiphy,
+ struct ieee80211_supported_band *sband,
const struct ieee80211_regdomain *regd)
{
unsigned int i;
- struct ieee80211_supported_band *sband;
- BUG_ON(!wiphy->bands[band]);
- sband = wiphy->bands[band];
+ if (!sband)
+ return;
for (i = 0; i < sband->n_channels; i++)
- handle_channel_custom(wiphy, band, i, regd);
+ handle_channel_custom(wiphy, &sband->channels[i], regd);
}
/* Used by drivers prior to wiphy registration */
@@ -1354,60 +1286,50 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
enum ieee80211_band band;
unsigned int bands_set = 0;
- mutex_lock(&reg_mutex);
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!wiphy->bands[band])
continue;
- handle_band_custom(wiphy, band, regd);
+ handle_band_custom(wiphy, wiphy->bands[band], regd);
bands_set++;
}
- mutex_unlock(&reg_mutex);
/*
* no point in calling this if it won't have any effect
- * on your device's supportd bands.
+ * on your device's supported bands.
*/
WARN_ON(!bands_set);
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
-/*
- * Return value which can be used by ignore_request() to indicate
- * it has been determined we should intersect two regulatory domains
- */
-#define REG_INTERSECT 1
-
/* This has the logic which determines when a new request
* should be ignored. */
-static int ignore_request(struct wiphy *wiphy,
+static enum reg_request_treatment
+get_reg_request_treatment(struct wiphy *wiphy,
struct regulatory_request *pending_request)
{
struct wiphy *last_wiphy = NULL;
-
- assert_cfg80211_lock();
+ struct regulatory_request *lr = get_last_request();
/* All initial requests are respected */
- if (!last_request)
- return 0;
+ if (!lr)
+ return REG_REQ_OK;
switch (pending_request->initiator) {
case NL80211_REGDOM_SET_BY_CORE:
- return 0;
+ return REG_REQ_OK;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-
- if (reg_request_cell_base(last_request)) {
+ if (reg_request_cell_base(lr)) {
/* Trust a Cell base station over the AP's country IE */
if (regdom_changes(pending_request->alpha2))
- return -EOPNOTSUPP;
- return -EALREADY;
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
}
- last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+ last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (unlikely(!is_an_alpha2(pending_request->alpha2)))
return -EINVAL;
- if (last_request->initiator ==
- NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
if (last_wiphy != wiphy) {
/*
* Two cards with two APs claiming different
@@ -1416,23 +1338,23 @@ static int ignore_request(struct wiphy *wiphy,
* to be correct. Reject second one for now.
*/
if (regdom_changes(pending_request->alpha2))
- return -EOPNOTSUPP;
- return -EALREADY;
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
}
/*
* Two consecutive Country IE hints on the same wiphy.
* This should be picked up early by the driver/stack
*/
if (WARN_ON(regdom_changes(pending_request->alpha2)))
- return 0;
- return -EALREADY;
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
}
return 0;
case NL80211_REGDOM_SET_BY_DRIVER:
- if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) {
+ if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
if (regdom_changes(pending_request->alpha2))
- return 0;
- return -EALREADY;
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
}
/*
@@ -1440,59 +1362,59 @@ static int ignore_request(struct wiphy *wiphy,
* back in or if you add a new device for which the previously
* loaded card also agrees on the regulatory domain.
*/
- if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
!regdom_changes(pending_request->alpha2))
- return -EALREADY;
+ return REG_REQ_ALREADY_SET;
- return REG_INTERSECT;
+ return REG_REQ_INTERSECT;
case NL80211_REGDOM_SET_BY_USER:
if (reg_request_cell_base(pending_request))
return reg_ignore_cell_hint(pending_request);
- if (reg_request_cell_base(last_request))
- return -EOPNOTSUPP;
+ if (reg_request_cell_base(lr))
+ return REG_REQ_IGNORE;
- if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
- return REG_INTERSECT;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_INTERSECT;
/*
* If the user knows better the user should set the regdom
* to their country before the IE is picked up
*/
- if (last_request->initiator == NL80211_REGDOM_SET_BY_USER &&
- last_request->intersect)
- return -EOPNOTSUPP;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->intersect)
+ return REG_REQ_IGNORE;
/*
* Process user requests only after previous user/driver/core
* requests have been processed
*/
- if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE ||
- last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
- if (regdom_changes(last_request->alpha2))
- return -EAGAIN;
- }
+ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+ regdom_changes(lr->alpha2))
+ return REG_REQ_IGNORE;
if (!regdom_changes(pending_request->alpha2))
- return -EALREADY;
+ return REG_REQ_ALREADY_SET;
- return 0;
+ return REG_REQ_OK;
}
- return -EINVAL;
+ return REG_REQ_IGNORE;
}
static void reg_set_request_processed(void)
{
bool need_more_processing = false;
+ struct regulatory_request *lr = get_last_request();
- last_request->processed = true;
+ lr->processed = true;
spin_lock(&reg_requests_lock);
if (!list_empty(&reg_requests_list))
need_more_processing = true;
spin_unlock(&reg_requests_lock);
- if (last_request->initiator == NL80211_REGDOM_SET_BY_USER)
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
cancel_delayed_work(&reg_timeout);
if (need_more_processing)
@@ -1508,116 +1430,122 @@ static void reg_set_request_processed(void)
* The Wireless subsystem can use this function to hint to the wireless core
* what it believes should be the current regulatory domain.
*
- * Returns zero if all went fine, %-EALREADY if a regulatory domain had
- * already been set or other standard error codes.
+ * Returns one of the different reg request treatment values.
*
- * Caller must hold &cfg80211_mutex and &reg_mutex
+ * Caller must hold &reg_mutex
*/
-static int __regulatory_hint(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+static enum reg_request_treatment
+__regulatory_hint(struct wiphy *wiphy,
+ struct regulatory_request *pending_request)
{
+ const struct ieee80211_regdomain *regd;
bool intersect = false;
- int r = 0;
-
- assert_cfg80211_lock();
+ enum reg_request_treatment treatment;
+ struct regulatory_request *lr;
- r = ignore_request(wiphy, pending_request);
+ treatment = get_reg_request_treatment(wiphy, pending_request);
- if (r == REG_INTERSECT) {
+ switch (treatment) {
+ case REG_REQ_INTERSECT:
if (pending_request->initiator ==
NL80211_REGDOM_SET_BY_DRIVER) {
- r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
- if (r) {
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd)) {
kfree(pending_request);
- return r;
+ return PTR_ERR(regd);
}
+ rcu_assign_pointer(wiphy->regd, regd);
}
intersect = true;
- } else if (r) {
+ break;
+ case REG_REQ_OK:
+ break;
+ default:
/*
* If the regulatory domain being requested by the
* driver has already been set just copy it to the
* wiphy
*/
- if (r == -EALREADY &&
- pending_request->initiator ==
- NL80211_REGDOM_SET_BY_DRIVER) {
- r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
- if (r) {
+ if (treatment == REG_REQ_ALREADY_SET &&
+ pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd)) {
kfree(pending_request);
- return r;
+ return REG_REQ_IGNORE;
}
- r = -EALREADY;
+ treatment = REG_REQ_ALREADY_SET;
+ rcu_assign_pointer(wiphy->regd, regd);
goto new_request;
}
kfree(pending_request);
- return r;
+ return treatment;
}
new_request:
- if (last_request != &core_request_world)
- kfree(last_request);
+ lr = get_last_request();
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
- last_request = pending_request;
- last_request->intersect = intersect;
+ pending_request->intersect = intersect;
+ pending_request->processed = false;
+ rcu_assign_pointer(last_request, pending_request);
+ lr = pending_request;
pending_request = NULL;
- if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
- user_alpha2[0] = last_request->alpha2[0];
- user_alpha2[1] = last_request->alpha2[1];
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER) {
+ user_alpha2[0] = lr->alpha2[0];
+ user_alpha2[1] = lr->alpha2[1];
}
- /* When r == REG_INTERSECT we do need to call CRDA */
- if (r < 0) {
+ /* When r == REG_REQ_INTERSECT we do need to call CRDA */
+ if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
/*
* Since CRDA will not be called in this case as we already
* have applied the requested regulatory domain before we just
* inform userspace we have processed the request
*/
- if (r == -EALREADY) {
- nl80211_send_reg_change_event(last_request);
+ if (treatment == REG_REQ_ALREADY_SET) {
+ nl80211_send_reg_change_event(lr);
reg_set_request_processed();
}
- return r;
+ return treatment;
}
- return call_crda(last_request->alpha2);
+ if (call_crda(lr->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_OK;
}
/* This processes *all* regulatory hints */
static void reg_process_hint(struct regulatory_request *reg_request,
enum nl80211_reg_initiator reg_initiator)
{
- int r = 0;
struct wiphy *wiphy = NULL;
- BUG_ON(!reg_request->alpha2);
+ if (WARN_ON(!reg_request->alpha2))
+ return;
- if (wiphy_idx_valid(reg_request->wiphy_idx))
+ if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
- if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER &&
- !wiphy) {
+ if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
kfree(reg_request);
return;
}
- r = __regulatory_hint(wiphy, reg_request);
- /* This is required so that the orig_* parameters are saved */
- if (r == -EALREADY && wiphy &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
- wiphy_update_regulatory(wiphy, reg_initiator);
- return;
+ switch (__regulatory_hint(wiphy, reg_request)) {
+ case REG_REQ_ALREADY_SET:
+ /* This is required so that the orig_* parameters are saved */
+ if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy_update_regulatory(wiphy, reg_initiator);
+ break;
+ default:
+ if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
+ schedule_delayed_work(&reg_timeout,
+ msecs_to_jiffies(3142));
+ break;
}
-
- /*
- * We only time out user hints, given that they should be the only
- * source of bogus requests.
- */
- if (r != -EALREADY &&
- reg_initiator == NL80211_REGDOM_SET_BY_USER)
- schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
}
/*
@@ -1627,15 +1555,15 @@ static void reg_process_hint(struct regulatory_request *reg_request,
*/
static void reg_process_pending_hints(void)
{
- struct regulatory_request *reg_request;
+ struct regulatory_request *reg_request, *lr;
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
+ lr = get_last_request();
/* When last_request->processed becomes true this will be rescheduled */
- if (last_request && !last_request->processed) {
- REG_DBG_PRINT("Pending regulatory request, waiting "
- "for it to be processed...\n");
+ if (lr && !lr->processed) {
+ REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");
goto out;
}
@@ -1666,23 +1594,14 @@ static void reg_process_pending_beacon_hints(void)
struct cfg80211_registered_device *rdev;
struct reg_beacon *pending_beacon, *tmp;
- /*
- * No need to hold the reg_mutex here as we just touch wiphys
- * and do not read or access regulatory variables.
- */
mutex_lock(&cfg80211_mutex);
+ mutex_lock(&reg_mutex);
/* This goes through the _pending_ beacon list */
spin_lock_bh(&reg_pending_beacons_lock);
- if (list_empty(&reg_pending_beacons)) {
- spin_unlock_bh(&reg_pending_beacons_lock);
- goto out;
- }
-
list_for_each_entry_safe(pending_beacon, tmp,
&reg_pending_beacons, list) {
-
list_del_init(&pending_beacon->list);
/* Applies the beacon hint to current wiphys */
@@ -1694,7 +1613,7 @@ static void reg_process_pending_beacon_hints(void)
}
spin_unlock_bh(&reg_pending_beacons_lock);
-out:
+ mutex_unlock(&reg_mutex);
mutex_unlock(&cfg80211_mutex);
}
@@ -1706,10 +1625,8 @@ static void reg_todo(struct work_struct *work)
static void queue_regulatory_request(struct regulatory_request *request)
{
- if (isalpha(request->alpha2[0]))
- request->alpha2[0] = toupper(request->alpha2[0]);
- if (isalpha(request->alpha2[1]))
- request->alpha2[1] = toupper(request->alpha2[1]);
+ request->alpha2[0] = toupper(request->alpha2[0]);
+ request->alpha2[1] = toupper(request->alpha2[1]);
spin_lock(&reg_requests_lock);
list_add_tail(&request->list, &reg_requests_list);
@@ -1726,8 +1643,7 @@ static int regulatory_hint_core(const char *alpha2)
{
struct regulatory_request *request;
- request = kzalloc(sizeof(struct regulatory_request),
- GFP_KERNEL);
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
if (!request)
return -ENOMEM;
@@ -1746,13 +1662,14 @@ int regulatory_hint_user(const char *alpha2,
{
struct regulatory_request *request;
- BUG_ON(!alpha2);
+ if (WARN_ON(!alpha2))
+ return -EINVAL;
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
if (!request)
return -ENOMEM;
- request->wiphy_idx = WIPHY_IDX_STALE;
+ request->wiphy_idx = WIPHY_IDX_INVALID;
request->alpha2[0] = alpha2[0];
request->alpha2[1] = alpha2[1];
request->initiator = NL80211_REGDOM_SET_BY_USER;
@@ -1768,8 +1685,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
{
struct regulatory_request *request;
- BUG_ON(!alpha2);
- BUG_ON(!wiphy);
+ if (WARN_ON(!alpha2 || !wiphy))
+ return -EINVAL;
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
if (!request)
@@ -1777,9 +1694,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
request->wiphy_idx = get_wiphy_idx(wiphy);
- /* Must have registered wiphy first */
- BUG_ON(!wiphy_idx_valid(request->wiphy_idx));
-
request->alpha2[0] = alpha2[0];
request->alpha2[1] = alpha2[1];
request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
@@ -1794,18 +1708,17 @@ EXPORT_SYMBOL(regulatory_hint);
* We hold wdev_lock() here so we cannot hold cfg80211_mutex() and
* therefore cannot iterate over the rdev list here.
*/
-void regulatory_hint_11d(struct wiphy *wiphy,
- enum ieee80211_band band,
- const u8 *country_ie,
- u8 country_ie_len)
+void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
+ const u8 *country_ie, u8 country_ie_len)
{
char alpha2[2];
enum environment_cap env = ENVIRON_ANY;
- struct regulatory_request *request;
+ struct regulatory_request *request, *lr;
mutex_lock(&reg_mutex);
+ lr = get_last_request();
- if (unlikely(!last_request))
+ if (unlikely(!lr))
goto out;
/* IE len must be evenly divisible by 2 */
@@ -1828,9 +1741,8 @@ void regulatory_hint_11d(struct wiphy *wiphy,
* We leave conflict resolution to the workqueue, where can hold
* cfg80211_mutex.
*/
- if (likely(last_request->initiator ==
- NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy_idx_valid(last_request->wiphy_idx)))
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ lr->wiphy_idx != WIPHY_IDX_INVALID)
goto out;
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
@@ -1843,12 +1755,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,
request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
request->country_ie_env = env;
- mutex_unlock(&reg_mutex);
-
queue_regulatory_request(request);
-
- return;
-
out:
mutex_unlock(&reg_mutex);
}
@@ -1863,8 +1770,7 @@ static void restore_alpha2(char *alpha2, bool reset_user)
if (is_user_regdom_saved()) {
/* Unless we're asked to ignore it and reset it */
if (reset_user) {
- REG_DBG_PRINT("Restoring regulatory settings "
- "including user preference\n");
+ REG_DBG_PRINT("Restoring regulatory settings including user preference\n");
user_alpha2[0] = '9';
user_alpha2[1] = '7';
@@ -1874,26 +1780,20 @@ static void restore_alpha2(char *alpha2, bool reset_user)
* back as they were for a full restore.
*/
if (!is_world_regdom(ieee80211_regdom)) {
- REG_DBG_PRINT("Keeping preference on "
- "module parameter ieee80211_regdom: %c%c\n",
- ieee80211_regdom[0],
- ieee80211_regdom[1]);
+ REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+ ieee80211_regdom[0], ieee80211_regdom[1]);
alpha2[0] = ieee80211_regdom[0];
alpha2[1] = ieee80211_regdom[1];
}
} else {
- REG_DBG_PRINT("Restoring regulatory settings "
- "while preserving user preference for: %c%c\n",
- user_alpha2[0],
- user_alpha2[1]);
+ REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n",
+ user_alpha2[0], user_alpha2[1]);
alpha2[0] = user_alpha2[0];
alpha2[1] = user_alpha2[1];
}
} else if (!is_world_regdom(ieee80211_regdom)) {
- REG_DBG_PRINT("Keeping preference on "
- "module parameter ieee80211_regdom: %c%c\n",
- ieee80211_regdom[0],
- ieee80211_regdom[1]);
+ REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+ ieee80211_regdom[0], ieee80211_regdom[1]);
alpha2[0] = ieee80211_regdom[0];
alpha2[1] = ieee80211_regdom[1];
} else
@@ -1948,7 +1848,7 @@ static void restore_regulatory_settings(bool reset_user)
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
- reset_regdomains(true);
+ reset_regdomains(true, cfg80211_world_regdom);
restore_alpha2(alpha2, reset_user);
/*
@@ -1958,49 +1858,35 @@ static void restore_regulatory_settings(bool reset_user)
* settings.
*/
spin_lock(&reg_requests_lock);
- if (!list_empty(&reg_requests_list)) {
- list_for_each_entry_safe(reg_request, tmp,
- &reg_requests_list, list) {
- if (reg_request->initiator !=
- NL80211_REGDOM_SET_BY_USER)
- continue;
- list_move_tail(&reg_request->list, &tmp_reg_req_list);
- }
+ list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
+ if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER)
+ continue;
+ list_move_tail(&reg_request->list, &tmp_reg_req_list);
}
spin_unlock(&reg_requests_lock);
/* Clear beacon hints */
spin_lock_bh(&reg_pending_beacons_lock);
- if (!list_empty(&reg_pending_beacons)) {
- list_for_each_entry_safe(reg_beacon, btmp,
- &reg_pending_beacons, list) {
- list_del(&reg_beacon->list);
- kfree(reg_beacon);
- }
+ list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
}
spin_unlock_bh(&reg_pending_beacons_lock);
- if (!list_empty(&reg_beacon_list)) {
- list_for_each_entry_safe(reg_beacon, btmp,
- &reg_beacon_list, list) {
- list_del(&reg_beacon->list);
- kfree(reg_beacon);
- }
+ list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
}
/* First restore to the basic regulatory settings */
- cfg80211_regdomain = cfg80211_world_regdom;
- world_alpha2[0] = cfg80211_regdomain->alpha2[0];
- world_alpha2[1] = cfg80211_regdomain->alpha2[1];
+ world_alpha2[0] = cfg80211_world_regdom->alpha2[0];
+ world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
restore_custom_reg_settings(&rdev->wiphy);
}
- mutex_unlock(&reg_mutex);
- mutex_unlock(&cfg80211_mutex);
-
regulatory_hint_core(world_alpha2);
/*
@@ -2011,20 +1897,8 @@ static void restore_regulatory_settings(bool reset_user)
if (is_an_alpha2(alpha2))
regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
- if (list_empty(&tmp_reg_req_list))
- return;
-
- mutex_lock(&cfg80211_mutex);
- mutex_lock(&reg_mutex);
-
spin_lock(&reg_requests_lock);
- list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) {
- REG_DBG_PRINT("Adding request for country %c%c back "
- "into the queue\n",
- reg_request->alpha2[0],
- reg_request->alpha2[1]);
- list_move_tail(&reg_request->list, &reg_requests_list);
- }
+ list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
spin_unlock(&reg_requests_lock);
mutex_unlock(&reg_mutex);
@@ -2037,8 +1911,7 @@ static void restore_regulatory_settings(bool reset_user)
void regulatory_hint_disconnect(void)
{
- REG_DBG_PRINT("All devices are disconnected, going to "
- "restore regulatory settings\n");
+ REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n");
restore_regulatory_settings(false);
}
@@ -2051,31 +1924,48 @@ static bool freq_is_chan_12_13_14(u16 freq)
return false;
}
+static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan)
+{
+ struct reg_beacon *pending_beacon;
+
+ list_for_each_entry(pending_beacon, &reg_pending_beacons, list)
+ if (beacon_chan->center_freq ==
+ pending_beacon->chan.center_freq)
+ return true;
+ return false;
+}
+
int regulatory_hint_found_beacon(struct wiphy *wiphy,
struct ieee80211_channel *beacon_chan,
gfp_t gfp)
{
struct reg_beacon *reg_beacon;
+ bool processing;
- if (likely((beacon_chan->beacon_found ||
- (beacon_chan->flags & IEEE80211_CHAN_RADAR) ||
+ if (beacon_chan->beacon_found ||
+ beacon_chan->flags & IEEE80211_CHAN_RADAR ||
(beacon_chan->band == IEEE80211_BAND_2GHZ &&
- !freq_is_chan_12_13_14(beacon_chan->center_freq)))))
+ !freq_is_chan_12_13_14(beacon_chan->center_freq)))
+ return 0;
+
+ spin_lock_bh(&reg_pending_beacons_lock);
+ processing = pending_reg_beacon(beacon_chan);
+ spin_unlock_bh(&reg_pending_beacons_lock);
+
+ if (processing)
return 0;
reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
if (!reg_beacon)
return -ENOMEM;
- REG_DBG_PRINT("Found new beacon on "
- "frequency: %d MHz (Ch %d) on %s\n",
+ REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
beacon_chan->center_freq,
ieee80211_frequency_to_channel(beacon_chan->center_freq),
wiphy_name(wiphy));
memcpy(&reg_beacon->chan, beacon_chan,
- sizeof(struct ieee80211_channel));
-
+ sizeof(struct ieee80211_channel));
/*
* Since we can be called from BH or and non-BH context
@@ -2155,21 +2045,19 @@ static void print_dfs_region(u8 dfs_region)
pr_info(" DFS Master region JP");
break;
default:
- pr_info(" DFS Master region Uknown");
+ pr_info(" DFS Master region Unknown");
break;
}
}
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
+ struct regulatory_request *lr = get_last_request();
if (is_intersected_alpha2(rd->alpha2)) {
-
- if (last_request->initiator ==
- NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
struct cfg80211_registered_device *rdev;
- rdev = cfg80211_rdev_by_wiphy_idx(
- last_request->wiphy_idx);
+ rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx);
if (rdev) {
pr_info("Current regulatory domain updated by AP to: %c%c\n",
rdev->country_ie_alpha2[0],
@@ -2178,22 +2066,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
pr_info("Current regulatory domain intersected:\n");
} else
pr_info("Current regulatory domain intersected:\n");
- } else if (is_world_regdom(rd->alpha2))
+ } else if (is_world_regdom(rd->alpha2)) {
pr_info("World regulatory domain updated:\n");
- else {
+ } else {
if (is_unknown_alpha2(rd->alpha2))
pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
else {
- if (reg_request_cell_base(last_request))
- pr_info("Regulatory domain changed "
- "to country: %c%c by Cell Station\n",
+ if (reg_request_cell_base(lr))
+ pr_info("Regulatory domain changed to country: %c%c by Cell Station\n",
rd->alpha2[0], rd->alpha2[1]);
else
- pr_info("Regulatory domain changed "
- "to country: %c%c\n",
+ pr_info("Regulatory domain changed to country: %c%c\n",
rd->alpha2[0], rd->alpha2[1]);
}
}
+
print_dfs_region(rd->dfs_region);
print_rd_rules(rd);
}
@@ -2207,22 +2094,23 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
/* Takes ownership of rd only if it doesn't fail */
static int __set_regdom(const struct ieee80211_regdomain *rd)
{
+ const struct ieee80211_regdomain *regd;
const struct ieee80211_regdomain *intersected_rd = NULL;
struct wiphy *request_wiphy;
+ struct regulatory_request *lr = get_last_request();
+
/* Some basic sanity checks first */
+ if (!reg_is_valid_request(rd->alpha2))
+ return -EINVAL;
+
if (is_world_regdom(rd->alpha2)) {
- if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
- return -EINVAL;
update_world_regdomain(rd);
return 0;
}
if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
- !is_unknown_alpha2(rd->alpha2))
- return -EINVAL;
-
- if (!last_request)
+ !is_unknown_alpha2(rd->alpha2))
return -EINVAL;
/*
@@ -2230,7 +2118,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* rd is non static (it means CRDA was present and was used last)
* and the pending request came in from a country IE
*/
- if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
/*
* If someone else asked us to change the rd lets only bother
* checking if the alpha2 changes if CRDA was already called
@@ -2246,29 +2134,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* internal EEPROM data
*/
- if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
- return -EINVAL;
-
if (!is_valid_rd(rd)) {
pr_err("Invalid regulatory domain detected:\n");
print_regdomain_info(rd);
return -EINVAL;
}
- request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (!request_wiphy &&
- (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+ (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
schedule_delayed_work(&reg_timeout, 0);
return -ENODEV;
}
- if (!last_request->intersect) {
- int r;
-
- if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
- reset_regdomains(false);
- cfg80211_regdomain = rd;
+ if (!lr->intersect) {
+ if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
+ reset_regdomains(false, rd);
return 0;
}
@@ -2284,20 +2166,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
if (request_wiphy->regd)
return -EALREADY;
- r = reg_copy_regd(&request_wiphy->regd, rd);
- if (r)
- return r;
+ regd = reg_copy_regd(rd);
+ if (IS_ERR(regd))
+ return PTR_ERR(regd);
- reset_regdomains(false);
- cfg80211_regdomain = rd;
+ rcu_assign_pointer(request_wiphy->regd, regd);
+ reset_regdomains(false, rd);
return 0;
}
/* Intersection requires a bit more work */
- if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-
- intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
if (!intersected_rd)
return -EINVAL;
@@ -2306,15 +2187,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* However if a driver requested this specific regulatory
* domain we keep it for its private use
*/
- if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER)
- request_wiphy->regd = rd;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER)
+ rcu_assign_pointer(request_wiphy->regd, rd);
else
kfree(rd);
rd = NULL;
- reset_regdomains(false);
- cfg80211_regdomain = intersected_rd;
+ reset_regdomains(false, intersected_rd);
return 0;
}
@@ -2326,15 +2206,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
/*
* Use this call to set the current regulatory domain. Conflicts with
* multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. Caller must hold cfg80211_mutex
+ * kmalloc'd the rd structure.
*/
int set_regdom(const struct ieee80211_regdomain *rd)
{
+ struct regulatory_request *lr;
int r;
- assert_cfg80211_lock();
-
mutex_lock(&reg_mutex);
+ lr = get_last_request();
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd);
@@ -2343,23 +2223,25 @@ int set_regdom(const struct ieee80211_regdomain *rd)
reg_set_request_processed();
kfree(rd);
- mutex_unlock(&reg_mutex);
- return r;
+ goto out;
}
/* This would make this whole thing pointless */
- if (!last_request->intersect)
- BUG_ON(rd != cfg80211_regdomain);
+ if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) {
+ r = -EINVAL;
+ goto out;
+ }
/* update all wiphys now with the new established regulatory domain */
- update_all_wiphy_regulatory(last_request->initiator);
+ update_all_wiphy_regulatory(lr->initiator);
- print_regdomain(cfg80211_regdomain);
+ print_regdomain(get_cfg80211_regdom());
- nl80211_send_reg_change_event(last_request);
+ nl80211_send_reg_change_event(lr);
reg_set_request_processed();
+ out:
mutex_unlock(&reg_mutex);
return r;
@@ -2368,10 +2250,11 @@ int set_regdom(const struct ieee80211_regdomain *rd)
#ifdef CONFIG_HOTPLUG
int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- if (last_request && !last_request->processed) {
+ struct regulatory_request *lr = get_last_request();
+
+ if (lr && !lr->processed) {
if (add_uevent_var(env, "COUNTRY=%c%c",
- last_request->alpha2[0],
- last_request->alpha2[1]))
+ lr->alpha2[0], lr->alpha2[1]))
return -ENOMEM;
}
@@ -2386,8 +2269,6 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
void wiphy_regulatory_register(struct wiphy *wiphy)
{
- assert_cfg80211_lock();
-
mutex_lock(&reg_mutex);
if (!reg_dev_ignore_cell_hint(wiphy))
@@ -2402,32 +2283,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
void wiphy_regulatory_deregister(struct wiphy *wiphy)
{
struct wiphy *request_wiphy = NULL;
-
- assert_cfg80211_lock();
+ struct regulatory_request *lr;
mutex_lock(&reg_mutex);
+ lr = get_last_request();
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint--;
- kfree(wiphy->regd);
+ rcu_free_regdom(get_wiphy_regdom(wiphy));
+ rcu_assign_pointer(wiphy->regd, NULL);
- if (last_request)
- request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+ if (lr)
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (!request_wiphy || request_wiphy != wiphy)
goto out;
- last_request->wiphy_idx = WIPHY_IDX_STALE;
- last_request->country_ie_env = ENVIRON_ANY;
+ lr->wiphy_idx = WIPHY_IDX_INVALID;
+ lr->country_ie_env = ENVIRON_ANY;
out:
mutex_unlock(&reg_mutex);
}
static void reg_timeout_work(struct work_struct *work)
{
- REG_DBG_PRINT("Timeout while waiting for CRDA to reply, "
- "restoring regulatory settings\n");
+ REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
restore_regulatory_settings(true);
}
@@ -2446,13 +2327,13 @@ int __init regulatory_init(void)
reg_regdb_size_check();
- cfg80211_regdomain = cfg80211_world_regdom;
+ rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
user_alpha2[0] = '9';
user_alpha2[1] = '7';
/* We always try to get an update for the static regdomain */
- err = regulatory_hint_core(cfg80211_regdomain->alpha2);
+ err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
if (err) {
if (err == -ENOMEM)
return err;
@@ -2464,10 +2345,6 @@ int __init regulatory_init(void)
* errors as non-fatal.
*/
pr_err("kobject_uevent_env() was unable to call CRDA during init\n");
-#ifdef CONFIG_CFG80211_REG_DEBUG
- /* We want to find out exactly why when debugging */
- WARN_ON(err);
-#endif
}
/*
@@ -2481,7 +2358,7 @@ int __init regulatory_init(void)
return 0;
}
-void /* __init_or_exit */ regulatory_exit(void)
+void regulatory_exit(void)
{
struct regulatory_request *reg_request, *tmp;
struct reg_beacon *reg_beacon, *btmp;
@@ -2489,43 +2366,27 @@ void /* __init_or_exit */ regulatory_exit(void)
cancel_work_sync(&reg_work);
cancel_delayed_work_sync(&reg_timeout);
- mutex_lock(&cfg80211_mutex);
+ /* Lock to suppress warnings */
mutex_lock(&reg_mutex);
-
- reset_regdomains(true);
+ reset_regdomains(true, NULL);
+ mutex_unlock(&reg_mutex);
dev_set_uevent_suppress(&reg_pdev->dev, true);
platform_device_unregister(reg_pdev);
- spin_lock_bh(&reg_pending_beacons_lock);
- if (!list_empty(&reg_pending_beacons)) {
- list_for_each_entry_safe(reg_beacon, btmp,
- &reg_pending_beacons, list) {
- list_del(&reg_beacon->list);
- kfree(reg_beacon);
- }
+ list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
}
- spin_unlock_bh(&reg_pending_beacons_lock);
- if (!list_empty(&reg_beacon_list)) {
- list_for_each_entry_safe(reg_beacon, btmp,
- &reg_beacon_list, list) {
- list_del(&reg_beacon->list);
- kfree(reg_beacon);
- }
+ list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
}
- spin_lock(&reg_requests_lock);
- if (!list_empty(&reg_requests_list)) {
- list_for_each_entry_safe(reg_request, tmp,
- &reg_requests_list, list) {
- list_del(&reg_request->list);
- kfree(reg_request);
- }
+ list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
+ list_del(&reg_request->list);
+ kfree(reg_request);
}
- spin_unlock(&reg_requests_lock);
-
- mutex_unlock(&reg_mutex);
- mutex_unlock(&cfg80211_mutex);
}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 4c0a32ffd530..af2d5f8a5d82 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -16,10 +16,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-extern const struct ieee80211_regdomain *cfg80211_regdomain;
+extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
bool is_world_regdom(const char *alpha2);
-bool reg_is_valid_request(const char *alpha2);
bool reg_supported_dfs_region(u8 dfs_region);
int regulatory_hint_user(const char *alpha2,
@@ -55,8 +54,8 @@ bool reg_last_request_cell_base(void);
* set the wiphy->disable_beacon_hints to true.
*/
int regulatory_hint_found_beacon(struct wiphy *wiphy,
- struct ieee80211_channel *beacon_chan,
- gfp_t gfp);
+ struct ieee80211_channel *beacon_chan,
+ gfp_t gfp);
/**
* regulatory_hint_11d - hints a country IE as a regulatory domain
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f2431e41a373..d2d26518cdd7 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -519,10 +519,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
* - country_ie + 2, the start of the country ie data, and
* - and country_ie[1] which is the IE length
*/
- regulatory_hint_11d(wdev->wiphy,
- bss->channel->band,
- country_ie + 2,
- country_ie[1]);
+ regulatory_hint_11d(wdev->wiphy, bss->channel->band,
+ country_ie + 2, country_ie[1]);
kfree(country_ie);
}