From 0037f59694f10c30603da07530859ce4cec65536 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 5 Dec 2012 15:07:54 +0200 Subject: [PATCH] --- yaml --- r: 351773 b: refs/heads/master c: 046db346386661906dffa33f5ed3dfcdccfddc0b h: refs/heads/master i: 351771: 104182d3af551081a9402d1134f50c7826ebb8b3 v: v3 --- [refs] | 2 +- .../net/wireless/ath/ath9k/htc_drv_main.c | 4 +- trunk/drivers/net/wireless/ath/ath9k/main.c | 4 +- .../drivers/net/wireless/ath/carl9170/main.c | 4 +- trunk/drivers/net/wireless/ath/regd.c | 19 +- .../net/wireless/brcm80211/brcmsmac/channel.c | 7 +- .../wireless/brcm80211/brcmsmac/mac80211_if.c | 4 +- .../drivers/net/wireless/iwlegacy/4965-mac.c | 4 +- .../net/wireless/iwlwifi/dvm/mac80211.c | 4 +- .../drivers/net/wireless/iwlwifi/iwl-trans.h | 3 + .../net/wireless/iwlwifi/pcie/internal.h | 2 + .../drivers/net/wireless/iwlwifi/pcie/trans.c | 1 + trunk/drivers/net/wireless/iwlwifi/pcie/tx.c | 5 +- trunk/drivers/net/wireless/mac80211_hwsim.c | 30 +- trunk/drivers/net/wireless/mwl8k.c | 4 +- trunk/drivers/net/wireless/rt2x00/rt2800lib.c | 4 +- trunk/drivers/net/wireless/rtlwifi/core.c | 4 +- trunk/drivers/net/wireless/rtlwifi/regd.c | 17 +- trunk/drivers/net/wireless/ti/wlcore/main.c | 4 +- trunk/include/linux/ieee80211.h | 100 +- trunk/include/net/cfg80211.h | 32 +- trunk/include/net/mac80211.h | 33 +- trunk/include/net/regulatory.h | 4 - trunk/include/uapi/linux/nl80211.h | 16 - trunk/net/mac80211/agg-tx.c | 253 ++-- trunk/net/mac80211/cfg.c | 139 +-- trunk/net/mac80211/chan.c | 41 +- trunk/net/mac80211/driver-ops.h | 23 +- trunk/net/mac80211/ht.c | 33 +- trunk/net/mac80211/ibss.c | 66 +- trunk/net/mac80211/ieee80211_i.h | 39 +- trunk/net/mac80211/iface.c | 76 +- trunk/net/mac80211/main.c | 70 +- trunk/net/mac80211/mesh.c | 42 +- trunk/net/mac80211/mesh.h | 2 +- trunk/net/mac80211/mlme.c | 143 +-- trunk/net/mac80211/offchannel.c | 15 +- trunk/net/mac80211/pm.c | 64 +- trunk/net/mac80211/rx.c | 31 +- trunk/net/mac80211/scan.c | 55 +- trunk/net/mac80211/sta_info.c | 66 +- trunk/net/mac80211/sta_info.h | 45 +- trunk/net/mac80211/util.c | 18 +- trunk/net/wireless/ap.c | 62 - trunk/net/wireless/chan.c | 4 - trunk/net/wireless/core.c | 14 +- trunk/net/wireless/core.h | 18 +- trunk/net/wireless/mlme.c | 62 + trunk/net/wireless/nl80211.c | 114 +- trunk/net/wireless/reg.c | 1111 ++++++++++------- trunk/net/wireless/reg.h | 7 +- trunk/net/wireless/sme.c | 6 +- 52 files changed, 1348 insertions(+), 1582 deletions(-) diff --git a/[refs] b/[refs] index 50cf14c4afd7..4f4fae407fb4 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: ba23d2068d85f6616ea5f92320c04e87d4b9e141 +refs/heads/master: 046db346386661906dffa33f5ed3dfcdccfddc0b diff --git a/trunk/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/trunk/drivers/net/wireless/ath/ath9k/htc_drv_main.c index a8016d70088a..9c07a8fa5134 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/trunk/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1628,9 +1628,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; diff --git a/trunk/drivers/net/wireless/ath/ath9k/main.c b/trunk/drivers/net/wireless/ath/ath9k/main.c index e1fa70596e61..be30a9af1528 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/main.c +++ b/trunk/drivers/net/wireless/ath/ath9k/main.c @@ -1610,9 +1610,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: ath9k_ps_wakeup(sc); ath_tx_aggr_stop(sc, sta, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); diff --git a/trunk/drivers/net/wireless/ath/carl9170/main.c b/trunk/drivers/net/wireless/ath/carl9170/main.c index 9d2051aeb782..25a1e2f4f738 100644 --- a/trunk/drivers/net/wireless/ath/carl9170/main.c +++ b/trunk/drivers/net/wireless/ath/carl9170/main.c @@ -1394,9 +1394,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: rcu_read_lock(); tid_info = rcu_dereference(sta_info->agg[tid]); if (tid_info) { diff --git a/trunk/drivers/net/wireless/ath/regd.c b/trunk/drivers/net/wireless/ath/regd.c index 7a6c79e1f819..d81698015bf7 100644 --- a/trunk/drivers/net/wireless/ath/regd.c +++ b/trunk/drivers/net/wireless/ath/regd.c @@ -195,6 +195,8 @@ 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++) { @@ -212,8 +214,11 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (IS_ERR(reg_rule)) + r = freq_reg_info(wiphy, + ch->center_freq, + bandwidth, + ®_rule); + if (r) continue; /* * If 11d had a rule for this channel ensure @@ -249,6 +254,8 @@ 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) @@ -276,16 +283,16 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { 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 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/trunk/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/trunk/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 4eb3f0d52105..a90b72202ec5 100644 --- a/trunk/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/trunk/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; + int band, i, ret; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -685,8 +685,9 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - rule = freq_reg_info(wiphy, ch->center_freq); - if (IS_ERR(rule)) + ret = freq_reg_info(wiphy, ch->center_freq, + 0, &rule); + if (ret) continue; if (!(rule->flags & NL80211_RRF_NO_IBSS)) diff --git a/trunk/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/trunk/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f0fc8cd4d5df..1fbd8ecbe2ea 100644 --- a/trunk/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/trunk/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -668,9 +668,7 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: spin_lock_bh(&wl->lock); brcms_c_ampdu_flush(wl->wlc, sta, tid); spin_unlock_bh(&wl->lock); diff --git a/trunk/drivers/net/wireless/iwlegacy/4965-mac.c b/trunk/drivers/net/wireless/iwlegacy/4965-mac.c index 6a86ed45835d..c3fbf6717564 100644 --- a/trunk/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/trunk/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5968,9 +5968,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: 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/trunk/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/trunk/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 02fdcea76b21..3163e0f38c25 100644 --- a/trunk/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/trunk/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -679,9 +679,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: 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/trunk/drivers/net/wireless/iwlwifi/iwl-trans.h b/trunk/drivers/net/wireless/iwlwifi/iwl-trans.h index b76532e238c1..15b4700d2a2f 100644 --- a/trunk/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/trunk/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -321,6 +321,8 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) * @n_no_reclaim_cmds: # of commands in list * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, * if unset 4k will be the RX buffer size + * @bc_table_dword: set to true if the BC table expects the byte count to be + * in DWORD (as opposed to bytes) * @queue_watchdog_timeout: time (in ms) after which queues * are considered stuck and will trigger device restart * @command_names: array of command names, must be 256 entries @@ -335,6 +337,7 @@ struct iwl_trans_config { int n_no_reclaim_cmds; bool rx_buf_size_8k; + bool bc_table_dword; unsigned int queue_watchdog_timeout; const char **command_names; }; diff --git a/trunk/drivers/net/wireless/iwlwifi/pcie/internal.h b/trunk/drivers/net/wireless/iwlwifi/pcie/internal.h index d91d2e8c62f5..15f79754b67b 100644 --- a/trunk/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/trunk/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -234,6 +234,7 @@ struct iwl_txq { * @status - transport specific status flags * @cmd_queue - command queue number * @rx_buf_size_8k: 8 kB RX buffer size + * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @rx_page_order: page order for receive buffer size * @wd_timeout: queue watchdog timeout (jiffies) */ @@ -279,6 +280,7 @@ struct iwl_trans_pcie { u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; bool rx_buf_size_8k; + bool bc_table_dword; u32 rx_page_order; const char **command_names; diff --git a/trunk/drivers/net/wireless/iwlwifi/pcie/trans.c b/trunk/drivers/net/wireless/iwlwifi/pcie/trans.c index 35708b959ad6..ab179ce36de5 100644 --- a/trunk/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/trunk/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -703,6 +703,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); trans_pcie->command_names = trans_cfg->command_names; + trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; } void iwl_trans_pcie_free(struct iwl_trans *trans) diff --git a/trunk/drivers/net/wireless/iwlwifi/pcie/tx.c b/trunk/drivers/net/wireless/iwlwifi/pcie/tx.c index 6c5b867c353a..7af8f0b55d2d 100644 --- a/trunk/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/trunk/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -237,7 +237,10 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, break; } - bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12)); + if (trans_pcie->bc_table_dword) + len = DIV_ROUND_UP(len, 4); + + bc_ent = cpu_to_le16(len | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; diff --git a/trunk/drivers/net/wireless/mac80211_hwsim.c b/trunk/drivers/net/wireless/mac80211_hwsim.c index d248a4cc6656..ff9085502bea 100644 --- a/trunk/drivers/net/wireless/mac80211_hwsim.c +++ b/trunk/drivers/net/wireless/mac80211_hwsim.c @@ -48,10 +48,6 @@ 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 * @@ -759,25 +755,9 @@ 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 */ - 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; - } + nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC); + if (nskb == NULL) + continue; if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; @@ -1312,9 +1292,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/trunk/drivers/net/wireless/mwl8k.c b/trunk/drivers/net/wireless/mwl8k.c index 19b46fdf9f0f..f221b95b90b3 100644 --- a/trunk/drivers/net/wireless/mwl8k.c +++ b/trunk/drivers/net/wireless/mwl8k.c @@ -5170,9 +5170,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { spin_unlock(&priv->stream_lock); diff --git a/trunk/drivers/net/wireless/rt2x00/rt2800lib.c b/trunk/drivers/net/wireless/rt2x00/rt2800lib.c index 12f93e42e160..197b4466a5d2 100644 --- a/trunk/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/trunk/drivers/net/wireless/rt2x00/rt2800lib.c @@ -5484,9 +5484,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/trunk/drivers/net/wireless/rtlwifi/core.c b/trunk/drivers/net/wireless/rtlwifi/core.c index d3ce9fbef00e..be33aa14c8af 100644 --- a/trunk/drivers/net/wireless/rtlwifi/core.c +++ b/trunk/drivers/net/wireless/rtlwifi/core.c @@ -879,9 +879,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: 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/trunk/drivers/net/wireless/rtlwifi/regd.c b/trunk/drivers/net/wireless/rtlwifi/regd.c index 7e3ead774fb9..c1608cddc529 100644 --- a/trunk/drivers/net/wireless/rtlwifi/regd.c +++ b/trunk/drivers/net/wireless/rtlwifi/regd.c @@ -158,6 +158,8 @@ 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++) { @@ -172,8 +174,9 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (IS_ERR(reg_rule)) + r = freq_reg_info(wiphy, ch->center_freq, + bandwidth, ®_rule); + if (r) continue; /* @@ -208,6 +211,8 @@ 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; @@ -235,16 +240,16 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { 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 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/trunk/drivers/net/wireless/ti/wlcore/main.c b/trunk/drivers/net/wireless/ti/wlcore/main.c index d7de06359ae1..ea9d8e011bc9 100644 --- a/trunk/drivers/net/wireless/ti/wlcore/main.c +++ b/trunk/drivers/net/wireless/ti/wlcore/main.c @@ -4575,9 +4575,7 @@ 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_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_OPERATIONAL: ret = -EINVAL; break; diff --git a/trunk/include/linux/ieee80211.h b/trunk/include/linux/ieee80211.h index ccf9ee1dca8c..f0859cc73861 100644 --- a/trunk/include/linux/ieee80211.h +++ b/trunk/include/linux/ieee80211.h @@ -180,7 +180,7 @@ struct ieee80211_hdr { u8 addr3[6]; __le16 seq_ctrl; u8 addr4[6]; -} __packed; +} __attribute__ ((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; -} __packed; +} __attribute__ ((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; -} __packed; +} __attribute__ ((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]; -} __packed; +} __attribute__ ((packed)); /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 @@ -614,7 +614,7 @@ struct ieee80211_quiet_ie { u8 period; __le16 duration; __le16 offset; -} __packed; +} __attribute__ ((packed)); /** * struct ieee80211_msrment_ie @@ -626,7 +626,7 @@ struct ieee80211_msrment_ie { u8 mode; u8 type; u8 request[0]; -} __packed; +} __attribute__ ((packed)); /** * struct ieee80211_channel_sw_ie @@ -637,7 +637,7 @@ struct ieee80211_channel_sw_ie { u8 mode; u8 new_ch_num; u8 count; -} __packed; +} __attribute__ ((packed)); /** * struct ieee80211_tim @@ -650,7 +650,7 @@ struct ieee80211_tim_ie { u8 bitmap_ctrl; /* variable size: 1 - 251 bytes */ u8 virtual_map[1]; -} __packed; +} __attribute__ ((packed)); /** * struct ieee80211_meshconf_ie @@ -665,7 +665,7 @@ struct ieee80211_meshconf_ie { u8 meshconf_auth; u8 meshconf_form; u8 meshconf_cap; -} __packed; +} __attribute__ ((packed)); /** * enum mesh_config_capab_flags - Mesh Configuration IE capability field flags @@ -695,17 +695,12 @@ struct ieee80211_rann_ie { __le32 rann_seq; __le32 rann_interval; __le32 rann_metric; -} __packed; +} __attribute__ ((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 { @@ -722,33 +717,33 @@ struct ieee80211_mgmt { __le16 status_code; /* possibly followed by Challenge text */ u8 variable[0]; - } __packed auth; + } __attribute__ ((packed)) auth; struct { __le16 reason_code; - } __packed deauth; + } __attribute__ ((packed)) deauth; struct { __le16 capab_info; __le16 listen_interval; /* followed by SSID and Supported rates */ u8 variable[0]; - } __packed assoc_req; + } __attribute__ ((packed)) assoc_req; struct { __le16 capab_info; __le16 status_code; __le16 aid; /* followed by Supported rates */ u8 variable[0]; - } __packed assoc_resp, reassoc_resp; + } __attribute__ ((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]; - } __packed reassoc_req; + } __attribute__ ((packed)) reassoc_req; struct { __le16 reason_code; - } __packed disassoc; + } __attribute__ ((packed)) disassoc; struct { __le64 timestamp; __le16 beacon_int; @@ -756,11 +751,11 @@ struct ieee80211_mgmt { /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[0]; - } __packed beacon; + } __attribute__ ((packed)) beacon; struct { /* only variable items: SSID, Supported rates */ u8 variable[0]; - } __packed probe_req; + } __attribute__ ((packed)) probe_req; struct { __le64 timestamp; __le16 beacon_int; @@ -768,7 +763,7 @@ struct ieee80211_mgmt { /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ u8 variable[0]; - } __packed probe_resp; + } __attribute__ ((packed)) probe_resp; struct { u8 category; union { @@ -777,59 +772,55 @@ struct ieee80211_mgmt { u8 dialog_token; u8 status_code; u8 variable[0]; - } __packed wme_action; + } __attribute__ ((packed)) wme_action; struct{ u8 action_code; u8 element_id; u8 length; struct ieee80211_channel_sw_ie sw_elem; - } __packed chan_switch; + } __attribute__((packed)) chan_switch; struct{ u8 action_code; u8 dialog_token; u8 element_id; u8 length; struct ieee80211_msrment_ie msr_elem; - } __packed measurement; + } __attribute__((packed)) measurement; struct{ u8 action_code; u8 dialog_token; __le16 capab; __le16 timeout; __le16 start_seq_num; - } __packed addba_req; + } __attribute__((packed)) addba_req; struct{ u8 action_code; u8 dialog_token; __le16 status; __le16 capab; __le16 timeout; - } __packed addba_resp; + } __attribute__((packed)) addba_resp; struct{ u8 action_code; __le16 params; __le16 reason_code; - } __packed delba; + } __attribute__((packed)) delba; struct { u8 action_code; u8 variable[0]; - } __packed self_prot; + } __attribute__((packed)) self_prot; struct{ u8 action_code; u8 variable[0]; - } __packed mesh_action; + } __attribute__((packed)) mesh_action; struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; - } __packed sa_query; + } __attribute__ ((packed)) sa_query; struct { u8 action; u8 smps_control; - } __packed ht_smps; - struct { - u8 action_code; - u8 chanwidth; - } __packed ht_notify_cw; + } __attribute__ ((packed)) ht_smps; struct { u8 action_code; u8 dialog_token; @@ -837,9 +828,9 @@ struct ieee80211_mgmt { u8 variable[0]; } __packed tdls_discover_resp; } u; - } __packed action; + } __attribute__ ((packed)) action; } u; -} __packed; +} __attribute__ ((packed)); /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 @@ -855,7 +846,7 @@ struct ieee80211_mmie { __le16 key_id; u8 sequence_number[6]; u8 mic[8]; -} __packed; +} __attribute__ ((packed)); struct ieee80211_vendor_ie { u8 element_id; @@ -870,20 +861,20 @@ struct ieee80211_rts { __le16 duration; u8 ra[6]; u8 ta[6]; -} __packed; +} __attribute__ ((packed)); struct ieee80211_cts { __le16 frame_control; __le16 duration; u8 ra[6]; -} __packed; +} __attribute__ ((packed)); struct ieee80211_pspoll { __le16 frame_control; __le16 aid; u8 bssid[6]; u8 ta[6]; -} __packed; +} __attribute__ ((packed)); /* TDLS */ @@ -976,7 +967,7 @@ struct ieee80211_bar { __u8 ta[6]; __le16 control; __le16 start_seq_num; -} __packed; +} __attribute__((packed)); /* 802.11 BAR control masks */ #define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 @@ -1001,7 +992,7 @@ struct ieee80211_mcs_info { __le16 rx_highest; u8 tx_params; u8 reserved[3]; -} __packed; +} __attribute__((packed)); /* 802.11n HT capability MSC set */ #define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff @@ -1040,7 +1031,7 @@ struct ieee80211_ht_cap { __le16 extended_ht_cap_info; __le32 tx_BF_cap_info; u8 antenna_selection_info; -} __packed; +} __attribute__ ((packed)); /* 802.11n HT capabilities masks (for cap_info) */ #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 @@ -1111,7 +1102,7 @@ struct ieee80211_ht_operation { __le16 operation_mode; __le16 stbc_param; u8 basic_set[16]; -} __packed; +} __attribute__ ((packed)); /* for ht_param */ #define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 @@ -1320,21 +1311,16 @@ 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) @@ -1848,14 +1834,14 @@ struct ieee80211_country_ie_triplet { u8 first_channel; u8 num_channels; s8 max_power; - } __packed chans; + } __attribute__ ((packed)) chans; struct { u8 reg_extension_id; u8 reg_class; u8 coverage_class; - } __packed ext; + } __attribute__ ((packed)) ext; }; -} __packed; +} __attribute__ ((packed)); enum ieee80211_timeout_interval_type { WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, diff --git a/trunk/include/net/cfg80211.h b/trunk/include/net/cfg80211.h index e5f085c89221..8e6a6b73b9c9 100644 --- a/trunk/include/net/cfg80211.h +++ b/trunk/include/net/cfg80211.h @@ -1256,7 +1256,7 @@ struct cfg80211_bss { u8 bssid[ETH_ALEN]; - u8 priv[0] __aligned(sizeof(void *)); + u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; /** @@ -2369,7 +2369,7 @@ struct wiphy { /* fields below are read-only, assigned by cfg80211 */ - const struct ieee80211_regdomain __rcu *regd; + const struct ieee80211_regdomain *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] __aligned(NETDEV_ALIGN); + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; static inline struct net *wiphy_net(struct wiphy *wiphy) @@ -2938,22 +2938,28 @@ 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. * - * 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); + * 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); /* * callbacks for asynchronous cfg80211 methods, notification diff --git a/trunk/include/net/mac80211.h b/trunk/include/net/mac80211.h index 23daed3c78ed..ee50c5eba50c 100644 --- a/trunk/include/net/mac80211.h +++ b/trunk/include/net/mac80211.h @@ -173,7 +173,7 @@ struct ieee80211_chanctx_conf { u8 rx_chains_static, rx_chains_dynamic; - u8 drv_priv[0] __aligned(sizeof(void *)); + u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; /** @@ -1059,7 +1059,7 @@ struct ieee80211_vif { u32 driver_flags; /* must be last */ - u8 drv_priv[0] __aligned(sizeof(void *)); + u8 drv_priv[0] __attribute__((__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] __aligned(sizeof(void *)); + u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; /** @@ -2033,29 +2033,17 @@ 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_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_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_CONT, - IEEE80211_AMPDU_TX_STOP_FLUSH, - IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, + IEEE80211_AMPDU_TX_STOP, IEEE80211_AMPDU_TX_OPERATIONAL, }; @@ -3766,11 +3754,6 @@ 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/trunk/include/net/regulatory.h b/trunk/include/net/regulatory.h index f17ed590d64a..7dcaa2794fde 100644 --- a/trunk/include/net/regulatory.h +++ b/trunk/include/net/regulatory.h @@ -18,7 +18,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include /** * enum environment_cap - Environment parsed from country IE @@ -36,7 +35,6 @@ 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 @@ -74,7 +72,6 @@ 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; @@ -104,7 +101,6 @@ 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/trunk/include/uapi/linux/nl80211.h b/trunk/include/uapi/linux/nl80211.h index 547017100a30..e3e19f8b16f2 100644 --- a/trunk/include/uapi/linux/nl80211.h +++ b/trunk/include/uapi/linux/nl80211.h @@ -1697,9 +1697,6 @@ 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 */ @@ -1711,7 +1708,6 @@ 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, @@ -3144,17 +3140,6 @@ 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, @@ -3170,7 +3155,6 @@ 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/trunk/net/mac80211/agg-tx.c b/trunk/net/mac80211/agg-tx.c index 2f0ccbc5f13e..eb9df22418f0 100644 --- a/trunk/net/mac80211/agg-tx.c +++ b/trunk/net/mac80211/agg-tx.c @@ -149,133 +149,16 @@ 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_agg_stop_reason reason) + enum ieee80211_back_parties initiator, + bool tx) { 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); @@ -284,19 +167,10 @@ 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 - * unless we're destroying it in which case notify the driver - */ + /* if we're already stopping ignore any new requests to stop */ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { spin_unlock_bh(&sta->lock); - 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; + return -EALREADY; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { @@ -338,12 +212,11 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ synchronize_net(); - tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ? - WLAN_BACK_RECIPIENT : - WLAN_BACK_INITIATOR; - tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; + tid_tx->stop_initiator = initiator; + tid_tx->tx_stop = tx; - ret = drv_ampdu_action(local, sta->sdata, action, + ret = drv_ampdu_action(local, sta->sdata, + IEEE80211_AMPDU_TX_STOP, &sta->sta, tid, NULL, 0); /* HW shall not deny going back to legacy */ @@ -354,14 +227,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ } - 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; + return ret; } /* @@ -398,6 +264,80 @@ 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; @@ -720,13 +660,14 @@ 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_agg_stop_reason reason) + enum ieee80211_back_parties initiator, + bool tx) { int ret; mutex_lock(&sta->ampdu_mlme.mtx); - ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason); + ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx); mutex_unlock(&sta->ampdu_mlme.mtx); @@ -810,7 +751,24 @@ 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); - ieee80211_remove_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); unlock_sta: spin_unlock_bh(&sta->lock); @@ -910,7 +868,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, } } else { - ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); + ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, + false); } out: diff --git a/trunk/net/mac80211/cfg.c b/trunk/net/mac80211/cfg.c index 7d290bce0927..5c61677487cf 100644 --- a/trunk/net/mac80211/cfg.c +++ b/trunk/net/mac80211/cfg.c @@ -510,7 +510,6 @@ 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); @@ -522,8 +521,6 @@ 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); } @@ -933,7 +930,6 @@ 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) @@ -1013,16 +1009,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) if (old_probe_resp) kfree_rcu(old_probe_resp, rcu_head); - 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); + sta_info_flush(local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); @@ -1080,58 +1067,6 @@ 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) @@ -1149,20 +1084,52 @@ static int sta_apply_parameters(struct ieee80211_local *local, mask = params->sta_flags_mask; set = params->sta_flags_set; - 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); + /* + * 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; + } } - 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)) @@ -1208,11 +1175,10 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.aid = params->aid; /* - * 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. + * 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. */ if (params->listen_interval >= 0) @@ -1297,10 +1263,6 @@ 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); @@ -1337,6 +1299,7 @@ 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); @@ -1344,7 +1307,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(sdata); + sta_info_flush(local, sdata); return 0; } diff --git a/trunk/net/mac80211/chan.c b/trunk/net/mac80211/chan.c index 1bfe0a8b19d2..53f03120db55 100644 --- a/trunk/net/mac80211/chan.c +++ b/trunk/net/mac80211/chan.c @@ -4,7 +4,6 @@ #include #include -#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -198,15 +197,6 @@ 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); @@ -326,15 +316,6 @@ 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); @@ -350,25 +331,6 @@ 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, @@ -381,8 +343,7 @@ void ieee80211_iter_chan_contexts_atomic( rcu_read_lock(); list_for_each_entry_rcu(ctx, &local->chanctx_list, list) - if (ctx->driver_present) - iter(hw, &ctx->conf, iter_data); + iter(hw, &ctx->conf, iter_data); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); diff --git a/trunk/net/mac80211/driver-ops.h b/trunk/net/mac80211/driver-ops.h index 0c07f94c5378..698dc7e6f309 100644 --- a/trunk/net/mac80211/driver-ops.h +++ b/trunk/net/mac80211/driver-ops.h @@ -207,14 +207,6 @@ 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); @@ -921,8 +913,6 @@ 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; } @@ -934,7 +924,6 @@ 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, @@ -942,10 +931,8 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, u32 changed) { trace_drv_change_chanctx(local, ctx, changed); - if (local->ops->change_chanctx) { - WARN_ON_ONCE(!ctx->driver_present); + if (local->ops->change_chanctx) local->ops->change_chanctx(&local->hw, &ctx->conf, changed); - } trace_drv_return_void(local); } @@ -958,12 +945,10 @@ 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) { - WARN_ON_ONCE(!ctx->driver_present); + if (local->ops->assign_vif_chanctx) ret = local->ops->assign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); - } trace_drv_return_int(local, ret); return ret; @@ -976,12 +961,10 @@ 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) { - WARN_ON_ONCE(!ctx->driver_present); + if (local->ops->unassign_vif_chanctx) local->ops->unassign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); - } trace_drv_return_void(local); } diff --git a/trunk/net/mac80211/ht.c b/trunk/net/mac80211/ht.c index 61ac7c48ac0c..a71d891794a4 100644 --- a/trunk/net/mac80211/ht.c +++ b/trunk/net/mac80211/ht.c @@ -62,9 +62,6 @@ 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); @@ -120,21 +117,6 @@ 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. @@ -197,19 +179,16 @@ 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, - enum ieee80211_agg_stop_reason reason) +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) { int i; cancel_work_sync(&sta->ampdu_mlme.work); for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - __ieee80211_stop_tx_ba_session(sta, i, reason); + __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, - WLAN_REASON_QSTA_LEAVE_QBSS, - reason != AGG_STOP_DESTROY_STA && - reason != AGG_STOP_PEER_REQUEST); + WLAN_REASON_QSTA_LEAVE_QBSS, tx); } } @@ -266,7 +245,8 @@ 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, - AGG_STOP_LOCAL_REQUEST); + WLAN_BACK_INITIATOR, + true); } mutex_unlock(&sta->ampdu_mlme.mtx); } @@ -334,7 +314,8 @@ 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, AGG_STOP_PEER_REQUEST); + __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, + true); } int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, diff --git a/trunk/net/mac80211/ibss.c b/trunk/net/mac80211/ibss.c index b4b866f41919..8881fc77fb13 100644 --- a/trunk/net/mac80211/ibss.c +++ b/trunk/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); + sta_info_flush(sdata->local, sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { @@ -191,7 +191,6 @@ 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; @@ -426,9 +425,11 @@ 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) + struct ieee802_11_elems *elems, + bool beacon) { struct ieee80211_local *local = sdata->local; int freq; @@ -529,7 +530,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); + channel, beacon); if (!bss) return; @@ -702,8 +703,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_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, - NULL); + ieee80211_request_internal_scan(sdata, + ifibss->ssid, ifibss->ssid_len, NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -801,8 +802,9 @@ 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_ibss_scan(sdata, ifibss->ssid, - ifibss->ssid_len, chan); + ieee80211_request_internal_scan(sdata, + ifibss->ssid, ifibss->ssid_len, + ifibss->fixed_channel ? ifibss->channel : NULL); } else { int interval = IEEE80211_SCAN_INTERVAL; @@ -876,21 +878,14 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); } -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) +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) { 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; @@ -898,7 +893,25 @@ void ieee80211_rx_mgmt_probe_beacon(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); + 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); } void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -922,9 +935,12 @@ 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_probe_beacon(sdata, mgmt, skb->len, - rx_status); + ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, + rx_status); break; case IEEE80211_STYPE_AUTH: ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); @@ -1167,7 +1183,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); + sta_info_flush(sdata->local, sdata); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { @@ -1190,8 +1206,6 @@ 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/trunk/net/mac80211/ieee80211_i.h b/trunk/net/mac80211/ieee80211_i.h index 0fa44a965ad9..42d0d0267730 100644 --- a/trunk/net/mac80211/ieee80211_i.h +++ b/trunk/net/mac80211/ieee80211_i.h @@ -92,6 +92,8 @@ struct ieee80211_bss { u32 device_ts; + u8 dtim_period; + bool wmm_used; bool uapsd_supported; @@ -138,6 +140,7 @@ 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 @@ -148,6 +151,7 @@ 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) @@ -405,8 +409,6 @@ struct ieee80211_mgd_assoc_data { u8 ap_ht_param; - struct ieee80211_vht_cap ap_vht_cap; - size_t ie_len; u8 ie[]; }; @@ -438,7 +440,6 @@ 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 */ @@ -661,13 +662,10 @@ 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, }; /** @@ -690,7 +688,6 @@ struct ieee80211_chanctx { enum ieee80211_chanctx_mode mode; int refcount; - bool driver_present; struct ieee80211_chanctx_conf conf; }; @@ -776,10 +773,6 @@ 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; @@ -789,11 +782,6 @@ 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; }; @@ -1341,9 +1329,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_ibss_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan); +int ieee80211_request_internal_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); @@ -1357,7 +1345,8 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - struct ieee80211_channel *channel); + struct ieee80211_channel *channel, + bool beacon); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); @@ -1432,8 +1421,7 @@ 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, - enum ieee80211_agg_stop_reason reason); +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); @@ -1447,9 +1435,11 @@ 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_agg_stop_reason reason); + enum ieee80211_back_parties initiator, + bool tx); int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_agg_stop_reason reason); + enum ieee80211_back_parties initiator, + bool tx); 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); @@ -1638,7 +1628,6 @@ 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/trunk/net/mac80211/iface.c b/trunk/net/mac80211/iface.c index 06fac2991d40..09a80b55cf5a 100644 --- a/trunk/net/mac80211/iface.c +++ b/trunk/net/mac80211/iface.c @@ -207,8 +207,17 @@ void ieee80211_recalc_idle(struct ieee80211_local *local) static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) { - if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) + 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) { return -EINVAL; + } dev->mtu = new_mtu; return 0; @@ -577,13 +586,11 @@ 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 and chanctx */ - if (rtnl_dereference(sdata->bss->beacon)) { - ieee80211_vif_vlan_copy_chanctx(sdata); + /* no need to tell driver, but set carrier */ + if (rtnl_dereference(sdata->bss->beacon)) netif_carrier_on(dev); - } else { + else netif_carrier_off(dev); - } break; case NL80211_IFTYPE_MONITOR: if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { @@ -747,7 +754,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, flushed; + int i; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -772,15 +779,11 @@ 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 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. + * This is relevant only in AP, WDS and mesh modes, since in + * all other modes we've already removed all stations when + * disconnecting etc. */ - 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)); + sta_info_flush(local, sdata); /* * Don't count this interface for promisc/allmulti while it @@ -836,7 +839,6 @@ 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: @@ -863,16 +865,19 @@ 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. - * - * 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. + * 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. */ rcu_barrier(); - sta_info_flush_cleanup(sdata); + + /* + * 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); skb_queue_purge(&sdata->skb_queue); @@ -971,6 +976,7 @@ 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; @@ -986,7 +992,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(sdata); + flushed = sta_info_flush(local, sdata); WARN_ON(flushed); } @@ -1227,7 +1233,6 @@ 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; @@ -1235,11 +1240,9 @@ 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: @@ -1253,12 +1256,8 @@ 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: @@ -1499,15 +1498,6 @@ 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) @@ -1583,10 +1573,6 @@ 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/trunk/net/mac80211/main.c b/trunk/net/mac80211/main.c index 39cfe8f10ad2..1b087fff93e7 100644 --- a/trunk/net/mac80211/main.c +++ b/trunk/net/mac80211/main.c @@ -207,10 +207,76 @@ 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); } @@ -471,7 +537,6 @@ 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, @@ -541,8 +606,7 @@ 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_FULL_AP_CLIENT_STATE; + NL80211_FEATURE_VIF_TXPOWER; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | diff --git a/trunk/net/mac80211/mesh.c b/trunk/net/mac80211/mesh.c index 245885841c8d..1bf03f9ff3ba 100644 --- a/trunk/net/mac80211/mesh.c +++ b/trunk/net/mac80211/mesh.c @@ -20,11 +20,16 @@ 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) { @@ -158,7 +163,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]); + INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list); return 0; } @@ -172,7 +177,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_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { list_del(&p->list); kmem_cache_free(rm_cache, p); } @@ -205,7 +210,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_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { ++entries; if (time_after(jiffies, p->exp_time) || (entries == RMC_QUEUE_MAX_LEN)) { @@ -224,7 +229,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_add(&p->list, &rmc->bucket[idx].list); return 0; } @@ -602,12 +607,6 @@ 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 */ @@ -626,16 +625,14 @@ 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(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); + 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); netif_carrier_on(sdata->dev); } @@ -649,12 +646,10 @@ 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); + sta_info_flush(sdata->local, sdata); mesh_path_flush_by_iface(sdata); del_timer_sync(&sdata->u.mesh.housekeeping_timer); @@ -810,7 +805,6 @@ 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, @@ -836,6 +830,4 @@ 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/trunk/net/mac80211/mesh.h b/trunk/net/mac80211/mesh.h index 84c28c6101cd..7c9215fb2ac8 100644 --- a/trunk/net/mac80211/mesh.h +++ b/trunk/net/mac80211/mesh.h @@ -184,7 +184,7 @@ struct rmc_entry { }; struct mesh_rmc { - struct list_head bucket[RMC_BUCKETS]; + struct rmc_entry bucket[RMC_BUCKETS]; u32 idx_mask; }; diff --git a/trunk/net/mac80211/mlme.c b/trunk/net/mac80211/mlme.c index d90c07b1795f..7753a9ca98a6 100644 --- a/trunk/net/mac80211/mlme.c +++ b/trunk/net/mac80211/mlme.c @@ -341,13 +341,11 @@ 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_vht_cap *ap_vht_cap) + struct ieee80211_supported_band *sband) { u8 *pos; u32 cap; struct ieee80211_sta_vht_cap vht_cap; - int i; BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); @@ -366,42 +364,6 @@ 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); @@ -600,8 +562,7 @@ 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, - &assoc_data->ap_vht_cap); + ieee80211_add_vht_ie(sdata, skb, sband); /* if present, add any custom non-vendor IEs that go after HT */ if (assoc_data->ie_len && assoc_data->ie) { @@ -1113,8 +1074,12 @@ 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 = found->u.mgd.dtim_period; + u8 dtimper; + + bss = (void *)found->u.mgd.associated->priv; + dtimper = bss->dtim_period; /* If the TIM IE is invalid, pretend the value is 1 */ if (!dtimper) @@ -1445,17 +1410,10 @@ 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) { - /* - * 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 { + if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) + bss_conf->dtim_period = bss->dtim_period; + else bss_conf->dtim_period = 0; - } bss_conf->assoc = 1; @@ -1525,7 +1483,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, AGG_STOP_DESTROY_STA); + ieee80211_sta_tear_down_BA_sessions(sta, false); } mutex_unlock(&local->sta_mtx); @@ -1560,7 +1518,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_defer(sdata); + sta_info_flush(local, sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); @@ -1604,8 +1562,6 @@ 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); } @@ -2408,7 +2364,8 @@ 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) + struct ieee802_11_elems *elems, + bool beacon) { struct ieee80211_local *local = sdata->local; int freq; @@ -2416,18 +2373,11 @@ 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)) || - (sdata->u.mgd.assoc_data && - ether_addr_equal(mgmt->bssid, - sdata->u.mgd.assoc_data->bss->bssid))) { + if (sdata->u.mgd.associated && + ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) { + bss = (void *)sdata->u.mgd.associated->priv; /* not previously set so we may need to recalc */ - need_ps = 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; - } + need_ps = !bss->dtim_period; } if (elems->ds_params && elems->ds_params_len == 1) @@ -2442,7 +2392,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); + channel, beacon); if (bss) ieee80211_rx_bss_put(local, bss); @@ -2485,7 +2435,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); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) @@ -2566,7 +2516,8 @@ 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); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, + false); ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->sent_assoc = false; /* continue assoc process */ @@ -2719,7 +2670,8 @@ 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); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, + true); if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len)) @@ -3792,7 +3744,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, *vht_ie; + const u8 *ssidie, *ht_ie; int i, err; assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); @@ -3911,12 +3863,6 @@ 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 && @@ -3950,41 +3896,20 @@ 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 (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(); + 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); } else { assoc_data->have_beacon = true; assoc_data->sent_assoc = false; diff --git a/trunk/net/mac80211/offchannel.c b/trunk/net/mac80211/offchannel.c index 1430b48600fc..a5379aea7d09 100644 --- a/trunk/net/mac80211/offchannel.c +++ b/trunk/net/mac80211/offchannel.c @@ -126,13 +126,11 @@ 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.bss_conf.enable_beacon) { - set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, - &sdata->state); - sdata->vif.bss_conf.enable_beacon = false; + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); - } if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { netif_tx_stop_all_queues(sdata->dev); @@ -185,12 +183,11 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, - &sdata->state)) { - sdata->vif.bss_conf.enable_beacon = true; + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); - } } mutex_unlock(&local->iflist_mtx); } diff --git a/trunk/net/mac80211/pm.c b/trunk/net/mac80211/pm.c index e45b83610e85..79a48f37d409 100644 --- a/trunk/net/mac80211/pm.c +++ b/trunk/net/mac80211/pm.c @@ -7,23 +7,25 @@ #include "led.h" /* return value indicates whether the driver should be further notified */ -static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) +static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) { switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_quiesce(sdata); - break; + return true; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_quiesce(sdata); - break; + return true; case NL80211_IFTYPE_MESH_POINT: ieee80211_mesh_quiesce(sdata); - break; + return true; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* don't tell driver about this */ + return false; default: - break; + return true; } - - cancel_work_sync(&sdata->work); } int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) @@ -42,8 +44,7 @@ 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, AGG_STOP_LOCAL_REQUEST); + ieee80211_sta_tear_down_BA_sessions(sta, true); } mutex_unlock(&local->sta_mtx); } @@ -93,9 +94,10 @@ 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) - if (ieee80211_sdata_running(sdata)) - ieee80211_quiesce(sdata); + list_for_each_entry(sdata, &local->interfaces, list) { + cancel_work_sync(&sdata->work); + ieee80211_quiesce(sdata); + } goto suspend; } } @@ -122,43 +124,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - static u8 zero_addr[ETH_ALEN] = {}; - u32 changed = 0; + cancel_work_sync(&sdata->work); - if (!ieee80211_sdata_running(sdata)) + if (!ieee80211_quiesce(sdata)) continue; - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_MONITOR: - /* skip these */ + if (!ieee80211_sdata_running(sdata)) 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 or remove association */ - ieee80211_bss_info_change_notify(sdata, changed); + /* disable beaconing */ + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_BEACON_ENABLED); if (sdata->vif.type == NL80211_IFTYPE_AP && rcu_access_pointer(sdata->u.ap.beacon)) diff --git a/trunk/net/mac80211/rx.c b/trunk/net/mac80211/rx.c index a19089565c4b..580704eba8b8 100644 --- a/trunk/net/mac80211/rx.c +++ b/trunk/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/chanwidth are present */ + /* verify action & smps_control are present */ if (len < IEEE80211_MIN_ACTION_SIZE + 2) goto invalid; @@ -2392,35 +2392,6 @@ 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/trunk/net/mac80211/scan.c b/trunk/net/mac80211/scan.c index 06cbe26892a8..8ed83dcc149f 100644 --- a/trunk/net/mac80211/scan.c +++ b/trunk/net/mac80211/scan.c @@ -65,11 +65,12 @@ 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) + struct ieee80211_channel *channel, + bool beacon) { - bool beacon = ieee80211_is_beacon(mgmt->frame_control); struct cfg80211_bss *cbss; struct ieee80211_bss *bss; int clen, srlen; @@ -112,6 +113,18 @@ 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)) { @@ -202,7 +215,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); + channel, beacon); if (bss) ieee80211_rx_bss_put(local, bss); } @@ -819,9 +832,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, return res; } -int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan) +int ieee80211_request_internal_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; @@ -835,36 +848,22 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, /* fill internal scan request */ if (!chan) { - int i, max_n; - int n_ch = 0; + int i, nchan = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; - - max_n = local->hw.wiphy->bands[band]->n_channels; - for (i = 0; i < max_n; i++) { - struct ieee80211_channel *tmp_ch = + for (i = 0; + i < local->hw.wiphy->bands[band]->n_channels; + i++) { + local->int_scan_req->channels[nchan] = &local->hw.wiphy->bands[band]->channels[i]; - - if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_DISABLED)) - continue; - - local->int_scan_req->channels[n_ch] = tmp_ch; - n_ch++; + nchan++; } } - if (WARN_ON_ONCE(n_ch == 0)) - goto unlock; - - local->int_scan_req->n_channels = n_ch; + local->int_scan_req->n_channels = nchan; } 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/trunk/net/mac80211/sta_info.c b/trunk/net/mac80211/sta_info.c index 9d864ed5f3da..f3e502502fee 100644 --- a/trunk/net/mac80211/sta_info.c +++ b/trunk/net/mac80211/sta_info.c @@ -91,8 +91,9 @@ static int sta_info_hash_del(struct ieee80211_local *local, return -ENOENT; } -static void cleanup_single_sta(struct sta_info *sta) +static void free_sta_work(struct work_struct *wk) { + 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; @@ -104,16 +105,6 @@ static void cleanup_single_sta(struct sta_info *sta) * 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)) { @@ -162,35 +153,11 @@ static void cleanup_single_sta(struct sta_info *sta) 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; - 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); + ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); } /* protected by RCU */ @@ -343,6 +310,7 @@ 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); @@ -784,7 +752,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, AGG_STOP_DESTROY_STA); + ieee80211_sta_tear_down_BA_sessions(sta, false); ret = sta_info_hash_del(local, sta); if (ret) @@ -894,13 +862,21 @@ void sta_info_init(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local) { - del_timer_sync(&local->sta_cleanup); + del_timer(&local->sta_cleanup); + sta_info_flush(local, NULL); } - -int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) +/** + * 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) { - struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; int ret = 0; @@ -908,7 +884,7 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (sdata == sta->sdata) { + if (!sdata || sdata == sta->sdata) { WARN_ON(__sta_info_destroy(sta)); ret++; } @@ -918,12 +894,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) 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/trunk/net/mac80211/sta_info.h b/trunk/net/mac80211/sta_info.h index af7d78aa5523..1489bca9ea97 100644 --- a/trunk/net/mac80211/sta_info.h +++ b/trunk/net/mac80211/sta_info.h @@ -92,13 +92,6 @@ 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). * @@ -306,6 +299,7 @@ struct sta_info { spinlock_t lock; struct work_struct drv_unblock_wk; + struct work_struct free_sta_wk; u16 listen_interval; @@ -555,39 +549,8 @@ 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_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; -} - +int sta_info_flush(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); @@ -600,6 +563,4 @@ 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/trunk/net/mac80211/util.c b/trunk/net/mac80211/util.c index 1b9420730d8c..f11e8c540db4 100644 --- a/trunk/net/mac80211/util.c +++ b/trunk/net/mac80211/util.c @@ -1526,11 +1526,6 @@ 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 | @@ -1555,11 +1550,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* fall through */ case NL80211_IFTYPE_MESH_POINT: - if (sdata->vif.bss_conf.enable_beacon) { - changed |= BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED; - ieee80211_bss_info_change_notify(sdata, changed); - } + changed |= BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED; + ieee80211_bss_info_change_notify(sdata, changed); break; case NL80211_IFTYPE_WDS: break; @@ -1639,8 +1632,7 @@ 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, AGG_STOP_LOCAL_REQUEST); + ieee80211_sta_tear_down_BA_sessions(sta, true); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } @@ -1872,7 +1864,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/trunk/net/wireless/ap.c b/trunk/net/wireless/ap.c index a4a14e8f55cc..324e8d851dc4 100644 --- a/trunk/net/wireless/ap.c +++ b/trunk/net/wireless/ap.c @@ -46,65 +46,3 @@ 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/trunk/net/wireless/chan.c b/trunk/net/wireless/chan.c index 396373f3ec26..a7990bb16529 100644 --- a/trunk/net/wireless/chan.c +++ b/trunk/net/wireless/chan.c @@ -76,10 +76,6 @@ 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/trunk/net/wireless/core.c b/trunk/net/wireless/core.c index 747dd9365a44..14d990400354 100644 --- a/trunk/net/wireless/core.c +++ b/trunk/net/wireless/core.c @@ -57,6 +57,9 @@ 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) { @@ -71,8 +74,10 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - + struct cfg80211_registered_device *rdev; + if (!wiphy) + return WIPHY_IDX_STALE; + rdev = wiphy_to_dev(wiphy); return rdev->wiphy_idx; } @@ -81,6 +86,9 @@ 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); @@ -301,7 +309,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy_idx = wiphy_counter++; - if (unlikely(rdev->wiphy_idx < 0)) { + if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ diff --git a/trunk/net/wireless/core.h b/trunk/net/wireless/core.h index f342267e3620..3563097169cb 100644 --- a/trunk/net/wireless/core.h +++ b/trunk/net/wireless/core.h @@ -18,9 +18,6 @@ #include #include "reg.h" - -#define WIPHY_IDX_INVALID -1 - struct cfg80211_registered_device { const struct cfg80211_ops *ops; struct list_head list; @@ -89,7 +86,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 __aligned(NETDEV_ALIGN); + struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); }; static inline @@ -99,6 +96,13 @@ 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) { @@ -122,6 +126,12 @@ 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/trunk/net/wireless/mlme.c b/trunk/net/wireless/mlme.c index 461e692cdfec..5e8123ee63fd 100644 --- a/trunk/net/wireless/mlme.c +++ b/trunk/net/wireless/mlme.c @@ -987,3 +987,65 @@ 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/trunk/net/wireless/nl80211.c b/trunk/net/wireless/nl80211.c index 9bd8340af999..f45706adaf34 100644 --- a/trunk/net/wireless/nl80211.c +++ b/trunk/net/wireless/nl80211.c @@ -3188,9 +3188,13 @@ 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] || - info->attrs[NL80211_ATTR_HT_CAPABILITY]) - return -EINVAL; + 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 (!rdev->ops->change_station) return -EOPNOTSUPP; @@ -3227,25 +3231,11 @@ 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)) @@ -3265,6 +3255,10 @@ 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; @@ -3273,7 +3267,9 @@ 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.supported_rates) + if (params.ht_capa) + return -EINVAL; + if (params.listen_interval >= 0) return -EINVAL; /* * No special handling for TDLS here -- the userspace @@ -3397,31 +3393,17 @@ 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; @@ -3805,8 +3787,12 @@ 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. */ - if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) + mutex_lock(&cfg80211_mutex); + if (unlikely(!cfg80211_regdomain)) { + mutex_unlock(&cfg80211_mutex); return -EINPROGRESS; + } + mutex_unlock(&cfg80211_mutex); if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; @@ -4166,7 +4152,6 @@ 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; @@ -4189,36 +4174,35 @@ 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_rcu; + goto nla_put_failure; - for (i = 0; i < regdom->n_reg_rules; i++) { + for (i = 0; i < cfg80211_regdomain->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 = ®dom->reg_rules[i]; + reg_rule = &cfg80211_regdomain->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; nl_reg_rule = nla_nest_start(msg, i); if (!nl_reg_rule) - goto nla_put_failure_rcu; + goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, reg_rule->flags) || @@ -4232,11 +4216,10 @@ 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_rcu; + goto nla_put_failure; nla_nest_end(msg, nl_reg_rule); } - rcu_read_unlock(); nla_nest_end(msg, nl_reg_rules); @@ -4244,8 +4227,6 @@ 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: @@ -4278,18 +4259,27 @@ 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) - return -ENOMEM; + if (!rd) { + r = -ENOMEM; + goto bad_reg; + } rd->n_reg_rules = num_rules; rd->alpha2[0] = alpha2[0]; @@ -4303,10 +4293,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; @@ -4319,14 +4309,16 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } - mutex_lock(&cfg80211_mutex); + BUG_ON(rule_idx != num_rules); 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; } @@ -8059,7 +8051,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) goto nla_put_failure; } - if (request->wiphy_idx != WIPHY_IDX_INVALID && + if (wiphy_idx_valid(request->wiphy_idx) && nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; diff --git a/trunk/net/wireless/reg.c b/trunk/net/wireless/reg.c index 0b35de001937..6e5308998e30 100644 --- a/trunk/net/wireless/reg.c +++ b/trunk/net/wireless/reg.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -65,13 +66,6 @@ #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', @@ -82,8 +76,7 @@ static struct regulatory_request core_request_world = { }; /* Receipt of information from last regulatory request */ -static struct regulatory_request __rcu *last_request = - (void __rcu *)&core_request_world; +static struct regulatory_request *last_request = &core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; @@ -95,16 +88,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 __rcu *cfg80211_regdomain; +const struct ieee80211_regdomain *cfg80211_regdomain; /* * Protects static reg.c components: - * - cfg80211_regdomain (if not used with RCU) - * - cfg80211_world_regdom - * - last_request (if not used with RCU) - * - reg_num_devs_support_basehint + * - cfg80211_world_regdom + * - cfg80211_regdom + * - last_request + * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); @@ -119,31 +112,6 @@ static inline void assert_reg_lock(void) lockdep_assert_held(®_mutex); } -static const struct ieee80211_regdomain *get_cfg80211_regdom(void) -{ - return rcu_dereference_protected(cfg80211_regdomain, - lockdep_is_held(®_mutex)); -} - -static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) -{ - return rcu_dereference_protected(wiphy->regd, - lockdep_is_held(®_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(®_mutex)); -} - /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -209,37 +177,28 @@ 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, - const struct ieee80211_regdomain *new_regdom) +static void reset_regdomains(bool full_reset) { - 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 (r == cfg80211_world_regdom) - r = NULL; + if (cfg80211_regdomain == cfg80211_world_regdom) + cfg80211_regdomain = NULL; if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; - if (r == &world_regdom) - r = NULL; + if (cfg80211_regdomain == &world_regdom) + cfg80211_regdomain = NULL; - rcu_free_regdom(r); - rcu_free_regdom(cfg80211_world_regdom); + kfree(cfg80211_regdomain); + kfree(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; - rcu_assign_pointer(cfg80211_regdomain, new_regdom); + cfg80211_regdomain = NULL; if (!full_reset) return; - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); - rcu_assign_pointer(last_request, &core_request_world); + if (last_request != &core_request_world) + kfree(last_request); + last_request = &core_request_world; } /* @@ -248,29 +207,30 @@ static void reset_regdomains(bool full_reset, */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { - struct regulatory_request *lr; - - lr = get_last_request(); - - WARN_ON(!lr); + BUG_ON(!last_request); - reset_regdomains(false, rd); + reset_regdomains(false); cfg80211_world_regdom = rd; + cfg80211_regdomain = rd; } bool is_world_regdom(const char *alpha2) { if (!alpha2) return false; - return alpha2[0] == '0' && alpha2[1] == '0'; + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + return false; } static bool is_alpha2_set(const char *alpha2) { if (!alpha2) return false; - return alpha2[0] && alpha2[1]; + if (alpha2[0] != 0 && alpha2[1] != 0) + return true; + return false; } static bool is_unknown_alpha2(const char *alpha2) @@ -281,7 +241,9 @@ static bool is_unknown_alpha2(const char *alpha2) * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ - return alpha2[0] == '9' && alpha2[1] == '9'; + if (alpha2[0] == '9' && alpha2[1] == '9') + return true; + return false; } static bool is_intersected_alpha2(const char *alpha2) @@ -293,30 +255,39 @@ static bool is_intersected_alpha2(const char *alpha2) * result of an intersection between two regulatory domain * structures */ - return alpha2[0] == '9' && alpha2[1] == '8'; + if (alpha2[0] == '9' && alpha2[1] == '8') + return true; + return false; } static bool is_an_alpha2(const char *alpha2) { if (!alpha2) return false; - return isalpha(alpha2[0]) && isalpha(alpha2[1]); + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + return false; } static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) { if (!alpha2_x || !alpha2_y) return false; - return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; + if (alpha2_x[0] == alpha2_y[0] && + alpha2_x[1] == alpha2_y[1]) + return true; + return false; } static bool regdom_changes(const char *alpha2) { - const struct ieee80211_regdomain *r = get_cfg80211_regdom(); + assert_cfg80211_lock(); - if (!r) + if (!cfg80211_regdomain) return true; - return !alpha2_equal(r->alpha2, alpha2); + if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) + return false; + return true; } /* @@ -330,36 +301,38 @@ 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 const struct ieee80211_regdomain * -reg_copy_regd(const struct ieee80211_regdomain *src_regd) +static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, + const struct ieee80211_regdomain *src_regd) { struct ieee80211_regdomain *regd; - int size_of_regd; + int size_of_regd = 0; unsigned int i; - size_of_regd = - sizeof(struct ieee80211_regdomain) + - src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); + size_of_regd = sizeof(struct ieee80211_regdomain) + + ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) - return ERR_PTR(-ENOMEM); + return -ENOMEM; memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); for (i = 0; i < src_regd->n_reg_rules; i++) memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], - sizeof(struct ieee80211_reg_rule)); + sizeof(struct ieee80211_reg_rule)); - return regd; + *dst_regd = regd; + return 0; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB @@ -374,8 +347,9 @@ 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 = NULL; - int i; + const struct ieee80211_regdomain *curdom, *regdom; + int i, r; + bool set_reg = false; mutex_lock(&cfg80211_mutex); @@ -386,11 +360,14 @@ 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; ialpha2, curdom->alpha2)) { - regdom = reg_copy_regd(curdom); + if (!memcmp(request->alpha2, curdom->alpha2, 2)) { + r = reg_copy_regd(®dom, curdom); + if (r) + break; + set_reg = true; break; } } @@ -399,7 +376,7 @@ static void reg_regdb_search(struct work_struct *work) } mutex_unlock(®_regdb_search_mutex); - if (!IS_ERR_OR_NULL(regdom)) + if (set_reg) set_regdom(regdom); mutex_unlock(&cfg80211_mutex); @@ -457,14 +434,15 @@ static int call_crda(const char *alpha2) return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } -static bool reg_is_valid_request(const char *alpha2) +/* Used by nl80211 before kmalloc'ing our regulatory domain */ +bool reg_is_valid_request(const char *alpha2) { - struct regulatory_request *lr = get_last_request(); + assert_cfg80211_lock(); - if (!lr || lr->processed) + if (!last_request) return false; - return alpha2_equal(lr->alpha2, alpha2); + return alpha2_equal(last_request->alpha2, alpha2); } /* Sanity check on a regulatory rule */ @@ -482,7 +460,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; @@ -509,7 +487,8 @@ 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; @@ -539,7 +518,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 /* @@ -561,9 +540,10 @@ 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; @@ -580,11 +560,11 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, 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) @@ -595,7 +575,7 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, 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; @@ -616,9 +596,9 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, * 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; @@ -627,7 +607,12 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, struct ieee80211_reg_rule *intersected_rule; struct ieee80211_regdomain *rd; /* This is just a dummy holder to help us count */ - struct ieee80211_reg_rule dummy_rule; + struct ieee80211_reg_rule irule; + + /* Uses the stack temporarily for counter arithmetic */ + intersected_rule = &irule; + + memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); if (!rd1 || !rd2) return NULL; @@ -644,8 +629,11 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, 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, &dummy_rule)) + if (!reg_rules_intersect(rule1, rule2, + intersected_rule)) num_rules++; + memset(intersected_rule, 0, + sizeof(struct ieee80211_reg_rule)); } } @@ -653,15 +641,15 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + - num_rules * sizeof(struct ieee80211_reg_rule); + ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) return NULL; - for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { + for (x = 0; x < rd1->n_reg_rules; x++) { rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { + for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; /* * This time around instead of using the stack lets @@ -669,7 +657,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, * 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 @@ -710,16 +699,34 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -static const struct ieee80211_reg_rule * -freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_regdomain *regd) +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) { 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 ERR_PTR(-EINVAL); + return -EINVAL; for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; @@ -736,36 +743,33 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); + bw_fits = reg_does_bw_fit(fr, + center_freq, + desired_bw_khz); - if (band_rule_found && bw_fits) - return rr; + if (band_rule_found && bw_fits) { + *reg_rule = rr; + return 0; + } } if (!band_rule_found) - return ERR_PTR(-ERANGE); + return -ERANGE; - return ERR_PTR(-EINVAL); + return -EINVAL; } -const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, - u32 center_freq) +int freq_reg_info(struct wiphy *wiphy, + u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule) { - 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); + assert_cfg80211_lock(); + return freq_reg_info_regd(wiphy, + center_freq, + desired_bw_khz, + reg_rule, + NULL); } EXPORT_SYMBOL(freq_reg_info); @@ -788,6 +792,7 @@ 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; @@ -802,16 +807,21 @@ 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 with regulatory rule:\n", - chan->center_freq); + 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("%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; @@ -821,25 +831,43 @@ 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). + * 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. */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, - struct ieee80211_channel *chan) + enum ieee80211_band band, + unsigned int chan_idx) { + 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(); - request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + 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]; flags = chan->orig_flags; - reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); - if (IS_ERR(reg_rule)) { + r = freq_reg_info(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule); + + if (r) { /* * We will disable all channels that do not match our * received regulatory rule unless the hint is coming @@ -851,7 +879,7 @@ static void handle_channel(struct wiphy *wiphy, * while 5 GHz is still supported. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - PTR_ERR(reg_rule) == -ERANGE) + r == -ERANGE) return; REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); @@ -859,7 +887,7 @@ static void handle_channel(struct wiphy *wiphy, return; } - chan_reg_rule_print_dbg(chan, reg_rule); + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -867,7 +895,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 (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { /* @@ -886,9 +914,8 @@ 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_t(int, chan->orig_mag, - MBI_TO_DBI(power_rule->max_antenna_gain)); + chan->max_antenna_gain = min(chan->orig_mag, + (int) MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); if (chan->orig_mpwr) { /* @@ -908,65 +935,68 @@ static void handle_channel(struct wiphy *wiphy, } static void handle_band(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator, - struct ieee80211_supported_band *sband) + enum ieee80211_band band, + enum nl80211_reg_initiator initiator) { unsigned int i; + struct ieee80211_supported_band *sband; - if (!sband) - return; + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, initiator, &sband->channels[i]); + handle_channel(wiphy, initiator, band, i); } static bool reg_request_cell_base(struct regulatory_request *request) { if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; - return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; + if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) + return false; + return true; } bool reg_last_request_cell_base(void) { bool val; + assert_cfg80211_lock(); mutex_lock(®_mutex); - val = reg_request_cell_base(get_last_request()); + val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); - return val; } #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS + /* Core specific check */ -static enum reg_request_treatment -reg_ignore_cell_hint(struct regulatory_request *pending_request) +static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { - struct regulatory_request *lr = get_last_request(); - if (!reg_num_devs_support_basehint) - return REG_REQ_IGNORE; + return -EOPNOTSUPP; - if (reg_request_cell_base(lr) && - !regdom_changes(pending_request->alpha2)) - return REG_REQ_ALREADY_SET; - - return REG_REQ_OK; + if (reg_request_cell_base(last_request)) { + if (!regdom_changes(pending_request->alpha2)) + return -EALREADY; + return 0; + } + return 0; } /* Device specific check */ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { - return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); + if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) + return true; + return false; } #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { - return REG_REQ_IGNORE; + return -EOPNOTSUPP; } - -static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) +static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) { return true; } @@ -976,17 +1006,18 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { - struct regulatory_request *lr = get_last_request(); - - if (!lr) { - REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", + if (!last_request) { + 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; } @@ -997,35 +1028,22 @@ 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(lr->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", + !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", reg_initiator_name(initiator)); return true; } - if (reg_request_cell_base(lr)) + if (reg_request_cell_base(last_request)) return reg_dev_ignore_cell_hint(wiphy); return false; } -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, +static void handle_reg_beacon(struct wiphy *wiphy, + unsigned int chan_idx, struct reg_beacon *reg_beacon) { struct ieee80211_supported_band *sband; @@ -1033,6 +1051,8 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, bool channel_changed = false; struct ieee80211_channel chan_before; + assert_cfg80211_lock(); + sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; @@ -1044,9 +1064,6 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, chan->beacon_found = true; - if (!reg_is_world_roaming(wiphy)) - return; - if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) return; @@ -1077,6 +1094,8 @@ 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; @@ -1095,6 +1114,11 @@ 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(®_beacon_list)) + return; + list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; @@ -1104,6 +1128,18 @@ 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) { @@ -1113,27 +1149,39 @@ 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_allowed(struct ieee80211_channel *chan) +static bool is_ht40_not_allowed(struct ieee80211_channel *chan) { if (!chan) - return false; + return true; if (chan->flags & IEEE80211_CHAN_DISABLED) - return false; + return true; /* This would happen when regulatory rules disallow HT40 completely */ - return !(chan->flags & IEEE80211_CHAN_NO_HT40); + if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) + return true; + return false; } static void reg_process_ht_flags_channel(struct wiphy *wiphy, - struct ieee80211_channel *channel) + enum ieee80211_band band, + unsigned int chan_idx) { - struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channel; struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; - if (!is_ht40_allowed(channel)) { + 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)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; } @@ -1144,7 +1192,6 @@ 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)) @@ -1156,27 +1203,28 @@ 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_allowed(channel_before)) + if (is_ht40_not_allowed(channel_before)) channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - if (!is_ht40_allowed(channel_after)) + if (is_ht40_not_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, - struct ieee80211_supported_band *sband) + enum ieee80211_band band) { unsigned int i; + struct ieee80211_supported_band *sband; - if (!sband) - return; + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) - reg_process_ht_flags_channel(wiphy, &sband->channels[i]); + reg_process_ht_flags_channel(wiphy, band, i); } static void reg_process_ht_flags(struct wiphy *wiphy) @@ -1186,29 +1234,34 @@ static void reg_process_ht_flags(struct wiphy *wiphy) if (!wiphy) return; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - reg_process_ht_flags_band(wiphy, wiphy->bands[band]); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + reg_process_ht_flags_band(wiphy, band); + } + } static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; - struct regulatory_request *lr = get_last_request(); + + assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) return; - lr->dfs_region = get_cfg80211_regdom()->dfs_region; + last_request->dfs_region = cfg80211_regdomain->dfs_region; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - handle_band(wiphy, initiator, wiphy->bands[band]); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + handle_band(wiphy, band, initiator); + } reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); - if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, lr); + wiphy->reg_notifier(wiphy, last_request); } static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) @@ -1216,8 +1269,6 @@ 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); @@ -1229,30 +1280,47 @@ 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, get_last_request()); + wiphy->reg_notifier(wiphy, last_request); } } static void handle_channel_custom(struct wiphy *wiphy, - struct ieee80211_channel *chan, + enum ieee80211_band band, + unsigned int chan_idx, 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(); - reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - regd); + sband = wiphy->bands[band]; + BUG_ON(chan_idx >= sband->n_channels); + chan = &sband->channels[chan_idx]; - 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); + r = freq_reg_info_regd(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_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)); chan->flags = IEEE80211_CHAN_DISABLED; return; } - chan_reg_rule_print_dbg(chan, reg_rule); + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -1266,17 +1334,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, - struct ieee80211_supported_band *sband, +static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, const struct ieee80211_regdomain *regd) { unsigned int i; + struct ieee80211_supported_band *sband; - if (!sband) - return; + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, &sband->channels[i], regd); + handle_channel_custom(wiphy, band, i, regd); } /* Used by drivers prior to wiphy registration */ @@ -1286,50 +1354,60 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; + mutex_lock(®_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; - handle_band_custom(wiphy, wiphy->bands[band], regd); + handle_band_custom(wiphy, band, regd); bands_set++; } + mutex_unlock(®_mutex); /* * no point in calling this if it won't have any effect - * on your device's supported bands. + * on your device's supportd 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 enum reg_request_treatment -get_reg_request_treatment(struct wiphy *wiphy, +static int ignore_request(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; - struct regulatory_request *lr = get_last_request(); + + assert_cfg80211_lock(); /* All initial requests are respected */ - if (!lr) - return REG_REQ_OK; + if (!last_request) + return 0; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: - return REG_REQ_OK; + return 0; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (reg_request_cell_base(lr)) { + + if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_ALREADY_SET; + return -EOPNOTSUPP; + return -EALREADY; } - last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; - if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different @@ -1338,23 +1416,23 @@ get_reg_request_treatment(struct wiphy *wiphy, * to be correct. Reject second one for now. */ if (regdom_changes(pending_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_ALREADY_SET; + return -EOPNOTSUPP; + return -EALREADY; } /* * 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 REG_REQ_OK; - return REG_REQ_ALREADY_SET; + return 0; + return -EALREADY; } return 0; case NL80211_REGDOM_SET_BY_DRIVER: - if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) - return REG_REQ_OK; - return REG_REQ_ALREADY_SET; + return 0; + return -EALREADY; } /* @@ -1362,59 +1440,59 @@ get_reg_request_treatment(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 (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) - return REG_REQ_ALREADY_SET; + return -EALREADY; - return REG_REQ_INTERSECT; + return REG_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(lr)) - return REG_REQ_IGNORE; + if (reg_request_cell_base(last_request)) + return -EOPNOTSUPP; - if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - return REG_REQ_INTERSECT; + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + return REG_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ - if (lr->initiator == NL80211_REGDOM_SET_BY_USER && - lr->intersect) - return REG_REQ_IGNORE; + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && + last_request->intersect) + return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ - 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 (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 (!regdom_changes(pending_request->alpha2)) - return REG_REQ_ALREADY_SET; + return -EALREADY; - return REG_REQ_OK; + return 0; } - return REG_REQ_IGNORE; + return -EINVAL; } static void reg_set_request_processed(void) { bool need_more_processing = false; - struct regulatory_request *lr = get_last_request(); - lr->processed = true; + last_request->processed = true; spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); - if (lr->initiator == NL80211_REGDOM_SET_BY_USER) + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) cancel_delayed_work(®_timeout); if (need_more_processing) @@ -1430,122 +1508,116 @@ 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 one of the different reg request treatment values. + * Returns zero if all went fine, %-EALREADY if a regulatory domain had + * already been set or other standard error codes. * - * Caller must hold ®_mutex + * Caller must hold &cfg80211_mutex and ®_mutex */ -static enum reg_request_treatment -__regulatory_hint(struct wiphy *wiphy, - struct regulatory_request *pending_request) +static int __regulatory_hint(struct wiphy *wiphy, + struct regulatory_request *pending_request) { - const struct ieee80211_regdomain *regd; bool intersect = false; - enum reg_request_treatment treatment; - struct regulatory_request *lr; + int r = 0; + + assert_cfg80211_lock(); - treatment = get_reg_request_treatment(wiphy, pending_request); + r = ignore_request(wiphy, pending_request); - switch (treatment) { - case REG_REQ_INTERSECT: + if (r == REG_INTERSECT) { if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - regd = reg_copy_regd(get_cfg80211_regdom()); - if (IS_ERR(regd)) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) { kfree(pending_request); - return PTR_ERR(regd); + return r; } - rcu_assign_pointer(wiphy->regd, regd); } intersect = true; - break; - case REG_REQ_OK: - break; - default: + } else if (r) { /* * If the regulatory domain being requested by the * driver has already been set just copy it to the * wiphy */ - 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)) { + if (r == -EALREADY && + pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) { kfree(pending_request); - return REG_REQ_IGNORE; + return r; } - treatment = REG_REQ_ALREADY_SET; - rcu_assign_pointer(wiphy->regd, regd); + r = -EALREADY; goto new_request; } kfree(pending_request); - return treatment; + return r; } new_request: - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); + if (last_request != &core_request_world) + kfree(last_request); - pending_request->intersect = intersect; - pending_request->processed = false; - rcu_assign_pointer(last_request, pending_request); - lr = pending_request; + last_request = pending_request; + last_request->intersect = intersect; pending_request = NULL; - if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = lr->alpha2[0]; - user_alpha2[1] = lr->alpha2[1]; + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { + user_alpha2[0] = last_request->alpha2[0]; + user_alpha2[1] = last_request->alpha2[1]; } - /* When r == REG_REQ_INTERSECT we do need to call CRDA */ - if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { + /* When r == REG_INTERSECT we do need to call CRDA */ + if (r < 0) { /* * 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 (treatment == REG_REQ_ALREADY_SET) { - nl80211_send_reg_change_event(lr); + if (r == -EALREADY) { + nl80211_send_reg_change_event(last_request); reg_set_request_processed(); } - return treatment; + return r; } - if (call_crda(lr->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_OK; + return call_crda(last_request->alpha2); } /* 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; - if (WARN_ON(!reg_request->alpha2)) - return; + BUG_ON(!reg_request->alpha2); - if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) + if (wiphy_idx_valid(reg_request->wiphy_idx)) 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; } - 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(®_timeout, - msecs_to_jiffies(3142)); - break; + 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; } + + /* + * 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(®_timeout, msecs_to_jiffies(3142)); } /* @@ -1555,15 +1627,15 @@ static void reg_process_hint(struct regulatory_request *reg_request, */ static void reg_process_pending_hints(void) { - struct regulatory_request *reg_request, *lr; + struct regulatory_request *reg_request; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - lr = get_last_request(); /* When last_request->processed becomes true this will be rescheduled */ - if (lr && !lr->processed) { - REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); + if (last_request && !last_request->processed) { + REG_DBG_PRINT("Pending regulatory request, waiting " + "for it to be processed...\n"); goto out; } @@ -1594,14 +1666,23 @@ 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(®_mutex); /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); + if (list_empty(®_pending_beacons)) { + spin_unlock_bh(®_pending_beacons_lock); + goto out; + } + list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { + list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ @@ -1613,7 +1694,7 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); - mutex_unlock(®_mutex); +out: mutex_unlock(&cfg80211_mutex); } @@ -1625,8 +1706,10 @@ static void reg_todo(struct work_struct *work) static void queue_regulatory_request(struct regulatory_request *request) { - request->alpha2[0] = toupper(request->alpha2[0]); - request->alpha2[1] = toupper(request->alpha2[1]); + if (isalpha(request->alpha2[0])) + request->alpha2[0] = toupper(request->alpha2[0]); + if (isalpha(request->alpha2[1])) + request->alpha2[1] = toupper(request->alpha2[1]); spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); @@ -1643,7 +1726,8 @@ 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; @@ -1662,14 +1746,13 @@ int regulatory_hint_user(const char *alpha2, { struct regulatory_request *request; - if (WARN_ON(!alpha2)) - return -EINVAL; + BUG_ON(!alpha2); request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; - request->wiphy_idx = WIPHY_IDX_INVALID; + request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_USER; @@ -1685,8 +1768,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; - if (WARN_ON(!alpha2 || !wiphy)) - return -EINVAL; + BUG_ON(!alpha2); + BUG_ON(!wiphy); request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -1694,6 +1777,9 @@ 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; @@ -1708,17 +1794,18 @@ 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, *lr; + struct regulatory_request *request; mutex_lock(®_mutex); - lr = get_last_request(); - if (unlikely(!lr)) + if (unlikely(!last_request)) goto out; /* IE len must be evenly divisible by 2 */ @@ -1741,8 +1828,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ - if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - lr->wiphy_idx != WIPHY_IDX_INVALID) + if (likely(last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -1755,7 +1843,12 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_env = env; + mutex_unlock(®_mutex); + queue_regulatory_request(request); + + return; + out: mutex_unlock(®_mutex); } @@ -1770,7 +1863,8 @@ 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'; @@ -1780,20 +1874,26 @@ 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 @@ -1848,7 +1948,7 @@ static void restore_regulatory_settings(bool reset_user) mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true, cfg80211_world_regdom); + reset_regdomains(true); restore_alpha2(alpha2, reset_user); /* @@ -1858,35 +1958,49 @@ static void restore_regulatory_settings(bool reset_user) * settings. */ spin_lock(®_requests_lock); - list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { - if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) - continue; - list_move_tail(®_request->list, &tmp_reg_req_list); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + if (reg_request->initiator != + NL80211_REGDOM_SET_BY_USER) + continue; + list_move_tail(®_request->list, &tmp_reg_req_list); + } } spin_unlock(®_requests_lock); /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); - list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); + if (!list_empty(®_pending_beacons)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } } spin_unlock_bh(®_pending_beacons_lock); - list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); + if (!list_empty(®_beacon_list)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } } /* First restore to the basic regulatory settings */ - world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; - world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; + cfg80211_regdomain = cfg80211_world_regdom; + world_alpha2[0] = cfg80211_regdomain->alpha2[0]; + world_alpha2[1] = cfg80211_regdomain->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(®_mutex); + mutex_unlock(&cfg80211_mutex); + regulatory_hint_core(world_alpha2); /* @@ -1897,8 +2011,20 @@ 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(®_mutex); + spin_lock(®_requests_lock); - list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); + 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(®_request->list, ®_requests_list); + } spin_unlock(®_requests_lock); mutex_unlock(®_mutex); @@ -1911,7 +2037,8 @@ 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); } @@ -1924,48 +2051,31 @@ 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, ®_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 (beacon_chan->beacon_found || - beacon_chan->flags & IEEE80211_CHAN_RADAR || + if (likely((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))) - return 0; - - spin_lock_bh(®_pending_beacons_lock); - processing = pending_reg_beacon(beacon_chan); - spin_unlock_bh(®_pending_beacons_lock); - - if (processing) + !freq_is_chan_12_13_14(beacon_chan->center_freq))))) 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(®_beacon->chan, beacon_chan, - sizeof(struct ieee80211_channel)); + sizeof(struct ieee80211_channel)); + /* * Since we can be called from BH or and non-BH context @@ -2045,19 +2155,21 @@ static void print_dfs_region(u8 dfs_region) pr_info(" DFS Master region JP"); break; default: - pr_info(" DFS Master region Unknown"); + pr_info(" DFS Master region Uknown"); break; } } static void print_regdomain(const struct ieee80211_regdomain *rd) { - struct regulatory_request *lr = get_last_request(); if (is_intersected_alpha2(rd->alpha2)) { - if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; - rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); + rdev = cfg80211_rdev_by_wiphy_idx( + last_request->wiphy_idx); if (rdev) { pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], @@ -2066,21 +2178,22 @@ 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(lr)) - pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", + if (reg_request_cell_base(last_request)) + 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); } @@ -2094,23 +2207,22 @@ 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)) + !is_unknown_alpha2(rd->alpha2)) + return -EINVAL; + + if (!last_request) return -EINVAL; /* @@ -2118,7 +2230,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 (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->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 @@ -2134,23 +2246,29 @@ 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(lr->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy && - (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || - lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { + (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { schedule_delayed_work(®_timeout, 0); return -ENODEV; } - if (!lr->intersect) { - if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { - reset_regdomains(false, rd); + if (!last_request->intersect) { + int r; + + if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { + reset_regdomains(false); + cfg80211_regdomain = rd; return 0; } @@ -2166,19 +2284,20 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (request_wiphy->regd) return -EALREADY; - regd = reg_copy_regd(rd); - if (IS_ERR(regd)) - return PTR_ERR(regd); + r = reg_copy_regd(&request_wiphy->regd, rd); + if (r) + return r; - rcu_assign_pointer(request_wiphy->regd, regd); - reset_regdomains(false, rd); + reset_regdomains(false); + cfg80211_regdomain = rd; return 0; } /* Intersection requires a bit more work */ - if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + + intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) return -EINVAL; @@ -2187,14 +2306,15 @@ 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 (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) - rcu_assign_pointer(request_wiphy->regd, rd); + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) + request_wiphy->regd = rd; else kfree(rd); rd = NULL; - reset_regdomains(false, intersected_rd); + reset_regdomains(false); + cfg80211_regdomain = intersected_rd; return 0; } @@ -2206,15 +2326,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. + * kmalloc'd the rd structure. Caller must hold cfg80211_mutex */ int set_regdom(const struct ieee80211_regdomain *rd) { - struct regulatory_request *lr; int r; + assert_cfg80211_lock(); + mutex_lock(®_mutex); - lr = get_last_request(); /* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); @@ -2223,25 +2343,23 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); kfree(rd); - goto out; + mutex_unlock(®_mutex); + return r; } /* This would make this whole thing pointless */ - if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { - r = -EINVAL; - goto out; - } + if (!last_request->intersect) + BUG_ON(rd != cfg80211_regdomain); /* update all wiphys now with the new established regulatory domain */ - update_all_wiphy_regulatory(lr->initiator); + update_all_wiphy_regulatory(last_request->initiator); - print_regdomain(get_cfg80211_regdom()); + print_regdomain(cfg80211_regdomain); - nl80211_send_reg_change_event(lr); + nl80211_send_reg_change_event(last_request); reg_set_request_processed(); - out: mutex_unlock(®_mutex); return r; @@ -2250,11 +2368,10 @@ int set_regdom(const struct ieee80211_regdomain *rd) #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct regulatory_request *lr = get_last_request(); - - if (lr && !lr->processed) { + if (last_request && !last_request->processed) { if (add_uevent_var(env, "COUNTRY=%c%c", - lr->alpha2[0], lr->alpha2[1])) + last_request->alpha2[0], + last_request->alpha2[1])) return -ENOMEM; } @@ -2269,6 +2386,8 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) void wiphy_regulatory_register(struct wiphy *wiphy) { + assert_cfg80211_lock(); + mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) @@ -2283,32 +2402,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy) void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; - struct regulatory_request *lr; + + assert_cfg80211_lock(); mutex_lock(®_mutex); - lr = get_last_request(); if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; - rcu_free_regdom(get_wiphy_regdom(wiphy)); - rcu_assign_pointer(wiphy->regd, NULL); + kfree(wiphy->regd); - if (lr) - request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + if (last_request) + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) goto out; - lr->wiphy_idx = WIPHY_IDX_INVALID; - lr->country_ie_env = ENVIRON_ANY; + last_request->wiphy_idx = WIPHY_IDX_STALE; + last_request->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_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); } @@ -2327,13 +2446,13 @@ int __init regulatory_init(void) reg_regdb_size_check(); - rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); + 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_world_regdom->alpha2); + err = regulatory_hint_core(cfg80211_regdomain->alpha2); if (err) { if (err == -ENOMEM) return err; @@ -2345,6 +2464,10 @@ 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 } /* @@ -2358,7 +2481,7 @@ int __init regulatory_init(void) return 0; } -void regulatory_exit(void) +void /* __init_or_exit */ regulatory_exit(void) { struct regulatory_request *reg_request, *tmp; struct reg_beacon *reg_beacon, *btmp; @@ -2366,27 +2489,43 @@ void regulatory_exit(void) cancel_work_sync(®_work); cancel_delayed_work_sync(®_timeout); - /* Lock to suppress warnings */ + mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true, NULL); - mutex_unlock(®_mutex); + + reset_regdomains(true); dev_set_uevent_suppress(®_pdev->dev, true); platform_device_unregister(reg_pdev); - list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); + spin_lock_bh(®_pending_beacons_lock); + if (!list_empty(®_pending_beacons)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } } + spin_unlock_bh(®_pending_beacons_lock); - list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); + if (!list_empty(®_beacon_list)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } } - list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { - list_del(®_request->list); - kfree(reg_request); + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + list_del(®_request->list); + kfree(reg_request); + } } + spin_unlock(®_requests_lock); + + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); } diff --git a/trunk/net/wireless/reg.h b/trunk/net/wireless/reg.h index af2d5f8a5d82..4c0a32ffd530 100644 --- a/trunk/net/wireless/reg.h +++ b/trunk/net/wireless/reg.h @@ -16,9 +16,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; +extern const struct ieee80211_regdomain *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, @@ -54,8 +55,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/trunk/net/wireless/sme.c b/trunk/net/wireless/sme.c index d2d26518cdd7..f2431e41a373 100644 --- a/trunk/net/wireless/sme.c +++ b/trunk/net/wireless/sme.c @@ -519,8 +519,10 @@ 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); }