summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2013-03-08 15:52:21 -0500
committerJohn W. Linville <linville@tuxdriver.com>2013-03-08 15:52:21 -0500
commit3d5c203272b25c4391397247cdb0059a04fccddf (patch)
tree1cf51dc656c10fd7ea0c84c86afea1d73f3f76bc
parent7da060c1c01b103d181dba39bce9bd141a945f99 (diff)
parent87f59c70ce6d1abeaaf97594835be29f746b81a0 (diff)
downloadop-kernel-dev-3d5c203272b25c4391397247cdb0059a04fccddf.zip
op-kernel-dev-3d5c203272b25c4391397247cdb0059a04fccddf.tar.gz
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c8
-rw-r--r--drivers/net/wireless/iwlegacy/3945.h4
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c13
-rw-r--r--drivers/net/wireless/iwlegacy/common.h4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tx.c11
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c2
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c3
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c3
-rw-r--r--include/linux/ieee80211.h52
-rw-r--r--include/net/cfg80211.h126
-rw-r--r--include/net/mac80211.h29
-rw-r--r--include/uapi/linux/nl80211.h117
-rw-r--r--net/mac80211/cfg.c162
-rw-r--r--net/mac80211/debugfs_sta.c31
-rw-r--r--net/mac80211/driver-ops.h7
-rw-r--r--net/mac80211/ht.c52
-rw-r--r--net/mac80211/ibss.c29
-rw-r--r--net/mac80211/ieee80211_i.h26
-rw-r--r--net/mac80211/iface.c22
-rw-r--r--net/mac80211/key.c103
-rw-r--r--net/mac80211/key.h5
-rw-r--r--net/mac80211/main.c55
-rw-r--r--net/mac80211/mesh.c59
-rw-r--r--net/mac80211/mesh.h12
-rw-r--r--net/mac80211/mesh_plink.c37
-rw-r--r--net/mac80211/mlme.c110
-rw-r--r--net/mac80211/offchannel.c2
-rw-r--r--net/mac80211/pm.c117
-rw-r--r--net/mac80211/rc80211_minstrel.c204
-rw-r--r--net/mac80211/rc80211_minstrel.h31
-rw-r--r--net/mac80211/rc80211_minstrel_debugfs.c12
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c79
-rw-r--r--net/mac80211/rc80211_minstrel_ht.h6
-rw-r--r--net/mac80211/rx.c61
-rw-r--r--net/mac80211/sta_info.c11
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/trace.h11
-rw-r--r--net/mac80211/tx.c80
-rw-r--r--net/mac80211/util.c73
-rw-r--r--net/mac80211/vht.c212
-rw-r--r--net/rfkill/rfkill-regulator.c2
-rw-r--r--net/wireless/ap.c62
-rw-r--r--net/wireless/core.c76
-rw-r--r--net/wireless/core.h22
-rw-r--r--net/wireless/mesh.c15
-rw-r--r--net/wireless/mlme.c230
-rw-r--r--net/wireless/nl80211.c1817
-rw-r--r--net/wireless/nl80211.h68
-rw-r--r--net/wireless/rdev-ops.h20
-rw-r--r--net/wireless/reg.c6
-rw-r--r--net/wireless/sme.c26
-rw-r--r--net/wireless/sysfs.c25
-rw-r--r--net/wireless/trace.h46
59 files changed, 2523 insertions, 1897 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 752ffc4..28c413f 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -2990,13 +2990,15 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
+ int err;
if (vif->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
- /* Use this only for authorizing/unauthorizing a station */
- if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
- return -EOPNOTSUPP;
+ err = cfg80211_check_station_change(wiphy, params,
+ CFG80211_STA_AP_MLME_CLIENT);
+ if (err)
+ return err;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/iwlegacy/3945.h
index 1d45075..9a8703d 100644
--- a/drivers/net/wireless/iwlegacy/3945.h
+++ b/drivers/net/wireless/iwlegacy/3945.h
@@ -150,10 +150,6 @@ struct il3945_frame {
struct list_head list;
};
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-
#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index 7941eb3..c092fcb 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -2258,7 +2258,7 @@ il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
spin_lock_irqsave(&il->sta_lock, flags);
tid_data = &il->stations[sta_id].tid[tid];
- *ssn = SEQ_TO_SN(tid_data->seq_number);
+ *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
tid_data->agg.txq_id = txq_id;
il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id);
spin_unlock_irqrestore(&il->sta_lock, flags);
@@ -2408,7 +2408,7 @@ il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id)
/* aggregated HW queue */
if (txq_id == tid_data->agg.txq_id &&
q->read_ptr == q->write_ptr) {
- u16 ssn = SEQ_TO_SN(tid_data->seq_number);
+ u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
int tx_fifo = il4965_get_fifo_from_tid(tid);
D_HT("HW queue empty: continue DELBA flow\n");
il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo);
@@ -2627,7 +2627,8 @@ il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr)
static inline u32
il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp)
{
- return le32_to_cpup(&tx_resp->u.status + tx_resp->frame_count) & MAX_SN;
+ return le32_to_cpup(&tx_resp->u.status +
+ tx_resp->frame_count) & IEEE80211_MAX_SN;
}
static inline u32
@@ -2717,15 +2718,15 @@ il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg,
hdr = (struct ieee80211_hdr *) skb->data;
sc = le16_to_cpu(hdr->seq_ctrl);
- if (idx != (SEQ_TO_SN(sc) & 0xff)) {
+ if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) {
IL_ERR("BUG_ON idx doesn't match seq control"
" idx=%d, seq_idx=%d, seq=%d\n", idx,
- SEQ_TO_SN(sc), hdr->seq_ctrl);
+ IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl);
return -1;
}
D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx,
- SEQ_TO_SN(sc));
+ IEEE80211_SEQ_TO_SN(sc));
sh = idx - start;
if (sh > 64) {
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index 96f2025..73bd3ef 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -541,10 +541,6 @@ struct il_frame {
struct list_head list;
};
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-
enum {
CMD_SYNC = 0,
CMD_SIZE_NORMAL = 0,
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 323e4a3..c7cd2df 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -1137,7 +1137,8 @@ done:
static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
- int duration)
+ int duration,
+ enum ieee80211_roc_type type)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index 6aec2df..d499a03 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -418,7 +418,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
" Tx flags = 0x%08x, agg.state = %d",
info->flags, tid_data->agg.state);
IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d",
- sta_id, tid, SEQ_TO_SN(tid_data->seq_number));
+ sta_id, tid,
+ IEEE80211_SEQ_TO_SN(tid_data->seq_number));
goto drop_unlock_sta;
}
@@ -569,7 +570,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
return 0;
}
- tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
/* There are still packets for this RA / TID in the HW */
if (!test_bit(txq_id, priv->agg_q_alloc)) {
@@ -651,7 +652,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
spin_lock_bh(&priv->sta_lock);
tid_data = &priv->tid_data[sta_id][tid];
- tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
tid_data->agg.txq_id = txq_id;
*ssn = tid_data->agg.ssn;
@@ -911,7 +912,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp)
{
return le32_to_cpup((__le32 *)&tx_resp->status +
- tx_resp->frame_count) & MAX_SN;
+ tx_resp->frame_count) & IEEE80211_MAX_SN;
}
static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
@@ -1148,7 +1149,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
if (tx_resp->frame_count == 1) {
u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
- next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10);
+ next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
if (is_agg) {
/* If this is an aggregation queue, we can rely on the
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 8c7bec6..00bdc5b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -114,9 +114,6 @@
* completely agnostic to these differences.
* The transport does provide helper functionnality (i.e. SYNC / ASYNC mode),
*/
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f)
#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8)
#define SEQ_TO_INDEX(s) ((s) & 0xff)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index e8264e1..23460f4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1081,7 +1081,8 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
static int iwl_mvm_roc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
- int duration)
+ int duration,
+ enum ieee80211_roc_type type)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct cfg80211_chan_def chandef;
@@ -1092,8 +1093,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
return -EINVAL;
}
- IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
- duration);
+ IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+ duration, type);
mutex_lock(&mvm->mutex);
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 861a7f9..52aecf2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -686,7 +686,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
spin_lock_bh(&mvmsta->lock);
tid_data = &mvmsta->tid_data[tid];
- tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
tid_data->txq_id = txq_id;
*ssn = tid_data->ssn;
@@ -779,7 +779,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
switch (tid_data->state) {
case IWL_AGG_ON:
- tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
IWL_DEBUG_TX_QUEUES(mvm,
"ssn = %d, next_recl = %d\n",
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 6b67ce3..56df249 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -641,7 +641,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
next_reclaimed = ssn;
} else {
/* The next packet to be reclaimed is the one after this one */
- next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10);
+ next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10);
}
IWL_DEBUG_TX_REPLY(mvm,
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 8e9e321..ad7441d 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1581,7 +1581,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
* Check here that the packets are in the right place on the ring.
*/
#ifdef CONFIG_IWLWIFI_DEBUG
- wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+ wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) &&
((wifi_seq & 0xff) != q->write_ptr),
"Q: %d WiFi Seq %d tfdNum %d",
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index cffdf4f..7490c4f 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1535,7 +1535,8 @@ static void hw_roc_done(struct work_struct *work)
static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *chan,
- int duration)
+ int duration,
+ enum ieee80211_roc_type type)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index f13258a..c3eff32 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -2127,9 +2127,6 @@ value to host byte ordering.*/
#define WLAN_FC_GET_TYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE)
#define WLAN_FC_GET_STYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE)
#define WLAN_FC_MORE_DATA(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_MOREDATA)
-#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
-#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
#define RT_RF_OFF_LEVL_ASPM BIT(0) /*PCI ASPM */
#define RT_RF_OFF_LEVL_CLK_REQ BIT(1) /*PCI clock request */
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 2c2ff3e..d7e3063 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -4956,7 +4956,8 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *chan,
- int duration)
+ int duration,
+ enum ieee80211_roc_type type)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271 *wl = hw->priv;
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 7e24fe0..4cf0c9e 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -113,6 +113,34 @@
#define IEEE80211_CTL_EXT_SSW_FBACK 0x9000
#define IEEE80211_CTL_EXT_SSW_ACK 0xa000
+
+#define IEEE80211_SN_MASK ((IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_MAX_SN IEEE80211_SN_MASK
+#define IEEE80211_SN_MODULO (IEEE80211_MAX_SN + 1)
+
+static inline int ieee80211_sn_less(u16 sn1, u16 sn2)
+{
+ return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1);
+}
+
+static inline u16 ieee80211_sn_add(u16 sn1, u16 sn2)
+{
+ return (sn1 + sn2) & IEEE80211_SN_MASK;
+}
+
+static inline u16 ieee80211_sn_inc(u16 sn)
+{
+ return ieee80211_sn_add(sn, 1);
+}
+
+static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
+{
+ return (sn1 - sn2) & IEEE80211_SN_MASK;
+}
+
+#define IEEE80211_SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
+
/* miscellaneous IEEE 802.11 constants */
#define IEEE80211_MAX_FRAG_THRESHOLD 2352
#define IEEE80211_MAX_RTS_THRESHOLD 2353
@@ -185,7 +213,7 @@ struct ieee80211_hdr {
u8 addr3[6];
__le16 seq_ctrl;
u8 addr4[6];
-} __packed;
+} __packed __aligned(2);
struct ieee80211_hdr_3addr {
__le16 frame_control;
@@ -194,7 +222,7 @@ struct ieee80211_hdr_3addr {
u8 addr2[6];
u8 addr3[6];
__le16 seq_ctrl;
-} __packed;
+} __packed __aligned(2);
struct ieee80211_qos_hdr {
__le16 frame_control;
@@ -204,7 +232,7 @@ struct ieee80211_qos_hdr {
u8 addr3[6];
__le16 seq_ctrl;
__le16 qos_ctrl;
-} __packed;
+} __packed __aligned(2);
/**
* ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
@@ -581,7 +609,7 @@ struct ieee80211s_hdr {
__le32 seqnum;
u8 eaddr1[6];
u8 eaddr2[6];
-} __packed;
+} __packed __aligned(2);
/* Mesh flags */
#define MESH_FLAGS_AE_A4 0x1
@@ -875,7 +903,7 @@ struct ieee80211_mgmt {
} u;
} __packed action;
} u;
-} __packed;
+} __packed __aligned(2);
/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -906,20 +934,20 @@ struct ieee80211_rts {
__le16 duration;
u8 ra[6];
u8 ta[6];
-} __packed;
+} __packed __aligned(2);
struct ieee80211_cts {
__le16 frame_control;
__le16 duration;
u8 ra[6];
-} __packed;
+} __packed __aligned(2);
struct ieee80211_pspoll {
__le16 frame_control;
__le16 aid;
u8 bssid[6];
u8 ta[6];
-} __packed;
+} __packed __aligned(2);
/* TDLS */
@@ -1290,11 +1318,6 @@ struct ieee80211_vht_operation {
} __packed;
-#define IEEE80211_VHT_MCS_ZERO_TO_SEVEN_SUPPORT 0
-#define IEEE80211_VHT_MCS_ZERO_TO_EIGHT_SUPPORT 1
-#define IEEE80211_VHT_MCS_ZERO_TO_NINE_SUPPORT 2
-#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3
-
/* 802.11ac VHT Capabilities */
#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000
#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001
@@ -1310,10 +1333,11 @@ struct ieee80211_vht_operation {
#define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200
#define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300
#define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400
+#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000
-#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX 0x00030000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000
#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d581c6d..bdba9b6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -611,22 +611,10 @@ struct cfg80211_ap_settings {
};
/**
- * enum plink_action - actions to perform in mesh peers
- *
- * @PLINK_ACTION_INVALID: action 0 is reserved
- * @PLINK_ACTION_OPEN: start mesh peer link establishment
- * @PLINK_ACTION_BLOCK: block traffic from this mesh peer
- */
-enum plink_actions {
- PLINK_ACTION_INVALID,
- PLINK_ACTION_OPEN,
- PLINK_ACTION_BLOCK,
-};
-
-/**
* enum station_parameters_apply_mask - station parameter values to apply
* @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
* @STATION_PARAM_APPLY_CAPABILITY: apply new capability
+ * @STATION_PARAM_APPLY_PLINK_STATE: apply new plink state
*
* Not all station parameters have in-band "no change" signalling,
* for those that don't these flags will are used.
@@ -634,6 +622,7 @@ enum plink_actions {
enum station_parameters_apply_mask {
STATION_PARAM_APPLY_UAPSD = BIT(0),
STATION_PARAM_APPLY_CAPABILITY = BIT(1),
+ STATION_PARAM_APPLY_PLINK_STATE = BIT(2),
};
/**
@@ -669,7 +658,7 @@ enum station_parameters_apply_mask {
* @ext_capab_len: number of extended capabilities
*/
struct station_parameters {
- u8 *supported_rates;
+ const u8 *supported_rates;
struct net_device *vlan;
u32 sta_flags_mask, sta_flags_set;
u32 sta_modify_mask;
@@ -678,17 +667,60 @@ struct station_parameters {
u8 supported_rates_len;
u8 plink_action;
u8 plink_state;
- struct ieee80211_ht_cap *ht_capa;
- struct ieee80211_vht_cap *vht_capa;
+ const struct ieee80211_ht_cap *ht_capa;
+ const struct ieee80211_vht_cap *vht_capa;
u8 uapsd_queues;
u8 max_sp;
enum nl80211_mesh_power_mode local_pm;
u16 capability;
- u8 *ext_capab;
+ const u8 *ext_capab;
u8 ext_capab_len;
};
/**
+ * enum cfg80211_station_type - the type of station being modified
+ * @CFG80211_STA_AP_CLIENT: client of an AP interface
+ * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
+ * the AP MLME in the device
+ * @CFG80211_STA_AP_STA: AP station on managed interface
+ * @CFG80211_STA_IBSS: IBSS station
+ * @CFG80211_STA_TDLS_PEER_SETUP: TDLS peer on managed interface (dummy entry
+ * while TDLS setup is in progress, it moves out of this state when
+ * being marked authorized; use this only if TDLS with external setup is
+ * supported/used)
+ * @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active
+ * entry that is operating, has been marked authorized by userspace)
+ * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed)
+ * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed)
+ */
+enum cfg80211_station_type {
+ CFG80211_STA_AP_CLIENT,
+ CFG80211_STA_AP_MLME_CLIENT,
+ CFG80211_STA_AP_STA,
+ CFG80211_STA_IBSS,
+ CFG80211_STA_TDLS_PEER_SETUP,
+ CFG80211_STA_TDLS_PEER_ACTIVE,
+ CFG80211_STA_MESH_PEER_KERNEL,
+ CFG80211_STA_MESH_PEER_USER,
+};
+
+/**
+ * cfg80211_check_station_change - validate parameter changes
+ * @wiphy: the wiphy this operates on
+ * @params: the new parameters for a station
+ * @statype: the type of station being modified
+ *
+ * Utility function for the @change_station driver method. Call this function
+ * with the appropriate station type looking up the station (and checking that
+ * it exists). It will verify whether the station change is acceptable, and if
+ * not will return an error code. Note that it may modify the parameters for
+ * backward compatibility reasons, so don't use them before calling this.
+ */
+int cfg80211_check_station_change(struct wiphy *wiphy,
+ struct station_parameters *params,
+ enum cfg80211_station_type statype);
+
+/**
* enum station_info_flags - station information flags
*
* Used by the driver to indicate which info in &struct station_info
@@ -1119,6 +1151,7 @@ struct mesh_config {
* @ie_len: length of vendor information elements
* @is_authenticated: this mesh requires authentication
* @is_secure: this mesh uses security
+ * @user_mpm: userspace handles all MPM functions
* @dtim_period: DTIM period to use
* @beacon_interval: beacon interval to use
* @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
@@ -1136,6 +1169,7 @@ struct mesh_setup {
u8 ie_len;
bool is_authenticated;
bool is_secure;
+ bool user_mpm;
u8 dtim_period;
u16 beacon_interval;
int mcast_rate[IEEE80211_NUM_BANDS];
@@ -1398,9 +1432,11 @@ struct cfg80211_auth_request {
* enum cfg80211_assoc_req_flags - Over-ride default behaviour in association.
*
* @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n)
+ * @ASSOC_REQ_DISABLE_VHT: Disable VHT
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
+ ASSOC_REQ_DISABLE_VHT = BIT(1),
};
/**
@@ -1422,6 +1458,8 @@ enum cfg80211_assoc_req_flags {
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
+ * @vht_capa: VHT capability override
+ * @vht_capa_mask: VHT capability mask indicating which fields to use
*/
struct cfg80211_assoc_request {
struct cfg80211_bss *bss;
@@ -1432,6 +1470,7 @@ struct cfg80211_assoc_request {
u32 flags;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
+ struct ieee80211_vht_cap vht_capa, vht_capa_mask;
};
/**
@@ -1542,6 +1581,8 @@ struct cfg80211_ibss_params {
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
+ * @vht_capa: VHT Capability overrides
+ * @vht_capa_mask: The bits of vht_capa which are to be used.
*/
struct cfg80211_connect_params {
struct ieee80211_channel *channel;
@@ -1560,6 +1601,8 @@ struct cfg80211_connect_params {
int bg_scan_period;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
+ struct ieee80211_vht_cap vht_capa;
+ struct ieee80211_vht_cap vht_capa_mask;
};
/**
@@ -1722,6 +1765,21 @@ struct cfg80211_gtk_rekey_data {
};
/**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+ u16 md;
+ const u8 *ie;
+ size_t ie_len;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -1781,9 +1839,8 @@ struct cfg80211_gtk_rekey_data {
* @change_station: Modify a given station. Note that flags changes are not much
* validated in cfg80211, in particular the auth/assoc/authorized flags
* might come to the driver in invalid combinations -- make sure to check
- * them, also against the existing state! Also, supported_rates changes are
- * not checked in station mode -- drivers need to reject (or ignore) them
- * for anything but TDLS peers.
+ * them, also against the existing state! Drivers must call
+ * cfg80211_check_station_change() to validate the information.
* @get_station: get station information for the station identified by @mac
* @dump_station: dump station callback -- resume dump at index @idx
*
@@ -2168,6 +2225,8 @@ struct cfg80211_ops {
int (*start_radar_detection)(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef);
+ int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie);
};
/*
@@ -2485,6 +2544,8 @@ struct wiphy_wowlan_support {
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
* If null, then none can be over-ridden.
+ * @vht_capa_mod_mask: Specify what VHT capabilities can be over-ridden.
+ * If null, then none can be over-ridden.
*
* @max_acl_mac_addrs: Maximum number of MAC addresses that the device
* supports for ACL.
@@ -2593,6 +2654,7 @@ struct wiphy {
struct dentry *debugfsdir;
const struct ieee80211_ht_cap *ht_capa_mod_mask;
+ const struct ieee80211_vht_cap *vht_capa_mod_mask;
#ifdef CONFIG_NET_NS
/* the network namespace this phy lives in currently */
@@ -4002,6 +4064,30 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
void cfg80211_unregister_wdev(struct wireless_dev *wdev);
/**
+ * struct cfg80211_ft_event - FT Information Elements
+ * @ies: FT IEs
+ * @ies_len: length of the FT IE in bytes
+ * @target_ap: target AP's MAC address
+ * @ric_ies: RIC IE
+ * @ric_ies_len: length of the RIC IE in bytes
+ */
+struct cfg80211_ft_event_params {
+ const u8 *ies;
+ size_t ies_len;
+ const u8 *target_ap;
+ const u8 *ric_ies;
+ size_t ric_ies_len;
+};
+
+/**
+ * cfg80211_ft_event - notify userspace about FT IE and RIC IE
+ * @netdev: network device
+ * @ft_event: IE information
+ */
+void cfg80211_ft_event(struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event);
+
+/**
* cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
* @ies: the input IE buffer
* @len: the input length
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f7eba13..cdd7cea 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1101,8 +1101,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
* These flags are used for communication about keys between the driver
* and mac80211, with the @flags parameter of &struct ieee80211_key_conf.
*
- * @IEEE80211_KEY_FLAG_WMM_STA: Set by mac80211, this flag indicates
- * that the STA this key will be used with could be using QoS.
* @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the
* driver to indicate that it requires IV generation for this
* particular key.
@@ -1127,7 +1125,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
* %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
*/
enum ieee80211_key_flags {
- IEEE80211_KEY_FLAG_WMM_STA = 1<<0,
IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1,
IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
IEEE80211_KEY_FLAG_PAIRWISE = 1<<3,
@@ -1231,9 +1228,8 @@ enum ieee80211_sta_rx_bandwidth {
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
* @supp_rates: Bitmap of supported rates (per band)
- * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities
- * @vht_cap: VHT capabilities of this STA; Not restricting any capabilities
- * of remote STA. Taking as is.
+ * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
+ * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
* @wme: indicates whether the STA supports WME. Only valid during AP-mode.
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information.
@@ -2135,6 +2131,24 @@ enum ieee80211_rate_control_changed {
};
/**
+ * enum ieee80211_roc_type - remain on channel type
+ *
+ * With the support for multi channel contexts and multi channel operations,
+ * remain on channel operations might be limited/deferred/aborted by other
+ * flows/operations which have higher priority (and vise versa).
+ * Specifying the ROC type can be used by devices to prioritize the ROC
+ * operations compared to other operations/flows.
+ *
+ * @IEEE80211_ROC_TYPE_NORMAL: There are no special requirements for this ROC.
+ * @IEEE80211_ROC_TYPE_MGMT_TX: The remain on channel request is required
+ * for sending managment frames offchannel.
+ */
+enum ieee80211_roc_type {
+ IEEE80211_ROC_TYPE_NORMAL = 0,
+ IEEE80211_ROC_TYPE_MGMT_TX,
+};
+
+/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
* This structure contains various callbacks that the driver may
@@ -2687,7 +2701,8 @@ struct ieee80211_ops {
int (*remain_on_channel)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *chan,
- int duration);
+ int duration,
+ enum ieee80211_roc_type type);
int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx);
void (*get_ringparam)(struct ieee80211_hw *hw,
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c46bb01..79da871 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -36,7 +36,21 @@
* The station is still assumed to belong to the AP interface it was added
* to.
*
- * TODO: need more info?
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ * - a setup station entry is added, not yet authorized, without any rate
+ * or capability information, this just exists to avoid race conditions
+ * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ * to add rate and capability information to the station and at the same
+ * time mark it authorized.
+ * - %NL80211_TDLS_ENABLE_LINK is then used
+ * - after this, the only valid operation is to remove it by tearing down
+ * the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
*/
/**
@@ -499,9 +513,11 @@
* @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
* beacon or probe response from a compatible mesh peer. This is only
* sent while no station information (sta_info) exists for the new peer
- * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On
- * reception of this notification, userspace may decide to create a new
- * station (@NL80211_CMD_NEW_STATION). To stop this notification from
+ * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this
+ * notification, userspace may decide to create a new station
+ * (@NL80211_CMD_NEW_STATION). To stop this notification from
* reoccurring, the userspace authentication daemon may want to create the
* new station with the AUTHENTICATED flag unset and maybe change it later
* depending on the authentication result.
@@ -611,6 +627,18 @@
* %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
* event.
*
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ * i.e. features for the nl80211 protocol rather than device features.
+ * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ * Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ * to the supplicant. This will carry the target AP's MAC address along
+ * with the relevant Information Elements. This event is used to report
+ * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -765,6 +793,11 @@ enum nl80211_commands {
NL80211_CMD_RADAR_DETECT,
+ NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+ NL80211_CMD_UPDATE_FT_IES,
+ NL80211_CMD_FT_EVENT,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -884,7 +917,8 @@ enum nl80211_commands {
* consisting of a nested array.
*
* @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ * (see &enum nl80211_plink_action).
* @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
* @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
@@ -1167,10 +1201,10 @@ enum nl80211_commands {
* @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
* allows auth frames in a mesh to be passed to userspace for processing via
* the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
- * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as
- * defined in &enum nl80211_plink_state. Used when userspace is
- * driving the peer link management state machine.
- * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ * &enum nl80211_plink_state. Used when userspace is driving the peer link
+ * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
*
* @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
* capabilities, the supported WoWLAN triggers
@@ -1368,6 +1402,18 @@ enum nl80211_commands {
* advertised to the driver, e.g., to enable TDLS off channel operations
* and PU-APSD.
*
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ * &enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ * receiving the data for a single wiphy split across multiple
+ * messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ * Element
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1654,6 +1700,15 @@ enum nl80211_attrs {
NL80211_ATTR_STA_CAPABILITY,
NL80211_ATTR_STA_EXT_CAPABILITY,
+ NL80211_ATTR_PROTOCOL_FEATURES,
+ NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+ NL80211_ATTR_DISABLE_VHT,
+ NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+ NL80211_ATTR_MDID,
+ NL80211_ATTR_IE_RIC,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2412,8 +2467,10 @@ enum nl80211_mesh_power_mode {
* @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
* point.
*
- * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
- * open peer links when we detect compatible mesh peers.
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ * peer links when we detect compatible mesh peers. Disabled if
+ * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ * set.
*
* @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
* containing a PREQ that an MP can send to a particular destination (path
@@ -2559,6 +2616,9 @@ enum nl80211_meshconf_params {
* vendor specific synchronization method or disable it to use the default
* neighbor offset synchronization
*
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ * implement an MPM which handles peer allocation and state.
+ *
* @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
*
* @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@@ -2571,6 +2631,7 @@ enum nl80211_mesh_setup_params {
NL80211_MESH_SETUP_USERSPACE_AUTH,
NL80211_MESH_SETUP_USERSPACE_AMPE,
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+ NL80211_MESH_SETUP_USERSPACE_MPM,
/* keep last */
__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -3307,6 +3368,23 @@ enum nl80211_plink_state {
MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
};
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+ NL80211_PLINK_ACTION_NO_ACTION,
+ NL80211_PLINK_ACTION_OPEN,
+ NL80211_PLINK_ACTION_BLOCK,
+
+ NUM_NL80211_PLINK_ACTIONS,
+};
+
+
#define NL80211_KCK_LEN 16
#define NL80211_KEK_LEN 16
#define NL80211_REPLAY_CTR_LEN 8
@@ -3456,6 +3534,10 @@ enum nl80211_ap_sme_features {
* stations the authenticated/associated bits have to be set in the mask.
* @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
* (HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ * Peering Management entity which may be implemented by registering for
+ * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ * still generated by the driver.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@@ -3474,6 +3556,7 @@ enum nl80211_feature_flags {
/* bit 13 is reserved */
NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14,
NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
+ NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
};
/**
@@ -3587,4 +3670,16 @@ enum nl80211_dfs_state {
NL80211_DFS_AVAILABLE,
};
+/**
+ * enum enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ * wiphy dumps (if requested by the application with the attribute
+ * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ * %NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 09d96a8..1d1ddab 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -254,7 +254,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
goto out_unlock;
}
- __ieee80211_key_free(key);
+ __ieee80211_key_free(key, true);
ret = 0;
out_unlock:
@@ -1035,9 +1035,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
sta_info_flush_defer(vlan);
sta_info_flush_defer(sdata);
rcu_barrier();
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
sta_info_flush_cleanup(vlan);
+ ieee80211_free_keys(vlan);
+ }
sta_info_flush_cleanup(sdata);
+ ieee80211_free_keys(sdata);
sdata->vif.bss_conf.enable_beacon = false;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
@@ -1177,6 +1180,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ } else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+ /*
+ * TDLS -- everything follows authorized, but
+ * only becoming authorized is possible, not
+ * going back
+ */
+ if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+ set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED);
+ mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED);
+ }
}
ret = sta_apply_auth_flags(local, sta, mask, set);
@@ -1261,7 +1276,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
u32 changed = 0;
- if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
+
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
switch (params->plink_state) {
case NL80211_PLINK_ESTAB:
if (sta->plink_state != NL80211_PLINK_ESTAB)
@@ -1292,15 +1308,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
/* nothing */
break;
}
- } else {
- switch (params->plink_action) {
- case PLINK_ACTION_OPEN:
- changed |= mesh_plink_open(sta);
- break;
- case PLINK_ACTION_BLOCK:
- changed |= mesh_plink_block(sta);
- break;
- }
+ }
+
+ switch (params->plink_action) {
+ case NL80211_PLINK_ACTION_NO_ACTION:
+ /* nothing */
+ break;
+ case NL80211_PLINK_ACTION_OPEN:
+ changed |= mesh_plink_open(sta);
+ break;
+ case NL80211_PLINK_ACTION_BLOCK:
+ changed |= mesh_plink_block(sta);
+ break;
}
if (params->local_pm)
@@ -1346,8 +1365,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
* 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);
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+ }
err = sta_apply_parameters(local, sta, params);
if (err) {
@@ -1356,8 +1377,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
}
/*
- * for TDLS, rate control should be initialized only when supported
- * rates are known.
+ * for TDLS, rate control should be initialized only when
+ * rates are known and station is marked authorized
*/
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
rate_control_rate_init(sta);
@@ -1394,50 +1415,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_change_station(struct wiphy *wiphy,
- struct net_device *dev,
- u8 *mac,
+ struct net_device *dev, u8 *mac,
struct station_parameters *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta;
struct ieee80211_sub_if_data *vlansdata;
+ enum cfg80211_station_type statype;
int err;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mac);
if (!sta) {
- mutex_unlock(&local->sta_mtx);
- return -ENOENT;
+ err = -ENOENT;
+ goto out_err;
}
- /* in station mode, some updates are only valid with TDLS */
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- (params->supported_rates || params->ht_capa || params->vht_capa ||
- params->sta_modify_mask ||
- (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
- !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
- mutex_unlock(&local->sta_mtx);
- return -EINVAL;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ if (sdata->u.mesh.user_mpm)
+ statype = CFG80211_STA_MESH_PEER_USER;
+ else
+ statype = CFG80211_STA_MESH_PEER_KERNEL;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ statype = CFG80211_STA_IBSS;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+ statype = CFG80211_STA_AP_STA;
+ break;
+ }
+ if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+ statype = CFG80211_STA_TDLS_PEER_ACTIVE;
+ else
+ statype = CFG80211_STA_TDLS_PEER_SETUP;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ statype = CFG80211_STA_AP_CLIENT;
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ goto out_err;
}
+ err = cfg80211_check_station_change(wiphy, params, statype);
+ if (err)
+ goto out_err;
+
if (params->vlan && params->vlan != sta->sdata->dev) {
bool prev_4addr = false;
bool new_4addr = false;
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
- if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- vlansdata->vif.type != NL80211_IFTYPE_AP) {
- mutex_unlock(&local->sta_mtx);
- return -EINVAL;
- }
-
if (params->vlan->ieee80211_ptr->use_4addr) {
if (vlansdata->u.vlan.sta) {
- mutex_unlock(&local->sta_mtx);
- return -EBUSY;
+ err = -EBUSY;
+ goto out_err;
}
rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
@@ -1464,12 +1502,12 @@ static int ieee80211_change_station(struct wiphy *wiphy,
}
err = sta_apply_parameters(local, sta, params);
- if (err) {
- mutex_unlock(&local->sta_mtx);
- return err;
- }
+ if (err)
+ goto out_err;
- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
+ /* When peer becomes authorized, init rate control as well */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+ test_sta_flag(sta, WLAN_STA_AUTHORIZED))
rate_control_rate_init(sta);
mutex_unlock(&local->sta_mtx);
@@ -1479,7 +1517,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
ieee80211_recalc_ps(local, -1);
ieee80211_recalc_ps_vif(sdata);
}
+
return 0;
+out_err:
+ mutex_unlock(&local->sta_mtx);
+ return err;
}
#ifdef CONFIG_MAC80211_MESH
@@ -1687,6 +1729,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
ifmsh->mesh_sp_id = setup->sync_method;
ifmsh->mesh_pp_id = setup->path_sel_proto;
ifmsh->mesh_pm_id = setup->path_metric;
+ ifmsh->user_mpm = setup->user_mpm;
ifmsh->security = IEEE80211_MESH_SEC_NONE;
if (setup->is_authenticated)
ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
@@ -1730,8 +1773,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
conf->dot11MeshTTL = nconf->dot11MeshTTL;
if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
conf->element_ttl = nconf->element_ttl;
- if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+ if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
+ if (ifmsh->user_mpm)
+ return -EBUSY;
conf->auto_open_plinks = nconf->auto_open_plinks;
+ }
if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
conf->dot11MeshNbrOffsetMaxNeighbor =
nconf->dot11MeshNbrOffsetMaxNeighbor;
@@ -2371,7 +2417,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
unsigned int duration, u64 *cookie,
- struct sk_buff *txskb)
+ struct sk_buff *txskb,
+ enum ieee80211_roc_type type)
{
struct ieee80211_roc_work *roc, *tmp;
bool queued = false;
@@ -2390,6 +2437,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
roc->duration = duration;
roc->req_duration = duration;
roc->frame = txskb;
+ roc->type = type;
roc->mgmt_tx_cookie = (unsigned long)txskb;
roc->sdata = sdata;
INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
@@ -2420,7 +2468,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, sdata, channel, duration);
+ ret = drv_remain_on_channel(local, sdata, channel, duration, type);
if (ret) {
kfree(roc);
return ret;
@@ -2439,10 +2487,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
*
* If it hasn't started yet, just increase the duration
* and add the new one to the list of dependents.
+ * If the type of the new ROC has higher priority, modify the
+ * type of the previous one to match that of the new one.
*/
if (!tmp->started) {
list_add_tail(&roc->list, &tmp->dependents);
tmp->duration = max(tmp->duration, roc->duration);
+ tmp->type = max(tmp->type, roc->type);
queued = true;
break;
}
@@ -2454,16 +2505,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
/*
* In the offloaded ROC case, if it hasn't begun, add
* this new one to the dependent list to be handled
- * when the the master one begins. If it has begun,
+ * when the master one begins. If it has begun,
* check that there's still a minimum time left and
* if so, start this one, transmitting the frame, but
- * add it to the list directly after this one with a
+ * add it to the list directly after this one with
* a reduced time so we'll ask the driver to execute
* it right after finishing the previous one, in the
* hope that it'll also be executed right afterwards,
* effectively extending the old one.
* If there's no minimum time left, just add it to the
* normal list.
+ * TODO: the ROC type is ignored here, assuming that it
+ * is better to immediately use the current ROC.
*/
if (!tmp->hw_begun) {
list_add_tail(&roc->list, &tmp->dependents);
@@ -2557,7 +2610,8 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
mutex_lock(&local->mtx);
ret = ieee80211_start_roc_work(local, sdata, chan,
- duration, cookie, NULL);
+ duration, cookie, NULL,
+ IEEE80211_ROC_TYPE_NORMAL);
mutex_unlock(&local->mtx);
return ret;
@@ -2790,7 +2844,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* This will handle all kinds of coalescing and immediate TX */
ret = ieee80211_start_roc_work(local, sdata, chan,
- wait, cookie, skb);
+ wait, cookie, skb,
+ IEEE80211_ROC_TYPE_MGMT_TX);
if (ret)
kfree_skb(skb);
out_unlock:
@@ -3285,6 +3340,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_chanctx_conf *chanctx_conf;
int ret = -ENODATA;
@@ -3293,6 +3349,16 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
if (chanctx_conf) {
*chandef = chanctx_conf->def;
ret = 0;
+ } else if (local->open_count > 0 &&
+ local->open_count == local->monitors &&
+ sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ if (local->use_chanctx)
+ *chandef = local->monitor_chandef;
+ else
+ cfg80211_chandef_create(chandef,
+ local->_oper_channel,
+ local->_oper_channel_type);
+ ret = 0;
}
rcu_read_unlock();
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index c7591f7..4f841fe 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -325,6 +325,36 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
}
STA_OPS(ht_capa);
+static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[128], *p = buf;
+ struct sta_info *sta = file->private_data;
+ struct ieee80211_sta_vht_cap *vhtc = &sta->sta.vht_cap;
+
+ p += scnprintf(p, sizeof(buf) + buf - p, "VHT %ssupported\n",
+ vhtc->vht_supported ? "" : "not ");
+ if (vhtc->vht_supported) {
+ p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.8x\n", vhtc->cap);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "RX MCS: %.4x\n",
+ le16_to_cpu(vhtc->vht_mcs.rx_mcs_map));
+ if (vhtc->vht_mcs.rx_highest)
+ p += scnprintf(p, sizeof(buf)+buf-p,
+ "MCS RX highest: %d Mbps\n",
+ le16_to_cpu(vhtc->vht_mcs.rx_highest));
+ p += scnprintf(p, sizeof(buf)+buf-p, "TX MCS: %.4x\n",
+ le16_to_cpu(vhtc->vht_mcs.tx_mcs_map));
+ if (vhtc->vht_mcs.tx_highest)
+ p += scnprintf(p, sizeof(buf)+buf-p,
+ "MCS TX highest: %d Mbps\n",
+ le16_to_cpu(vhtc->vht_mcs.tx_highest));
+ }
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+STA_OPS(vht_capa);
+
static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
@@ -405,6 +435,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(dev);
DEBUGFS_ADD(last_signal);
DEBUGFS_ADD(ht_capa);
+ DEBUGFS_ADD(vht_capa);
DEBUGFS_ADD(last_ack_signal);
DEBUGFS_ADD(current_tx_rate);
DEBUGFS_ADD(last_rx_rate);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index ee56d07..832acea 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -787,15 +787,16 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
static inline int drv_remain_on_channel(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *chan,
- unsigned int duration)
+ unsigned int duration,
+ enum ieee80211_roc_type type)
{
int ret;
might_sleep();
- trace_drv_remain_on_channel(local, sdata, chan, duration);
+ trace_drv_remain_on_channel(local, sdata, chan, duration, type);
ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
- chan, duration);
+ chan, duration, type);
trace_drv_return_int(local, ret);
return ret;
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 0db25d4..af8cee0 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -40,13 +40,6 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
if (!ht_cap->ht_supported)
return;
- if (sdata->vif.type != NL80211_IFTYPE_STATION) {
- /* AP interfaces call this code when adding new stations,
- * so just silently ignore non station interfaces.
- */
- return;
- }
-
/* NOTE: If you add more over-rides here, update register_hw
* ht_capa_mod_msk logic in main.c as well.
* And, if this method can ever change ht_cap.ht_supported, fix
@@ -97,7 +90,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_cap *ht_cap_ie,
struct sta_info *sta)
{
- struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_ht_cap ht_cap, own_cap;
u8 ampdu_info, tx_mcs_set_cap;
int i, max_tx_streams;
bool changed;
@@ -111,6 +104,18 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
ht_cap.ht_supported = true;
+ own_cap = sband->ht_cap;
+
+ /*
+ * If user has specified capability over-rides, take care
+ * of that if the station we're setting up is the AP that
+ * we advertised a restricted capability set to. Override
+ * our own capabilities and then use those below.
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ ieee80211_apply_htcap_overrides(sdata, &own_cap);
+
/*
* The bits listed in this expression should be
* the same for the peer and us, if the station
@@ -118,21 +123,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* we mask them out.
*/
ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
- (sband->ht_cap.cap |
- ~(IEEE80211_HT_CAP_LDPC_CODING |
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_GRN_FLD |
- IEEE80211_HT_CAP_SGI_20 |
- IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_DSSSCCK40));
+ (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40));
/*
* The STBC bits are asymmetric -- if we don't have
* TX then mask out the peer's RX and vice versa.
*/
- if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
+ if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
- if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
+ if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
ampdu_info = ht_cap_ie->ampdu_params_info;
@@ -142,7 +146,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
/* own MCS TX capabilities */
- tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
+ tx_mcs_set_cap = own_cap.mcs.tx_params;
/* Copy peer MCS TX capabilities, the driver might need them. */
ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
@@ -168,26 +172,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
*/
for (i = 0; i < max_tx_streams; i++)
ht_cap.mcs.rx_mask[i] =
- sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+ own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
i < IEEE80211_HT_MCS_MASK_LEN; i++)
ht_cap.mcs.rx_mask[i] =
- sband->ht_cap.mcs.rx_mask[i] &
+ own_cap.mcs.rx_mask[i] &
ht_cap_ie->mcs.rx_mask[i];
/* handle MCS rate 32 too */
- if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+ if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
ht_cap.mcs.rx_mask[32/8] |= 1;
apply:
- /*
- * If user has specified capability over-rides, take care
- * of that here.
- */
- ieee80211_apply_htcap_overrides(sdata, &ht_cap);
-
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 40b71df..539d4a1 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -985,36 +985,9 @@ static void ieee80211_ibss_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- struct ieee80211_local *local = sdata->local;
-
- if (local->quiescing) {
- ifibss->timer_running = true;
- return;
- }
-
- ieee80211_queue_work(&local->hw, &sdata->work);
-}
-
-#ifdef CONFIG_PM
-void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- if (del_timer_sync(&ifibss->timer))
- ifibss->timer_running = true;
-}
-
-void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-
- if (ifibss->timer_running) {
- add_timer(&ifibss->timer);
- ifibss->timer_running = false;
- }
+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
-#endif
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 388580a..f4433f0 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -315,6 +315,7 @@ struct ieee80211_roc_work {
u32 duration, req_duration;
struct sk_buff *frame;
u64 cookie, mgmt_tx_cookie;
+ enum ieee80211_roc_type type;
};
/* flags used in struct ieee80211_if_managed.flags */
@@ -400,7 +401,6 @@ struct ieee80211_if_managed {
u16 aid;
- 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;
@@ -479,6 +479,8 @@ struct ieee80211_if_managed {
struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+ struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
+ struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
};
struct ieee80211_if_ibss {
@@ -490,8 +492,6 @@ struct ieee80211_if_ibss {
u32 basic_rates;
- bool timer_running;
-
bool fixed_bssid;
bool fixed_channel;
bool privacy;
@@ -543,8 +543,6 @@ struct ieee80211_if_mesh {
struct timer_list mesh_path_timer;
struct timer_list mesh_path_root_timer;
- unsigned long timers_running;
-
unsigned long wrkq_flags;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -590,6 +588,7 @@ struct ieee80211_if_mesh {
IEEE80211_MESH_SEC_AUTHED = 0x1,
IEEE80211_MESH_SEC_SECURED = 0x2,
} security;
+ bool user_mpm;
/* Extensible Synchronization Framework */
const struct ieee80211_mesh_sync_ops *sync_ops;
s64 sync_offset_clockdrift_max;
@@ -682,6 +681,8 @@ struct ieee80211_sub_if_data {
/* count for keys needing tailroom space allocation */
int crypto_tx_tailroom_needed_cnt;
+ int crypto_tx_tailroom_pending_dec;
+ struct delayed_work dec_tailroom_needed_wk;
struct net_device *dev;
struct ieee80211_local *local;
@@ -765,10 +766,6 @@ struct ieee80211_sub_if_data {
} 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;
};
@@ -1136,11 +1133,6 @@ struct ieee80211_local {
struct ieee80211_sub_if_data __rcu *p2p_sdata;
- /* dummy netdev for use w/ NAPI */
- struct net_device napi_dev;
-
- struct napi_struct napi;
-
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef;
@@ -1283,8 +1275,6 @@ void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss, u64 timestamp);
-void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
@@ -1302,8 +1292,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
-void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
@@ -1441,6 +1429,8 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta);
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
enum ieee80211_band band, bool nss_only);
+void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_vht_cap *vht_cap);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 2c059e5..d85282f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -107,7 +107,7 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
lockdep_assert_held(&local->mtx);
- active = !list_empty(&local->chanctx_list);
+ active = !list_empty(&local->chanctx_list) || local->monitors;
if (!local->ops->remain_on_channel) {
list_for_each_entry(roc, &local->roc_list, list) {
@@ -485,8 +485,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
res = drv_start(local);
if (res)
goto err_del_bss;
- if (local->ops->napi_poll)
- napi_enable(&local->napi);
/* we're brought up, everything changes */
hw_reconf_flags = ~0;
ieee80211_led_radio(local, true);
@@ -541,6 +539,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local);
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
netif_carrier_on(dev);
break;
@@ -812,6 +813,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_adjust_monitor_flags(sdata, -1);
ieee80211_configure_filter(local);
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
break;
case NL80211_IFTYPE_P2P_DEVICE:
/* relies on synchronize_rcu() below */
@@ -832,14 +836,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
rcu_barrier();
sta_info_flush_cleanup(sdata);
- skb_queue_purge(&sdata->skb_queue);
-
/*
* Free all remaining keys, there shouldn't be any,
- * except maybe group keys in AP more or WDS?
+ * except maybe in WDS mode?
*/
ieee80211_free_keys(sdata);
+ /* fall through */
+ case NL80211_IFTYPE_AP:
+ skb_queue_purge(&sdata->skb_queue);
+
drv_remove_interface_debugfs(local, sdata);
if (going_down)
@@ -851,8 +857,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
if (local->open_count == 0) {
- if (local->ops->napi_poll)
- napi_disable(&local->napi);
ieee80211_clear_tx_pending(local);
ieee80211_stop_device(local);
@@ -1541,6 +1545,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
ieee80211_dfs_cac_timer_work);
+ INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
+ ieee80211_delayed_tailroom_dec);
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index ef252eb..99e9f6a 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -397,7 +397,8 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
return key;
}
-static void __ieee80211_key_destroy(struct ieee80211_key *key)
+static void __ieee80211_key_destroy(struct ieee80211_key *key,
+ bool delay_tailroom)
{
if (!key)
return;
@@ -416,8 +417,18 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
if (key->local) {
+ struct ieee80211_sub_if_data *sdata = key->sdata;
+
ieee80211_debugfs_key_remove(key);
- key->sdata->crypto_tx_tailroom_needed_cnt--;
+
+ if (delay_tailroom) {
+ /* see ieee80211_delayed_tailroom_dec */
+ sdata->crypto_tx_tailroom_pending_dec++;
+ schedule_delayed_work(&sdata->dec_tailroom_needed_wk,
+ HZ/2);
+ } else {
+ sdata->crypto_tx_tailroom_needed_cnt--;
+ }
}
kfree(key);
@@ -440,32 +451,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
key->sdata = sdata;
key->sta = sta;
- if (sta) {
- /*
- * some hardware cannot handle TKIP with QoS, so
- * we indicate whether QoS could be in use.
- */
- if (test_sta_flag(sta, WLAN_STA_WME))
- key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
- } else {
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- struct sta_info *ap;
-
- /*
- * We're getting a sta pointer in, so must be under
- * appropriate locking for sta_info_get().
- */
-
- /* same here, the AP could be using QoS */
- ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid);
- if (ap) {
- if (test_sta_flag(ap, WLAN_STA_WME))
- key->conf.flags |=
- IEEE80211_KEY_FLAG_WMM_STA;
- }
- }
- }
-
mutex_lock(&sdata->local->key_mtx);
if (sta && pairwise)
@@ -478,7 +463,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
increment_tailroom_need_count(sdata);
__ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
- __ieee80211_key_destroy(old_key);
+ __ieee80211_key_destroy(old_key, true);
ieee80211_debugfs_key_add(key);
@@ -489,7 +474,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
return ret;
}
-void __ieee80211_key_free(struct ieee80211_key *key)
+void __ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
{
if (!key)
return;
@@ -501,14 +486,14 @@ void __ieee80211_key_free(struct ieee80211_key *key)
__ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
- __ieee80211_key_destroy(key);
+ __ieee80211_key_destroy(key, delay_tailroom);
}
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key)
{
mutex_lock(&local->key_mtx);
- __ieee80211_key_free(key);
+ __ieee80211_key_free(key, true);
mutex_unlock(&local->key_mtx);
}
@@ -566,36 +551,60 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_iter_keys);
-void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_key *key;
-
- ASSERT_RTNL();
-
- mutex_lock(&sdata->local->key_mtx);
-
- list_for_each_entry(key, &sdata->key_list, list)
- ieee80211_key_disable_hw_accel(key);
-
- mutex_unlock(&sdata->local->key_mtx);
-}
-
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_key *key, *tmp;
+ cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
+
mutex_lock(&sdata->local->key_mtx);
+ sdata->crypto_tx_tailroom_needed_cnt -=
+ sdata->crypto_tx_tailroom_pending_dec;
+ sdata->crypto_tx_tailroom_pending_dec = 0;
+
ieee80211_debugfs_key_remove_mgmt_default(sdata);
list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
- __ieee80211_key_free(key);
+ __ieee80211_key_free(key, false);
ieee80211_debugfs_key_update_default(sdata);
+ WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+ sdata->crypto_tx_tailroom_pending_dec);
+
mutex_unlock(&sdata->local->key_mtx);
}
+void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(wk, struct ieee80211_sub_if_data,
+ dec_tailroom_needed_wk.work);
+
+ /*
+ * The reason for the delayed tailroom needed decrementing is to
+ * make roaming faster: during roaming, all keys are first deleted
+ * and then new keys are installed. The first new key causes the
+ * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes
+ * the cost of synchronize_net() (which can be slow). Avoid this
+ * by deferring the crypto_tx_tailroom_needed_cnt decrementing on
+ * key removal for a while, so if we roam the value is larger than
+ * zero and no 0->1 transition happens.
+ *
+ * The cost is that if the AP switching was from an AP with keys
+ * to one without, we still allocate tailroom while it would no
+ * longer be needed. However, in the typical (fast) roaming case
+ * within an ESS this usually won't happen.
+ */
+
+ mutex_lock(&sdata->local->key_mtx);
+ sdata->crypto_tx_tailroom_needed_cnt -=
+ sdata->crypto_tx_tailroom_pending_dec;
+ sdata->crypto_tx_tailroom_pending_dec = 0;
+ mutex_unlock(&sdata->local->key_mtx);
+}
void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 382dc44..2a682d8 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -134,7 +134,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
int __must_check ieee80211_key_link(struct ieee80211_key *key,
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta);
-void __ieee80211_key_free(struct ieee80211_key *key);
+void __ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom);
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key);
void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
@@ -143,9 +143,10 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
int idx);
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
-void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);
#define key_mtx_dereference(local, ref) \
rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
+void ieee80211_delayed_tailroom_dec(struct work_struct *wk);
+
#endif /* IEEE80211_KEY_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 1a8591b..5a53aa5 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -399,30 +399,6 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,
}
#endif
-static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
-{
- struct ieee80211_local *local =
- container_of(napi, struct ieee80211_local, napi);
-
- return local->ops->napi_poll(&local->hw, budget);
-}
-
-void ieee80211_napi_schedule(struct ieee80211_hw *hw)
-{
- struct ieee80211_local *local = hw_to_local(hw);
-
- napi_schedule(&local->napi);
-}
-EXPORT_SYMBOL(ieee80211_napi_schedule);
-
-void ieee80211_napi_complete(struct ieee80211_hw *hw)
-{
- struct ieee80211_local *local = hw_to_local(hw);
-
- napi_complete(&local->napi);
-}
-EXPORT_SYMBOL(ieee80211_napi_complete);
-
/* There isn't a lot of sense in it, but you can transmit anything you like */
static const struct ieee80211_txrx_stypes
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
@@ -501,6 +477,27 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
},
};
+static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
+ .vht_cap_info =
+ cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_SHORT_GI_160 |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_RXSTBC_2 |
+ IEEE80211_VHT_CAP_RXSTBC_3 |
+ IEEE80211_VHT_CAP_RXSTBC_4 |
+ IEEE80211_VHT_CAP_TXSTBC |
+ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+ IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN |
+ IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK),
+ .supp_mcs = {
+ .rx_mcs_map = cpu_to_le16(~0),
+ .tx_mcs_map = cpu_to_le16(~0),
+ },
+};
+
static const u8 extended_capabilities[] = {
0, 0, 0, 0, 0, 0, 0,
WLAN_EXT_CAPA8_OPMODE_NOTIF,
@@ -572,7 +569,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS |
- NL80211_FEATURE_VIF_TXPOWER;
+ NL80211_FEATURE_VIF_TXPOWER |
+ NL80211_FEATURE_USERSPACE_MPM;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -609,6 +607,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
+ wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
@@ -664,9 +663,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable);
- /* init dummy netdev for use w/ NAPI */
- init_dummy_netdev(&local->napi_dev);
-
ieee80211_led_names(local);
ieee80211_roc_setup(local);
@@ -1021,9 +1017,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_ifa6;
#endif
- netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
- local->hw.napi_weight);
-
return 0;
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 29ce2aa..5ac017f 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -13,10 +13,6 @@
#include "ieee80211_i.h"
#include "mesh.h"
-#define TMR_RUNNING_HK 0
-#define TMR_RUNNING_MP 1
-#define TMR_RUNNING_MPR 2
-
static int mesh_allocated;
static struct kmem_cache *rm_cache;
@@ -50,11 +46,6 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
- if (local->quiescing) {
- set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
- return;
- }
-
ieee80211_queue_work(&local->hw, &sdata->work);
}
@@ -165,7 +156,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
* an update.
*/
changed = mesh_accept_plinks_update(sdata);
- if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+ if (!sdata->u.mesh.user_mpm) {
changed |= mesh_plink_deactivate(sta);
del_timer_sync(&sta->plink_timer);
}
@@ -479,15 +470,8 @@ static void ieee80211_mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- struct ieee80211_local *local = sdata->local;
-
- if (local->quiescing) {
- set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
- return;
- }
- ieee80211_queue_work(&local->hw, &sdata->work);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
static void ieee80211_mesh_path_root_timer(unsigned long data)
@@ -495,16 +479,10 @@ static void ieee80211_mesh_path_root_timer(unsigned long data)
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- struct ieee80211_local *local = sdata->local;
set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
- if (local->quiescing) {
- set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
- return;
- }
-
- ieee80211_queue_work(&local->hw, &sdata->work);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)
@@ -622,35 +600,6 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)
round_jiffies(TU_TO_EXP_TIME(interval)));
}
-#ifdef CONFIG_PM
-void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-
- /* use atomic bitops in case all timers fire at the same time */
-
- if (del_timer_sync(&ifmsh->housekeeping_timer))
- set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
- if (del_timer_sync(&ifmsh->mesh_path_timer))
- set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
- if (del_timer_sync(&ifmsh->mesh_path_root_timer))
- set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
-}
-
-void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-
- if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
- add_timer(&ifmsh->housekeeping_timer);
- if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
- add_timer(&ifmsh->mesh_path_timer);
- if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running))
- add_timer(&ifmsh->mesh_path_root_timer);
- ieee80211_mesh_root_setup(ifmsh);
-}
-#endif
-
static int
ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
{
@@ -871,8 +820,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local);
-
- sdata->u.mesh.timers_running = 0;
}
static void
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 336c88a..6ffabbe 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -313,8 +313,6 @@ void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta);
void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
-void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
-void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
@@ -359,22 +357,12 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
-void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
-void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
-void mesh_plink_quiesce(struct sta_info *sta);
-void mesh_plink_restart(struct sta_info *sta);
void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
void ieee80211s_stop(void);
#else
static inline void
ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
-static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
-{}
-static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
-{}
-static inline void mesh_plink_quiesce(struct sta_info *sta) {}
-static inline void mesh_plink_restart(struct sta_info *sta) {}
static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
{ return false; }
static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 07d396d..937e06f 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -420,7 +420,6 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
return NULL;
sta->plink_state = NL80211_PLINK_LISTEN;
- init_timer(&sta->plink_timer);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
@@ -437,8 +436,9 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
{
struct sta_info *sta = NULL;
- /* Userspace handles peer allocation when security is enabled */
- if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
+ /* Userspace handles station allocation */
+ if (sdata->u.mesh.user_mpm ||
+ sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
cfg80211_notify_new_peer_candidate(sdata->dev, addr,
elems->ie_start,
elems->total_len,
@@ -534,10 +534,8 @@ static void mesh_plink_timer(unsigned long data)
*/
sta = (struct sta_info *) data;
- if (sta->sdata->local->quiescing) {
- sta->plink_timer_was_running = true;
+ if (sta->sdata->local->quiescing)
return;
- }
spin_lock_bh(&sta->lock);
if (sta->ignore_plink_timer) {
@@ -598,29 +596,6 @@ static void mesh_plink_timer(unsigned long data)
}
}
-#ifdef CONFIG_PM
-void mesh_plink_quiesce(struct sta_info *sta)
-{
- if (!ieee80211_vif_is_mesh(&sta->sdata->vif))
- return;
-
- /* no kernel mesh sta timers have been initialized */
- if (sta->sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
- return;
-
- if (del_timer_sync(&sta->plink_timer))
- sta->plink_timer_was_running = true;
-}
-
-void mesh_plink_restart(struct sta_info *sta)
-{
- if (sta->plink_timer_was_running) {
- add_timer(&sta->plink_timer);
- sta->plink_timer_was_running = false;
- }
-}
-#endif
-
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
{
sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
@@ -695,6 +670,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
return;
+ if (sdata->u.mesh.user_mpm)
+ /* userspace must register for these */
+ return;
+
if (is_multicast_ether_addr(mgmt->da)) {
mpl_dbg(sdata,
"Mesh plink: ignore frame from multicast address\n");
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9f6464f..fdc06e3 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -87,9 +87,6 @@ MODULE_PARM_DESC(probe_wait_ms,
*/
#define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
-#define TMR_RUNNING_TIMER 0
-#define TMR_RUNNING_CHANSW 1
-
/*
* All cfg80211 functions have to be called outside a locked
* section so that they can acquire a lock themselves... This
@@ -609,6 +606,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
/* determine capability flags */
cap = vht_cap.cap;
@@ -647,6 +645,9 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
mask) >> shift;
+ if (our_mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ continue;
+
switch (ap_mcs) {
default:
if (our_mcs <= ap_mcs)
@@ -1035,14 +1036,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- if (sdata->local->quiescing) {
- set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
- return;
- }
-
- ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
}
void
@@ -1799,9 +1794,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.p2p_ctwindow = 0;
sdata->vif.bss_conf.p2p_oppps = false;
- /* on the next assoc, re-program HT parameters */
+ /* on the next assoc, re-program HT/VHT parameters */
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
+ memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
+ memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
@@ -1827,8 +1824,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.timer);
del_timer_sync(&sdata->u.mgd.chswitch_timer);
- sdata->u.mgd.timers_running = 0;
-
sdata->vif.bss_conf.dtim_period = 0;
ifmgd->flags = 0;
@@ -3137,15 +3132,8 @@ static void ieee80211_sta_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
-
- if (local->quiescing) {
- set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
- return;
- }
- ieee80211_queue_work(&local->hw, &sdata->work);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
@@ -3497,68 +3485,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
}
}
-#ifdef CONFIG_PM
-void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- /*
- * we need to use atomic bitops for the running bits
- * only because both timers might fire at the same
- * time -- the code here is properly synchronised.
- */
-
- cancel_work_sync(&ifmgd->request_smps_work);
-
- cancel_work_sync(&ifmgd->monitor_work);
- cancel_work_sync(&ifmgd->beacon_connection_loss_work);
- cancel_work_sync(&ifmgd->csa_connection_drop_work);
- if (del_timer_sync(&ifmgd->timer))
- set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
-
- cancel_work_sync(&ifmgd->chswitch_work);
- if (del_timer_sync(&ifmgd->chswitch_timer))
- set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
-
- /* these will just be re-established on connection */
- del_timer_sync(&ifmgd->conn_mon_timer);
- del_timer_sync(&ifmgd->bcn_mon_timer);
-}
-
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- mutex_lock(&ifmgd->mtx);
- if (!ifmgd->associated) {
- mutex_unlock(&ifmgd->mtx);
- return;
- }
-
- if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
- sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
- mlme_dbg(sdata, "driver requested disconnect after resume\n");
- ieee80211_sta_connection_lost(sdata,
- ifmgd->associated->bssid,
- WLAN_REASON_UNSPECIFIED,
- true);
- mutex_unlock(&ifmgd->mtx);
- return;
- }
- mutex_unlock(&ifmgd->mtx);
-
- if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
- add_timer(&ifmgd->timer);
- if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
- add_timer(&ifmgd->chswitch_timer);
- ieee80211_sta_reset_beacon_monitor(sdata);
-
- mutex_lock(&sdata->local->mtx);
- ieee80211_restart_sta_timer(sdata);
- mutex_unlock(&sdata->local->mtx);
-}
-#endif
-
/* interface setup */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
@@ -4064,6 +3990,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
}
+ if (req->flags & ASSOC_REQ_DISABLE_VHT)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
/* Also disable HT if we don't support it or the AP doesn't use WMM */
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
@@ -4087,6 +4016,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
sizeof(ifmgd->ht_capa_mask));
+ memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa));
+ memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask,
+ sizeof(ifmgd->vht_capa_mask));
+
if (req->ie && req->ie_len) {
memcpy(assoc_data->ie, req->ie, req->ie_len);
assoc_data->ie_len = req->ie_len;
@@ -4315,6 +4248,17 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ /*
+ * Make sure some work items will not run after this,
+ * they will not do anything but might not have been
+ * cancelled when disconnecting.
+ */
+ cancel_work_sync(&ifmgd->monitor_work);
+ cancel_work_sync(&ifmgd->beacon_connection_loss_work);
+ cancel_work_sync(&ifmgd->request_smps_work);
+ cancel_work_sync(&ifmgd->csa_connection_drop_work);
+ cancel_work_sync(&ifmgd->chswitch_work);
+
mutex_lock(&ifmgd->mtx);
if (ifmgd->assoc_data)
ieee80211_destroy_assoc_data(sdata, false);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index cc79b4a..db547fc 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -277,7 +277,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
duration = 10;
ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
- duration);
+ duration, roc->type);
roc->started = true;
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index d0275f3..b471a67 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,32 +6,11 @@
#include "driver-ops.h"
#include "led.h"
-/* return value indicates whether the driver should be further notified */
-static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
-{
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- ieee80211_sta_quiesce(sdata);
- break;
- case NL80211_IFTYPE_ADHOC:
- ieee80211_ibss_quiesce(sdata);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- ieee80211_mesh_quiesce(sdata);
- break;
- default:
- break;
- }
-
- cancel_work_sync(&sdata->work);
-}
-
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- struct ieee80211_chanctx *ctx;
if (!local->open_count)
goto suspend;
@@ -93,19 +72,12 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return err;
} else if (err > 0) {
WARN_ON(err != 1);
- local->wowlan = false;
+ return err;
} else {
- list_for_each_entry(sdata, &local->interfaces, list)
- if (ieee80211_sdata_running(sdata))
- ieee80211_quiesce(sdata);
goto suspend;
}
}
- /* disable keys */
- list_for_each_entry(sdata, &local->interfaces, list)
- ieee80211_disable_keys(sdata);
-
/* tear down aggregation sessions and remove STAs */
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
@@ -117,100 +89,25 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
WARN_ON(drv_sta_state(local, sta->sdata, sta,
state, state - 1));
}
-
- mesh_plink_quiesce(sta);
}
mutex_unlock(&local->sta_mtx);
/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
- static u8 zero_addr[ETH_ALEN] = {};
- u32 changed = 0;
-
if (!ieee80211_sdata_running(sdata))
continue;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_MONITOR:
- /* skip these */
- continue;
- case NL80211_IFTYPE_STATION:
- if (sdata->vif.bss_conf.assoc)
- changed = BSS_CHANGED_ASSOC |
- BSS_CHANGED_BSSID |
- BSS_CHANGED_IDLE;
- break;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- if (sdata->vif.bss_conf.enable_beacon)
- changed = BSS_CHANGED_BEACON_ENABLED;
- break;
- default:
- break;
- }
-
- ieee80211_quiesce(sdata);
-
- sdata->suspend_bss_conf = sdata->vif.bss_conf;
- memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf));
- sdata->vif.bss_conf.idle = true;
- if (sdata->suspend_bss_conf.bssid)
- sdata->vif.bss_conf.bssid = zero_addr;
-
- /* disable beaconing or remove association */
- ieee80211_bss_info_change_notify(sdata, changed);
-
- if (sdata->vif.type == NL80211_IFTYPE_AP &&
- rcu_access_pointer(sdata->u.ap.beacon))
- drv_stop_ap(local, sdata);
-
- if (local->use_chanctx) {
- struct ieee80211_chanctx_conf *conf;
-
- mutex_lock(&local->chanctx_mtx);
- conf = rcu_dereference_protected(
- sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (conf) {
- ctx = container_of(conf,
- struct ieee80211_chanctx,
- conf);
- drv_unassign_vif_chanctx(local, sdata, ctx);
- }
-
- mutex_unlock(&local->chanctx_mtx);
- }
drv_remove_interface(local, sdata);
}
sdata = rtnl_dereference(local->monitor_sdata);
- if (sdata) {
- if (local->use_chanctx) {
- struct ieee80211_chanctx_conf *conf;
-
- mutex_lock(&local->chanctx_mtx);
- conf = rcu_dereference_protected(
- sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (conf) {
- ctx = container_of(conf,
- struct ieee80211_chanctx,
- conf);
- drv_unassign_vif_chanctx(local, sdata, ctx);
- }
-
- mutex_unlock(&local->chanctx_mtx);
- }
-
+ if (sdata)
drv_remove_interface(local, sdata);
- }
- mutex_lock(&local->chanctx_mtx);
- list_for_each_entry(ctx, &local->chanctx_list, list)
- drv_remove_chanctx(local, ctx);
- mutex_unlock(&local->chanctx_mtx);
+ /*
+ * We disconnected on all interfaces before suspend, all channel
+ * contexts should be released.
+ */
+ WARN_ON(!list_empty(&local->chanctx_list));
/* stop hardware - this must stop RX */
if (local->open_count)
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index eea45a2..1c36c9b 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -55,7 +55,6 @@
#include "rate.h"
#include "rc80211_minstrel.h"
-#define SAMPLE_COLUMNS 10
#define SAMPLE_TBL(_mi, _idx, _col) \
_mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col]
@@ -70,16 +69,31 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
return i;
}
+/* find & sort topmost throughput rates */
+static inline void
+minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
+{
+ int j = MAX_THR_RATES;
+
+ while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp)
+ j--;
+ if (j < MAX_THR_RATES - 1)
+ memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
+ if (j < MAX_THR_RATES)
+ tp_list[j] = i;
+}
+
static void
minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
{
- u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0;
- u32 max_prob = 0, index_max_prob = 0;
+ u8 tmp_tp_rate[MAX_THR_RATES];
+ u8 tmp_prob_rate = 0;
u32 usecs;
- u32 p;
int i;
- mi->stats_update = jiffies;
+ for (i=0; i < MAX_THR_RATES; i++)
+ tmp_tp_rate[i] = 0;
+
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
@@ -87,27 +101,32 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
if (!usecs)
usecs = 1000000;
- /* To avoid rounding issues, probabilities scale from 0 (0%)
- * to 18000 (100%) */
- if (mr->attempts) {
- p = (mr->success * 18000) / mr->attempts;
+ if (unlikely(mr->attempts > 0)) {
+ mr->sample_skipped = 0;
+ mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
mr->succ_hist += mr->success;
mr->att_hist += mr->attempts;
- mr->cur_prob = p;
- p = ((p * (100 - mp->ewma_level)) + (mr->probability *
- mp->ewma_level)) / 100;
- mr->probability = p;
- mr->cur_tp = p * (1000000 / usecs);
- }
+ mr->probability = minstrel_ewma(mr->probability,
+ mr->cur_prob,
+ EWMA_LEVEL);
+ } else
+ mr->sample_skipped++;
mr->last_success = mr->success;
mr->last_attempts = mr->attempts;
mr->success = 0;
mr->attempts = 0;
+ /* Update throughput per rate, reset thr. below 10% success */
+ if (mr->probability < MINSTREL_FRAC(10, 100))
+ mr->cur_tp = 0;
+ else
+ mr->cur_tp = mr->probability * (1000000 / usecs);
+
/* Sample less often below the 10% chance of success.
* Sample less often above the 95% chance of success. */
- if ((mr->probability > 17100) || (mr->probability < 1800)) {
+ if (mr->probability > MINSTREL_FRAC(95, 100) ||
+ mr->probability < MINSTREL_FRAC(10, 100)) {
mr->adjusted_retry_count = mr->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
@@ -118,35 +137,30 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
}
if (!mr->adjusted_retry_count)
mr->adjusted_retry_count = 2;
- }
- for (i = 0; i < mi->n_rates; i++) {
- struct minstrel_rate *mr = &mi->r[i];
- if (max_tp < mr->cur_tp) {
- index_max_tp = i;
- max_tp = mr->cur_tp;
- }
- if (max_prob < mr->probability) {
- index_max_prob = i;
- max_prob = mr->probability;
+ minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate);
+
+ /* To determine the most robust rate (max_prob_rate) used at
+ * 3rd mmr stage we distinct between two cases:
+ * (1) if any success probabilitiy >= 95%, out of those rates
+ * choose the maximum throughput rate as max_prob_rate
+ * (2) if all success probabilities < 95%, the rate with
+ * highest success probability is choosen as max_prob_rate */
+ if (mr->probability >= MINSTREL_FRAC(95,100)) {
+ if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp)
+ tmp_prob_rate = i;
+ } else {
+ if (mr->probability >= mi->r[tmp_prob_rate].probability)
+ tmp_prob_rate = i;
}
}
- max_tp = 0;
- for (i = 0; i < mi->n_rates; i++) {
- struct minstrel_rate *mr = &mi->r[i];
-
- if (i == index_max_tp)
- continue;
+ /* Assign the new rate set */
+ memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
+ mi->max_prob_rate = tmp_prob_rate;
- if (max_tp < mr->cur_tp) {
- index_max_tp2 = i;
- max_tp = mr->cur_tp;
- }
- }
- mi->max_tp_rate = index_max_tp;
- mi->max_tp_rate2 = index_max_tp2;
- mi->max_prob_rate = index_max_prob;
+ /* Reset update timer */
+ mi->stats_update = jiffies;
}
static void
@@ -207,10 +221,10 @@ static int
minstrel_get_next_sample(struct minstrel_sta_info *mi)
{
unsigned int sample_ndx;
- sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column);
- mi->sample_idx++;
- if ((int) mi->sample_idx > (mi->n_rates - 2)) {
- mi->sample_idx = 0;
+ sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column);
+ mi->sample_row++;
+ if ((int) mi->sample_row >= mi->n_rates) {
+ mi->sample_row = 0;
mi->sample_column++;
if (mi->sample_column >= SAMPLE_COLUMNS)
mi->sample_column = 0;
@@ -228,31 +242,37 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
struct minstrel_priv *mp = priv;
struct ieee80211_tx_rate *ar = info->control.rates;
unsigned int ndx, sample_ndx = 0;
- bool mrr;
- bool sample_slower = false;
- bool sample = false;
+ bool mrr_capable;
+ bool indirect_rate_sampling = false;
+ bool rate_sampling = false;
int i, delta;
int mrr_ndx[3];
- int sample_rate;
+ int sampling_ratio;
+ /* management/no-ack frames do not use rate control */
if (rate_control_send_low(sta, priv_sta, txrc))
return;
- mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
-
- ndx = mi->max_tp_rate;
-
- if (mrr)
- sample_rate = mp->lookaround_rate_mrr;
+ /* check multi-rate-retry capabilities & adjust lookaround_rate */
+ mrr_capable = mp->has_mrr &&
+ !txrc->rts &&
+ !txrc->bss_conf->use_cts_prot;
+ if (mrr_capable)
+ sampling_ratio = mp->lookaround_rate_mrr;
else
- sample_rate = mp->lookaround_rate;
+ sampling_ratio = mp->lookaround_rate;
+
+ /* init rateindex [ndx] with max throughput rate */
+ ndx = mi->max_tp_rate[0];
+ /* increase sum packet counter */
mi->packet_count++;
- delta = (mi->packet_count * sample_rate / 100) -
+
+ delta = (mi->packet_count * sampling_ratio / 100) -
(mi->sample_count + mi->sample_deferred / 2);
/* delta > 0: sampling required */
- if ((delta > 0) && (mrr || !mi->prev_sample)) {
+ if ((delta > 0) && (mrr_capable || !mi->prev_sample)) {
struct minstrel_rate *msr;
if (mi->packet_count >= 10000) {
mi->sample_deferred = 0;
@@ -271,21 +291,28 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
mi->sample_count += (delta - mi->n_rates * 2);
}
+ /* get next random rate sample */
sample_ndx = minstrel_get_next_sample(mi);
msr = &mi->r[sample_ndx];
- sample = true;
- sample_slower = mrr && (msr->perfect_tx_time >
- mi->r[ndx].perfect_tx_time);
-
- if (!sample_slower) {
+ rate_sampling = true;
+
+ /* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage)
+ * rate sampling method should be used.
+ * Respect such rates that are not sampled for 20 interations.
+ */
+ if (mrr_capable &&
+ msr->perfect_tx_time > mi->r[ndx].perfect_tx_time &&
+ msr->sample_skipped < 20)
+ indirect_rate_sampling = true;
+
+ if (!indirect_rate_sampling) {
if (msr->sample_limit != 0) {
ndx = sample_ndx;
mi->sample_count++;
if (msr->sample_limit > 0)
msr->sample_limit--;
- } else {
- sample = false;
- }
+ } else
+ rate_sampling = false;
} else {
/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
* packets that have the sampling rate deferred to the
@@ -297,34 +324,39 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
mi->sample_deferred++;
}
}
- mi->prev_sample = sample;
+ mi->prev_sample = rate_sampling;
/* If we're not using MRR and the sampling rate already
* has a probability of >95%, we shouldn't be attempting
* to use it, as this only wastes precious airtime */
- if (!mrr && sample && (mi->r[ndx].probability > 17100))
- ndx = mi->max_tp_rate;
+ if (!mrr_capable && rate_sampling &&
+ (mi->r[ndx].probability > MINSTREL_FRAC(95, 100)))
+ ndx = mi->max_tp_rate[0];
+ /* mrr setup for 1st stage */
ar[0].idx = mi->r[ndx].rix;
ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info);
- if (!mrr) {
- if (!sample)
+ /* non mrr setup for 2nd stage */
+ if (!mrr_capable) {
+ if (!rate_sampling)
ar[0].count = mp->max_retry;
ar[1].idx = mi->lowest_rix;
ar[1].count = mp->max_retry;
return;
}
- /* MRR setup */
- if (sample) {
- if (sample_slower)
+ /* mrr setup for 2nd stage */
+ if (rate_sampling) {
+ if (indirect_rate_sampling)
mrr_ndx[0] = sample_ndx;
else
- mrr_ndx[0] = mi->max_tp_rate;
+ mrr_ndx[0] = mi->max_tp_rate[0];
} else {
- mrr_ndx[0] = mi->max_tp_rate2;
+ mrr_ndx[0] = mi->max_tp_rate[1];
}
+
+ /* mrr setup for 3rd & 4th stage */
mrr_ndx[1] = mi->max_prob_rate;
mrr_ndx[2] = 0;
for (i = 1; i < 4; i++) {
@@ -351,26 +383,21 @@ static void
init_sample_table(struct minstrel_sta_info *mi)
{
unsigned int i, col, new_idx;
- unsigned int n_srates = mi->n_rates - 1;
u8 rnd[8];
mi->sample_column = 0;
- mi->sample_idx = 0;
- memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates);
+ mi->sample_row = 0;
+ memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);
for (col = 0; col < SAMPLE_COLUMNS; col++) {
- for (i = 0; i < n_srates; i++) {
+ for (i = 0; i < mi->n_rates; i++) {
get_random_bytes(rnd, sizeof(rnd));
- new_idx = (i + rnd[i & 7]) % n_srates;
+ new_idx = (i + rnd[i & 7]) % mi->n_rates;
- while (SAMPLE_TBL(mi, new_idx, col) != 0)
- new_idx = (new_idx + 1) % n_srates;
+ while (SAMPLE_TBL(mi, new_idx, col) != 0xff)
+ new_idx = (new_idx + 1) % mi->n_rates;
- /* Don't sample the slowest rate (i.e. slowest base
- * rate). We must presume that the slowest rate works
- * fine, or else other management frames will also be
- * failing and the link will break */
- SAMPLE_TBL(mi, new_idx, col) = i + 1;
+ SAMPLE_TBL(mi, new_idx, col) = i;
}
}
}
@@ -542,9 +569,6 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
mp->lookaround_rate = 5;
mp->lookaround_rate_mrr = 10;
- /* moving average weight for EWMA */
- mp->ewma_level = 75;
-
/* maximum time that the hw is allowed to stay in one MRR segment */
mp->segment_size = 6000;
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 5ecf757..85ebf42 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -9,6 +9,28 @@
#ifndef __RC_MINSTREL_H
#define __RC_MINSTREL_H
+#define EWMA_LEVEL 75 /* ewma weighting factor [%] */
+#define SAMPLE_COLUMNS 10 /* number of columns in sample table */
+
+
+/* scaled fraction values */
+#define MINSTREL_SCALE 16
+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
+#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+
+/* number of highest throughput rates to consider*/
+#define MAX_THR_RATES 4
+
+/*
+ * Perform EWMA (Exponentially Weighted Moving Average) calculation
+ */
+static inline int
+minstrel_ewma(int old, int new, int weight)
+{
+ return (new * (100 - weight) + old * weight) / 100;
+}
+
+
struct minstrel_rate {
int bitrate;
int rix;
@@ -26,6 +48,7 @@ struct minstrel_rate {
u32 attempts;
u32 last_attempts;
u32 last_success;
+ u8 sample_skipped;
/* parts per thousand */
u32 cur_prob;
@@ -45,14 +68,13 @@ struct minstrel_sta_info {
unsigned int lowest_rix;
- unsigned int max_tp_rate;
- unsigned int max_tp_rate2;
- unsigned int max_prob_rate;
+ u8 max_tp_rate[MAX_THR_RATES];
+ u8 max_prob_rate;
unsigned int packet_count;
unsigned int sample_count;
int sample_deferred;
- unsigned int sample_idx;
+ unsigned int sample_row;
unsigned int sample_column;
int n_rates;
@@ -73,7 +95,6 @@ struct minstrel_priv {
unsigned int cw_min;
unsigned int cw_max;
unsigned int max_retry;
- unsigned int ewma_level;
unsigned int segment_size;
unsigned int update_interval;
unsigned int lookaround_rate;
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
index d5a5622..d104834 100644
--- a/net/mac80211/rc80211_minstrel_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -73,15 +73,17 @@ minstrel_stats_open(struct inode *inode, struct file *file)
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
- *(p++) = (i == mi->max_tp_rate) ? 'T' : ' ';
- *(p++) = (i == mi->max_tp_rate2) ? 't' : ' ';
+ *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
+ *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
+ *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
+ *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
p += sprintf(p, "%3u%s", mr->bitrate / 2,
(mr->bitrate & 1 ? ".5" : " "));
- tp = mr->cur_tp / ((18000 << 10) / 96);
- prob = mr->cur_prob / 18;
- eprob = mr->probability / 18;
+ tp = MINSTREL_TRUNC(mr->cur_tp / 10);
+ prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
+ eprob = MINSTREL_TRUNC(mr->probability * 1000);
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
"%3u(%3u) %8llu %8llu\n",
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 3af141c..749552b 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -17,8 +17,6 @@
#include "rc80211_minstrel_ht.h"
#define AVG_PKT_SIZE 1200
-#define SAMPLE_COLUMNS 10
-#define EWMA_LEVEL 75
/* Number of bits for an average sized packet */
#define MCS_NBITS (AVG_PKT_SIZE << 3)
@@ -26,11 +24,11 @@
/* Number of symbols for a packet with (bps) bits per symbol */
#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps))
-/* Transmission time for a packet containing (syms) symbols */
+/* Transmission time (nanoseconds) for a packet containing (syms) symbols */
#define MCS_SYMBOL_TIME(sgi, syms) \
(sgi ? \
- ((syms) * 18 + 4) / 5 : /* syms * 3.6 us */ \
- (syms) << 2 /* syms * 4 us */ \
+ ((syms) * 18000 + 4000) / 5 : /* syms * 3.6 us */ \
+ ((syms) * 1000) << 2 /* syms * 4 us */ \
)
/* Transmit duration for the raw data part of an average sized packet */
@@ -64,9 +62,9 @@
}
#define CCK_DURATION(_bitrate, _short, _len) \
- (10 /* SIFS */ + \
+ (1000 * (10 /* SIFS */ + \
(_short ? 72 + 24 : 144 + 48 ) + \
- (8 * (_len + 4) * 10) / (_bitrate))
+ (8 * (_len + 4) * 10) / (_bitrate)))
#define CCK_ACK_DURATION(_bitrate, _short) \
(CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \
@@ -129,15 +127,6 @@ const struct mcs_group minstrel_mcs_groups[] = {
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
/*
- * Perform EWMA (Exponentially Weighted Moving Average) calculation
- */
-static int
-minstrel_ewma(int old, int new, int weight)
-{
- return (new * (100 - weight) + old * weight) / 100;
-}
-
-/*
* Look up an MCS group index based on mac80211 rate information
*/
static int
@@ -211,7 +200,8 @@ static void
minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
{
struct minstrel_rate_stats *mr;
- unsigned int usecs = 0;
+ unsigned int nsecs = 0;
+ unsigned int tp;
mr = &mi->groups[group].rates[rate];
@@ -221,10 +211,12 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
}
if (group != MINSTREL_CCK_GROUP)
- usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+ nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
- usecs += minstrel_mcs_groups[group].duration[rate];
- mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability);
+ nsecs += minstrel_mcs_groups[group].duration[rate];
+ tp = 1000000 * ((mr->probability * 1000) / nsecs);
+
+ mr->cur_tp = MINSTREL_TRUNC(tp);
}
/*
@@ -308,8 +300,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
}
}
- /* try to sample up to half of the available rates during each interval */
- mi->sample_count *= 4;
+ /* try to sample all available rates during each interval */
+ mi->sample_count *= 8;
cur_prob = 0;
cur_prob_tp = 0;
@@ -320,20 +312,13 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!mg->supported)
continue;
- mr = minstrel_get_ratestats(mi, mg->max_prob_rate);
- if (cur_prob_tp < mr->cur_tp &&
- minstrel_mcs_groups[group].streams == 1) {
- mi->max_prob_rate = mg->max_prob_rate;
- cur_prob = mr->cur_prob;
- cur_prob_tp = mr->cur_tp;
- }
-
mr = minstrel_get_ratestats(mi, mg->max_tp_rate);
if (cur_tp < mr->cur_tp) {
mi->max_tp_rate2 = mi->max_tp_rate;
cur_tp2 = cur_tp;
mi->max_tp_rate = mg->max_tp_rate;
cur_tp = mr->cur_tp;
+ mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1;
}
mr = minstrel_get_ratestats(mi, mg->max_tp_rate2);
@@ -343,6 +328,23 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
}
}
+ if (mi->max_prob_streams < 1)
+ mi->max_prob_streams = 1;
+
+ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+ mg = &mi->groups[group];
+ if (!mg->supported)
+ continue;
+ mr = minstrel_get_ratestats(mi, mg->max_prob_rate);
+ if (cur_prob_tp < mr->cur_tp &&
+ minstrel_mcs_groups[group].streams <= mi->max_prob_streams) {
+ mi->max_prob_rate = mg->max_prob_rate;
+ cur_prob = mr->cur_prob;
+ cur_prob_tp = mr->cur_tp;
+ }
+ }
+
+
mi->stats_update = jiffies;
}
@@ -467,7 +469,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {
mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len);
- mi->sample_tries = 2;
+ mi->sample_tries = 1;
mi->sample_count--;
}
@@ -536,7 +538,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
mr->retry_updated = true;
group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
- tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len;
+ tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;
/* Contention time for first 2 tries */
ctime = (t_slot * cw) >> 1;
@@ -616,6 +618,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
struct minstrel_rate_stats *mr;
struct minstrel_mcs_group_data *mg;
+ unsigned int sample_dur, sample_group;
int sample_idx = 0;
if (mi->sample_wait > 0) {
@@ -626,11 +629,11 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!mi->sample_tries)
return -1;
- mi->sample_tries--;
mg = &mi->groups[mi->sample_group];
sample_idx = sample_table[mg->column][mg->index];
mr = &mg->rates[sample_idx];
- sample_idx += mi->sample_group * MCS_GROUP_RATES;
+ sample_group = mi->sample_group;
+ sample_idx += sample_group * MCS_GROUP_RATES;
minstrel_next_sample_idx(mi);
/*
@@ -651,14 +654,18 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
* Make sure that lower rates get sampled only occasionally,
* if the link is working perfectly.
*/
- if (minstrel_get_duration(sample_idx) >
- minstrel_get_duration(mi->max_tp_rate)) {
+ sample_dur = minstrel_get_duration(sample_idx);
+ if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) &&
+ (mi->max_prob_streams <
+ minstrel_mcs_groups[sample_group].streams ||
+ sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
if (mr->sample_skipped < 20)
return -1;
if (mi->sample_slow++ > 2)
return -1;
}
+ mi->sample_tries--;
return sample_idx;
}
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index 302dbd5..9b16e9d 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -16,11 +16,6 @@
#define MINSTREL_MAX_STREAMS 3
#define MINSTREL_STREAM_GROUPS 4
-/* scaled fraction values */
-#define MINSTREL_SCALE 16
-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
-
#define MCS_GROUP_RATES 8
struct mcs_group {
@@ -85,6 +80,7 @@ struct minstrel_ht_sta {
/* best probability rate */
unsigned int max_prob_rate;
+ unsigned int max_prob_streams;
/* time of last status update */
unsigned long stats_update;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index bb73ed2d..5b4492a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -648,24 +648,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-#define SEQ_MODULO 0x1000
-#define SEQ_MASK 0xfff
-
-static inline int seq_less(u16 sq1, u16 sq2)
-{
- return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
-}
-
-static inline u16 seq_inc(u16 sq)
-{
- return (sq + 1) & SEQ_MASK;
-}
-
-static inline u16 seq_sub(u16 sq1, u16 sq2)
-{
- return (sq1 - sq2) & SEQ_MASK;
-}
-
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
int index,
@@ -687,7 +669,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
__skb_queue_tail(frames, skb);
no_frame:
- tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+ tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
}
static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata,
@@ -699,8 +681,9 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
lockdep_assert_held(&tid_agg_rx->reorder_lock);
- while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
- index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+ while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) {
+ index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+ tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
frames);
@@ -727,8 +710,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
lockdep_assert_held(&tid_agg_rx->reorder_lock);
/* release the buffer until next missing frame */
- index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
- tid_agg_rx->buf_size;
+ index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+ tid_agg_rx->ssn) % tid_agg_rx->buf_size;
if (!tid_agg_rx->reorder_buf[index] &&
tid_agg_rx->stored_mpdu_num) {
/*
@@ -756,19 +739,22 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
* Increment the head seq# also for the skipped slots.
*/
tid_agg_rx->head_seq_num =
- (tid_agg_rx->head_seq_num + skipped) & SEQ_MASK;
+ (tid_agg_rx->head_seq_num +
+ skipped) & IEEE80211_SN_MASK;
skipped = 0;
}
} else while (tid_agg_rx->reorder_buf[index]) {
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
frames);
- index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+ index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+ tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
}
if (tid_agg_rx->stored_mpdu_num) {
- j = index = seq_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+ j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
+ tid_agg_rx->ssn) %
+ tid_agg_rx->buf_size;
for (; j != (index - 1) % tid_agg_rx->buf_size;
j = (j + 1) % tid_agg_rx->buf_size) {
@@ -809,7 +795,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
head_seq_num = tid_agg_rx->head_seq_num;
/* frame with out of date sequence number */
- if (seq_less(mpdu_seq_num, head_seq_num)) {
+ if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {
dev_kfree_skb(skb);
goto out;
}
@@ -818,8 +804,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
* If frame the sequence number exceeds our buffering window
* size release some previous frames to make room for this one.
*/
- if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
- head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
+ if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) {
+ head_seq_num = ieee80211_sn_inc(
+ ieee80211_sn_sub(mpdu_seq_num, buf_size));
/* release stored frames up to new head to stack */
ieee80211_release_reorder_frames(sdata, tid_agg_rx,
head_seq_num, frames);
@@ -827,7 +814,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
/* Now the new frame is always in the range of the reordering buffer */
- index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+ index = ieee80211_sn_sub(mpdu_seq_num,
+ tid_agg_rx->ssn) % tid_agg_rx->buf_size;
/* check if we already stored this frame */
if (tid_agg_rx->reorder_buf[index]) {
@@ -843,7 +831,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
*/
if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
tid_agg_rx->stored_mpdu_num == 0) {
- tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+ tid_agg_rx->head_seq_num =
+ ieee80211_sn_inc(tid_agg_rx->head_seq_num);
ret = false;
goto out;
}
@@ -1894,8 +1883,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
* 'align' will only take the values 0 or 2 here
* since all frames are required to be aligned
* to 2-byte boundaries when being passed to
- * mac80211. That also explains the __skb_push()
- * below.
+ * mac80211; the code here works just as well if
+ * that isn't true, but mac80211 assumes it can
+ * access fields as 2-byte aligned (e.g. for
+ * compare_ether_addr)
*/
align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
if (align) {
@@ -2552,7 +2543,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
case WLAN_SP_MESH_PEERING_CONFIRM:
if (!ieee80211_vif_is_mesh(&sdata->vif))
goto invalid;
- if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+ if (sdata->u.mesh.user_mpm)
/* userspace handles this frame */
break;
goto queue;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a79ce82..3644ad7 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -342,6 +342,11 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
+#ifdef CONFIG_MAC80211_MESH
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ !sdata->u.mesh.user_mpm)
+ init_timer(&sta->plink_timer);
+#endif
memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->local = local;
@@ -794,9 +799,11 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
mutex_lock(&local->key_mtx);
for (i = 0; i < NUM_DEFAULT_KEYS; i++)
- __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i]));
+ __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i]),
+ true);
if (sta->ptk)
- __ieee80211_key_free(key_mtx_dereference(local, sta->ptk));
+ __ieee80211_key_free(key_mtx_dereference(local, sta->ptk),
+ true);
mutex_unlock(&local->key_mtx);
sta->dead = true;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 4947341..e5868c3 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -281,7 +281,6 @@ struct sta_ampdu_mlme {
* @plink_state: peer link state
* @plink_timeout: timeout of peer link
* @plink_timer: peer link watch timer
- * @plink_timer_was_running: used by suspend/resume to restore timers
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
@@ -379,7 +378,6 @@ struct sta_info {
__le16 reason;
u8 plink_retries;
bool ignore_plink_timer;
- bool plink_timer_was_running;
enum nl80211_plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 3d7cd2a..e7db2b8 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1042,15 +1042,17 @@ TRACE_EVENT(drv_remain_on_channel,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *chan,
- unsigned int duration),
+ unsigned int duration,
+ enum ieee80211_roc_type type),
- TP_ARGS(local, sdata, chan, duration),
+ TP_ARGS(local, sdata, chan, duration, type),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(int, center_freq)
__field(unsigned int, duration)
+ __field(u32, type)
),
TP_fast_assign(
@@ -1058,12 +1060,13 @@ TRACE_EVENT(drv_remain_on_channel,
VIF_ASSIGN;
__entry->center_freq = chan->center_freq;
__entry->duration = duration;
+ __entry->type = type;
),
TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms",
+ LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms type=%d",
LOCAL_PR_ARG, VIF_PR_ARG,
- __entry->center_freq, __entry->duration
+ __entry->center_freq, __entry->duration, __entry->type
)
);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index de8548b..8914d2d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1231,34 +1231,40 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
if (local->queue_stop_reasons[q] ||
(!txpending && !skb_queue_empty(&local->pending[q]))) {
if (unlikely(info->flags &
- IEEE80211_TX_INTFL_OFFCHAN_TX_OK &&
- local->queue_stop_reasons[q] &
- ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) {
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK)) {
+ if (local->queue_stop_reasons[q] &
+ ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL)) {
+ /*
+ * Drop off-channel frames if queues
+ * are stopped for any reason other
+ * than off-channel operation. Never
+ * queue them.
+ */
+ spin_unlock_irqrestore(
+ &local->queue_stop_reason_lock,
+ flags);
+ ieee80211_purge_tx_queue(&local->hw,
+ skbs);
+ return true;
+ }
+ } else {
+
/*
- * Drop off-channel frames if queues are stopped
- * for any reason other than off-channel
- * operation. Never queue them.
+ * Since queue is stopped, queue up frames for
+ * later transmission from the tx-pending
+ * tasklet when the queue is woken again.
*/
- spin_unlock_irqrestore(
- &local->queue_stop_reason_lock, flags);
- ieee80211_purge_tx_queue(&local->hw, skbs);
- return true;
+ if (txpending)
+ skb_queue_splice_init(skbs,
+ &local->pending[q]);
+ else
+ skb_queue_splice_tail_init(skbs,
+ &local->pending[q]);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+ return false;
}
-
- /*
- * Since queue is stopped, queue up frames for later
- * transmission from the tx-pending tasklet when the
- * queue is woken again.
- */
- if (txpending)
- skb_queue_splice_init(skbs, &local->pending[q]);
- else
- skb_queue_splice_tail_init(skbs,
- &local->pending[q]);
-
- spin_unlock_irqrestore(&local->queue_stop_reason_lock,
- flags);
- return false;
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -1844,9 +1850,24 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
}
if (!is_multicast_ether_addr(skb->data)) {
+ struct sta_info *next_hop;
+ bool mpp_lookup = true;
+
mpath = mesh_path_lookup(sdata, skb->data);
- if (!mpath)
+ if (mpath) {
+ mpp_lookup = false;
+ next_hop = rcu_dereference(mpath->next_hop);
+ if (!next_hop ||
+ !(mpath->flags & (MESH_PATH_ACTIVE |
+ MESH_PATH_RESOLVING)))
+ mpp_lookup = true;
+ }
+
+ if (mpp_lookup)
mppath = mpp_path_lookup(sdata, skb->data);
+
+ if (mppath && mpath)
+ mesh_path_del(mpath->sdata, mpath->dst);
}
/*
@@ -2350,9 +2371,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
if (local->tim_in_locked_section) {
__ieee80211_beacon_add_tim(sdata, ps, skb);
} else {
- spin_lock(&local->tim_lock);
+ spin_lock_bh(&local->tim_lock);
__ieee80211_beacon_add_tim(sdata, ps, skb);
- spin_unlock(&local->tim_lock);
+ spin_unlock_bh(&local->tim_lock);
}
return 0;
@@ -2724,7 +2745,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
- sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
dev_kfree_skb_any(skb);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0f38f43..b7a856e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1357,6 +1357,25 @@ void ieee80211_stop_device(struct ieee80211_local *local)
drv_stop(local);
}
+static void ieee80211_assign_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+
+ if (!local->use_chanctx)
+ return;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+}
+
int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
@@ -1445,36 +1464,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
list_for_each_entry(sdata, &local->interfaces, list) {
- struct ieee80211_chanctx_conf *ctx_conf;
-
if (!ieee80211_sdata_running(sdata))
continue;
-
- mutex_lock(&local->chanctx_mtx);
- ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (ctx_conf) {
- ctx = container_of(ctx_conf, struct ieee80211_chanctx,
- conf);
- drv_assign_vif_chanctx(local, sdata, ctx);
- }
- mutex_unlock(&local->chanctx_mtx);
+ ieee80211_assign_chanctx(local, sdata);
}
sdata = rtnl_dereference(local->monitor_sdata);
- if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
- struct ieee80211_chanctx_conf *ctx_conf;
-
- mutex_lock(&local->chanctx_mtx);
- ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (ctx_conf) {
- ctx = container_of(ctx_conf, struct ieee80211_chanctx,
- conf);
- drv_assign_vif_chanctx(local, sdata, ctx);
- }
- mutex_unlock(&local->chanctx_mtx);
- }
+ if (sdata && ieee80211_sdata_running(sdata))
+ ieee80211_assign_chanctx(local, sdata);
/* add STAs back */
mutex_lock(&local->sta_mtx);
@@ -1534,11 +1531,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_IDLE |
BSS_CHANGED_TXPOWER;
-#ifdef CONFIG_PM
- if (local->resuming && !reconfig_due_to_wowlan)
- sdata->vif.bss_conf = sdata->suspend_bss_conf;
-#endif
-
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
changed |= BSS_CHANGED_ASSOC |
@@ -1678,28 +1670,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mb();
local->resuming = false;
- list_for_each_entry(sdata, &local->interfaces, list) {
- switch(sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- ieee80211_sta_restart(sdata);
- break;
- case NL80211_IFTYPE_ADHOC:
- ieee80211_ibss_restart(sdata);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- ieee80211_mesh_restart(sdata);
- break;
- default:
- break;
- }
- }
-
mod_timer(&local->sta_cleanup, jiffies + 1);
-
- mutex_lock(&local->sta_mtx);
- list_for_each_entry(sta, &local->sta_list, list)
- mesh_plink_restart(sta);
- mutex_unlock(&local->sta_mtx);
#else
WARN_ON(1);
#endif
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index a2c2258..171344d 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -13,6 +13,104 @@
#include "rate.h"
+static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_vht_cap *vht_cap,
+ u32 flag)
+{
+ __le32 le_flag = cpu_to_le32(flag);
+
+ if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag &&
+ !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag))
+ vht_cap->cap &= ~flag;
+}
+
+void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_vht_cap *vht_cap)
+{
+ int i;
+ u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n;
+
+ if (!vht_cap->vht_supported)
+ return;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
+
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_RXLDPC);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_SHORT_GI_80);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_SHORT_GI_160);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_TXSTBC);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN);
+ __check_vhtcap_disable(sdata, vht_cap,
+ IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN);
+
+ /* Allow user to decrease AMPDU length exponent */
+ if (sdata->u.mgd.vht_capa_mask.vht_cap_info &
+ cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) {
+ u32 cap, n;
+
+ n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+ if (n < cap) {
+ vht_cap->cap &=
+ ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ vht_cap->cap |=
+ n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ }
+ }
+
+ /* Allow the user to decrease MCSes */
+ rxmcs_mask =
+ le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map);
+ rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map);
+ rxmcs_n &= rxmcs_mask;
+ rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+
+ txmcs_mask =
+ le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map);
+ txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map);
+ txmcs_n &= txmcs_mask;
+ txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+ for (i = 0; i < 8; i++) {
+ u8 m, n, c;
+
+ m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
+ n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
+ rxmcs_cap &= ~(3 << 2*i);
+ rxmcs_cap |= (rxmcs_n & (3 << 2*i));
+ }
+
+ m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
+ n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
+ txmcs_cap &= ~(3 << 2*i);
+ txmcs_cap |= (txmcs_n & (3 << 2*i));
+ }
+ }
+ vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap);
+ vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap);
+}
+
void
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
@@ -20,6 +118,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
+ struct ieee80211_sta_vht_cap own_cap;
+ u32 cap_info, i;
memset(vht_cap, 0, sizeof(*vht_cap));
@@ -35,12 +135,122 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
vht_cap->vht_supported = true;
- vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
+ own_cap = sband->vht_cap;
+ /*
+ * If user has specified capability overrides, take care
+ * of that if the station we're setting up is the AP that
+ * we advertised a restricted capability set to. Override
+ * our own capabilities and then use those below.
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ ieee80211_apply_vhtcap_overrides(sdata, &own_cap);
+
+ /* take some capabilities as-is */
+ cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info);
+ vht_cap->cap = cap_info;
+ vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 |
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_VHT_TXOP_PS |
+ IEEE80211_VHT_CAP_HTC_VHT |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+ IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB |
+ IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB |
+ IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
+ IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+ /* and some based on our own capabilities */
+ switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ vht_cap->cap |= cap_info &
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ break;
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+ vht_cap->cap |= cap_info &
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+
+ /* symmetric capabilities */
+ vht_cap->cap |= cap_info & own_cap.cap &
+ (IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_SHORT_GI_160);
+
+ /* remaining ones */
+ if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
+ vht_cap->cap |= cap_info &
+ (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
+ }
+
+ if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
+ vht_cap->cap |= cap_info &
+ IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+
+ if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
+ vht_cap->cap |= cap_info &
+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+ if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
+ vht_cap->cap |= cap_info &
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK;
+
+ if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
+ vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC;
/* Copy peer MCS info, the driver might need them. */
memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
sizeof(struct ieee80211_vht_mcs_info));
+ /* but also restrict MCSes */
+ for (i = 0; i < 8; i++) {
+ u16 own_rx, own_tx, peer_rx, peer_tx;
+
+ own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map);
+ own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map);
+ own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+ peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+ peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+
+ if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ else if (own_rx < peer_tx)
+ peer_tx = own_rx;
+ }
+
+ if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ else if (own_tx < peer_rx)
+ peer_rx = own_tx;
+ }
+
+ vht_cap->vht_mcs.rx_mcs_map &=
+ ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
+ vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2);
+
+ vht_cap->vht_mcs.tx_mcs_map &=
+ ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
+ vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2);
+ }
+
+ /* finally set up the bandwidth */
switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c
index 4b5ab21..d11ac79 100644
--- a/net/rfkill/rfkill-regulator.c
+++ b/net/rfkill/rfkill-regulator.c
@@ -51,7 +51,7 @@ static int rfkill_regulator_set_block(void *data, bool blocked)
return 0;
}
-struct rfkill_ops rfkill_regulator_ops = {
+static struct rfkill_ops rfkill_regulator_ops = {
.set_block = rfkill_regulator_set_block,
};
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index a4a14e8..324e8d8 100644
--- a/net/wireless/ap.c
+++ b/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/net/wireless/core.c b/net/wireless/core.c
index 5ffff03..f382cae 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -367,8 +367,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
- rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH |
- NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+ rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
return &rdev->wiphy;
}
@@ -815,6 +814,46 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
rdev->num_running_monitor_ifaces += num;
}
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ struct net_device *dev = wdev->netdev;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_leave_ibss(rdev, dev, true);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ mutex_lock(&rdev->sched_scan_mtx);
+ __cfg80211_stop_sched_scan(rdev, false);
+ mutex_unlock(&rdev->sched_scan_mtx);
+
+ wdev_lock(wdev);
+#ifdef CONFIG_CFG80211_WEXT
+ kfree(wdev->wext.ie);
+ wdev->wext.ie = NULL;
+ wdev->wext.ie_len = 0;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+ __cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ cfg80211_mlme_down(rdev, dev);
+ wdev_unlock(wdev);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ cfg80211_leave_mesh(rdev, dev);
+ break;
+ case NL80211_IFTYPE_AP:
+ cfg80211_stop_ap(rdev, dev);
+ break;
+ default:
+ break;
+ }
+
+ wdev->beacon_interval = 0;
+}
+
static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
unsigned long state,
void *ndev)
@@ -883,38 +922,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
dev->priv_flags |= IFF_DONT_BRIDGE;
break;
case NETDEV_GOING_DOWN:
- switch (wdev->iftype) {
- case NL80211_IFTYPE_ADHOC:
- cfg80211_leave_ibss(rdev, dev, true);
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_STATION:
- mutex_lock(&rdev->sched_scan_mtx);
- __cfg80211_stop_sched_scan(rdev, false);
- mutex_unlock(&rdev->sched_scan_mtx);
-
- wdev_lock(wdev);
-#ifdef CONFIG_CFG80211_WEXT
- kfree(wdev->wext.ie);
- wdev->wext.ie = NULL;
- wdev->wext.ie_len = 0;
- wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
-#endif
- __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, true);
- cfg80211_mlme_down(rdev, dev);
- wdev_unlock(wdev);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- cfg80211_leave_mesh(rdev, dev);
- break;
- case NL80211_IFTYPE_AP:
- cfg80211_stop_ap(rdev, dev);
- break;
- default:
- break;
- }
- wdev->beacon_interval = 0;
+ cfg80211_leave(rdev, wdev);
break;
case NETDEV_DOWN:
cfg80211_update_iface_num(rdev, wdev->iftype, -1);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 3aec0e4..d5d06fd 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -330,20 +330,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
- const u8 *bssid, const u8 *prev_bssid,
+ const u8 *bssid,
const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt,
- u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
- struct ieee80211_ht_cap *ht_capa_mask);
+ struct cfg80211_assoc_request *req);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev, struct ieee80211_channel *chan,
- const u8 *bssid, const u8 *prev_bssid,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid,
const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt,
- u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
- struct ieee80211_ht_cap *ht_capa_mask);
+ struct cfg80211_assoc_request *req);
int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
@@ -375,6 +370,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
bool no_cck, bool dont_wait_for_ack, u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+ const struct ieee80211_vht_cap *vht_capa_mask);
/* SME */
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
@@ -503,6 +500,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num);
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 55957a2..0bb93f3 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -85,6 +85,7 @@ const struct mesh_setup default_mesh_setup = {
.ie = NULL,
.ie_len = 0,
.is_secure = false,
+ .user_mpm = false,
.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
.dtim_period = MESH_DEFAULT_DTIM_PERIOD,
};
@@ -233,20 +234,6 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
return 0;
}
-void cfg80211_notify_new_peer_candidate(struct net_device *dev,
- const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
-
- trace_cfg80211_notify_new_peer_candidate(dev, macaddr);
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
- return;
-
- nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev,
- macaddr, ie, ie_len, gfp);
-}
-EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
-
static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index caddca3..390198b 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -187,30 +187,6 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
}
EXPORT_SYMBOL(cfg80211_send_disassoc);
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
- size_t len)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_send_unprot_deauth(dev);
- nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
-
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
- size_t len)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_send_unprot_disassoc(dev);
- nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
-
void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -367,27 +343,38 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
p1[i] &= p2[i];
}
+/* Do a logical ht_capa &= ht_capa_mask. */
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+ const struct ieee80211_vht_cap *vht_capa_mask)
+{
+ int i;
+ u8 *p1, *p2;
+ if (!vht_capa_mask) {
+ memset(vht_capa, 0, sizeof(*vht_capa));
+ return;
+ }
+
+ p1 = (u8*)(vht_capa);
+ p2 = (u8*)(vht_capa_mask);
+ for (i = 0; i < sizeof(*vht_capa); i++)
+ p1[i] &= p2[i];
+}
+
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
- const u8 *bssid, const u8 *prev_bssid,
+ const u8 *bssid,
const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt,
- u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
- struct ieee80211_ht_cap *ht_capa_mask)
+ struct cfg80211_assoc_request *req)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct cfg80211_assoc_request req;
int err;
bool was_connected = false;
ASSERT_WDEV_LOCK(wdev);
- memset(&req, 0, sizeof(req));
-
- if (wdev->current_bss && prev_bssid &&
- ether_addr_equal(wdev->current_bss->pub.bssid, prev_bssid)) {
+ if (wdev->current_bss && req->prev_bssid &&
+ ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
/*
* Trying to reassociate: Allow this to proceed and let the old
* association to be dropped when the new one is completed.
@@ -399,40 +386,30 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
} else if (wdev->current_bss)
return -EALREADY;
- req.ie = ie;
- req.ie_len = ie_len;
- memcpy(&req.crypto, crypt, sizeof(req.crypto));
- req.use_mfp = use_mfp;
- req.prev_bssid = prev_bssid;
- req.flags = assoc_flags;
- if (ht_capa)
- memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
- if (ht_capa_mask)
- memcpy(&req.ht_capa_mask, ht_capa_mask,
- sizeof(req.ht_capa_mask));
- cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
+ cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
rdev->wiphy.ht_capa_mod_mask);
+ cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
+ rdev->wiphy.vht_capa_mod_mask);
- req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
- WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
- if (!req.bss) {
+ req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+ if (!req->bss) {
if (was_connected)
wdev->sme_state = CFG80211_SME_CONNECTED;
return -ENOENT;
}
- err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
- CHAN_MODE_SHARED);
+ err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
if (err)
goto out;
- err = rdev_assoc(rdev, dev, &req);
+ err = rdev_assoc(rdev, dev, req);
out:
if (err) {
if (was_connected)
wdev->sme_state = CFG80211_SME_CONNECTED;
- cfg80211_put_bss(&rdev->wiphy, req.bss);
+ cfg80211_put_bss(&rdev->wiphy, req->bss);
}
return err;
@@ -441,21 +418,17 @@ out:
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
- const u8 *bssid, const u8 *prev_bssid,
+ const u8 *bssid,
const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt,
- u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
- struct ieee80211_ht_cap *ht_capa_mask)
+ struct cfg80211_assoc_request *req)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev);
- err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
- ssid, ssid_len, ie, ie_len, use_mfp, crypt,
- assoc_flags, ht_capa, ht_capa_mask);
+ err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+ ssid, ssid_len, req);
wdev_unlock(wdev);
mutex_unlock(&rdev->devlist_mtx);
@@ -577,62 +550,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
}
}
-void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
- struct ieee80211_channel *chan,
- unsigned int duration, gfp_t gfp)
-{
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
- nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp);
-}
-EXPORT_SYMBOL(cfg80211_ready_on_channel);
-
-void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
- struct ieee80211_channel *chan,
- gfp_t gfp)
-{
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
- nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp);
-}
-EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
-
-void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
- struct station_info *sinfo, gfp_t gfp)
-{
- struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_new_sta(dev, mac_addr, sinfo);
- nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
-}
-EXPORT_SYMBOL(cfg80211_new_sta);
-
-void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
-{
- struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_del_sta(dev, mac_addr);
- nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
-}
-EXPORT_SYMBOL(cfg80211_del_sta);
-
-void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
- enum nl80211_connect_failed_reason reason,
- gfp_t gfp)
-{
- struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
-}
-EXPORT_SYMBOL(cfg80211_conn_failed);
-
struct cfg80211_mgmt_registration {
struct list_head list;
@@ -909,85 +826,6 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
}
EXPORT_SYMBOL(cfg80211_rx_mgmt);
-void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
- const u8 *buf, size_t len, bool ack, gfp_t gfp)
-{
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
-
- /* Indicate TX status of the Action frame to user space */
- nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp);
-}
-EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
-
- /* Indicate roaming trigger event to user space */
- nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
-
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
- /* Indicate roaming trigger event to user space */
- nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
-void cfg80211_cqm_txe_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets,
- u32 rate, u32 intvl, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets,
- rate, intvl, gfp);
-}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
-
-void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
- const u8 *replay_ctr, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_gtk_rekey_notify(dev, bssid);
- nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
-}
-EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
-
-void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
- const u8 *bssid, bool preauth, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
- nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
-}
-EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-
void cfg80211_dfs_channels_update_work(struct work_struct *work)
{
struct delayed_work *delayed_work;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 35545cc..f924d45 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -370,6 +370,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
+ [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
+ [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
+ .len = NL80211_VHT_CAPABILITY_LEN,
+ },
+ [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+ [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
};
/* policy for the key attributes */
@@ -539,7 +547,8 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
}
static int nl80211_msg_put_channel(struct sk_buff *msg,
- struct ieee80211_channel *chan)
+ struct ieee80211_channel *chan,
+ bool large)
{
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
chan->center_freq))
@@ -555,27 +564,36 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
goto nla_put_failure;
if (chan->flags & IEEE80211_CHAN_RADAR) {
- u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
- chan->dfs_state))
+ if (large) {
+ u32 time;
+
+ time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+ chan->dfs_state))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
+ time))
+ goto nla_put_failure;
+ }
+ }
+
+ if (large) {
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time))
+ if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
goto nla_put_failure;
}
- if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
- goto nla_put_failure;
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
DBM_TO_MBM(chan->max_power)))
@@ -851,7 +869,8 @@ nla_put_failure:
}
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
- struct sk_buff *msg)
+ struct sk_buff *msg,
+ bool large)
{
struct nlattr *nl_combis;
int i, j;
@@ -900,7 +919,8 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
c->max_interfaces))
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+ if (large &&
+ nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
c->radar_detect_widths))
goto nla_put_failure;
@@ -954,426 +974,571 @@ static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
nla_nest_end(msg, nl_tcp);
return 0;
}
-#endif
-static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
- struct cfg80211_registered_device *dev)
+static int nl80211_send_wowlan(struct sk_buff *msg,
+ struct cfg80211_registered_device *dev,
+ bool large)
{
- void *hdr;
- struct nlattr *nl_bands, *nl_band;
- struct nlattr *nl_freqs, *nl_freq;
- struct nlattr *nl_rates, *nl_rate;
- struct nlattr *nl_cmds;
- enum ieee80211_band band;
- struct ieee80211_channel *chan;
- struct ieee80211_rate *rate;
- int i;
- const struct ieee80211_txrx_stypes *mgmt_stypes =
- dev->wiphy.mgmt_stypes;
+ struct nlattr *nl_wowlan;
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
- if (!hdr)
- return -1;
+ if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+ return 0;
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
- nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
- nla_put_u32(msg, NL80211_ATTR_GENERATION,
- cfg80211_rdev_list_generation) ||
- nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
- dev->wiphy.retry_short) ||
- nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
- dev->wiphy.retry_long) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
- dev->wiphy.frag_threshold) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
- dev->wiphy.rts_threshold) ||
- nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
- dev->wiphy.coverage_class) ||
- nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
- dev->wiphy.max_scan_ssids) ||
- nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
- dev->wiphy.max_sched_scan_ssids) ||
- nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
- dev->wiphy.max_scan_ie_len) ||
- nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
- dev->wiphy.max_sched_scan_ie_len) ||
- nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
- dev->wiphy.max_match_sets))
- goto nla_put_failure;
+ nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
+ if (!nl_wowlan)
+ return -ENOBUFS;
- if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
- goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
- goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
- goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
- nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
- goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
- nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
- goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
- nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
- goto nla_put_failure;
+ if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
+ return -ENOBUFS;
- if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
- sizeof(u32) * dev->wiphy.n_cipher_suites,
- dev->wiphy.cipher_suites))
- goto nla_put_failure;
+ if (dev->wiphy.wowlan.n_patterns) {
+ struct nl80211_wowlan_pattern_support pat = {
+ .max_patterns = dev->wiphy.wowlan.n_patterns,
+ .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
+ .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
+ .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+ };
- if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
- dev->wiphy.max_num_pmkids))
- goto nla_put_failure;
+ if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ sizeof(pat), &pat))
+ return -ENOBUFS;
+ }
- if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
- goto nla_put_failure;
+ if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+ return -ENOBUFS;
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
- dev->wiphy.available_antennas_tx) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
- dev->wiphy.available_antennas_rx))
- goto nla_put_failure;
+ nla_nest_end(msg, nl_wowlan);
- if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
- nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
- dev->wiphy.probe_resp_offload))
- goto nla_put_failure;
+ return 0;
+}
+#endif
- if ((dev->wiphy.available_antennas_tx ||
- dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
- u32 tx_ant = 0, rx_ant = 0;
- int res;
- res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
- if (!res) {
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
- tx_ant) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
- rx_ant))
- goto nla_put_failure;
- }
- }
+static int nl80211_send_band_rateinfo(struct sk_buff *msg,
+ struct ieee80211_supported_band *sband)
+{
+ struct nlattr *nl_rates, *nl_rate;
+ struct ieee80211_rate *rate;
+ int i;
- if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
- dev->wiphy.interface_modes))
- goto nla_put_failure;
+ /* add HT info */
+ if (sband->ht_cap.ht_supported &&
+ (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+ sizeof(sband->ht_cap.mcs),
+ &sband->ht_cap.mcs) ||
+ nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
+ sband->ht_cap.cap) ||
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ sband->ht_cap.ampdu_factor) ||
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+ sband->ht_cap.ampdu_density)))
+ return -ENOBUFS;
- nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
- if (!nl_bands)
- goto nla_put_failure;
+ /* add VHT info */
+ if (sband->vht_cap.vht_supported &&
+ (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
+ sizeof(sband->vht_cap.vht_mcs),
+ &sband->vht_cap.vht_mcs) ||
+ nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
+ sband->vht_cap.cap)))
+ return -ENOBUFS;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (!dev->wiphy.bands[band])
- continue;
+ /* add bitrates */
+ nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
+ if (!nl_rates)
+ return -ENOBUFS;
- nl_band = nla_nest_start(msg, band);
- if (!nl_band)
- goto nla_put_failure;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ nl_rate = nla_nest_start(msg, i);
+ if (!nl_rate)
+ return -ENOBUFS;
- /* add HT info */
- if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
- (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
- sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
- &dev->wiphy.bands[band]->ht_cap.mcs) ||
- nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
- dev->wiphy.bands[band]->ht_cap.cap) ||
- nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
- dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
- nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
- dev->wiphy.bands[band]->ht_cap.ampdu_density)))
- goto nla_put_failure;
+ rate = &sband->bitrates[i];
+ if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
+ rate->bitrate))
+ return -ENOBUFS;
+ if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+ nla_put_flag(msg,
+ NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
+ return -ENOBUFS;
- /* add VHT info */
- if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
- (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
- sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
- &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
- nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
- dev->wiphy.bands[band]->vht_cap.cap)))
- goto nla_put_failure;
+ nla_nest_end(msg, nl_rate);
+ }
- /* add frequencies */
- nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
- if (!nl_freqs)
- goto nla_put_failure;
+ nla_nest_end(msg, nl_rates);
- for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
- nl_freq = nla_nest_start(msg, i);
- if (!nl_freq)
- goto nla_put_failure;
+ return 0;
+}
+
+static int
+nl80211_send_mgmt_stypes(struct sk_buff *msg,
+ const struct ieee80211_txrx_stypes *mgmt_stypes)
+{
+ u16 stypes;
+ struct nlattr *nl_ftypes, *nl_ifs;
+ enum nl80211_iftype ift;
+ int i;
- chan = &dev->wiphy.bands[band]->channels[i];
+ if (!mgmt_stypes)
+ return 0;
- if (nl80211_msg_put_channel(msg, chan))
- goto nla_put_failure;
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
+ if (!nl_ifs)
+ return -ENOBUFS;
- nla_nest_end(msg, nl_freq);
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+ nl_ftypes = nla_nest_start(msg, ift);
+ if (!nl_ftypes)
+ return -ENOBUFS;
+ i = 0;
+ stypes = mgmt_stypes[ift].tx;
+ while (stypes) {
+ if ((stypes & 1) &&
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+ (i << 4) | IEEE80211_FTYPE_MGMT))
+ return -ENOBUFS;
+ stypes >>= 1;
+ i++;
}
+ nla_nest_end(msg, nl_ftypes);
+ }
- nla_nest_end(msg, nl_freqs);
+ nla_nest_end(msg, nl_ifs);
- /* add bitrates */
- nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
- if (!nl_rates)
- goto nla_put_failure;
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
+ if (!nl_ifs)
+ return -ENOBUFS;
- for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
- nl_rate = nla_nest_start(msg, i);
- if (!nl_rate)
- goto nla_put_failure;
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+ nl_ftypes = nla_nest_start(msg, ift);
+ if (!nl_ftypes)
+ return -ENOBUFS;
+ i = 0;
+ stypes = mgmt_stypes[ift].rx;
+ while (stypes) {
+ if ((stypes & 1) &&
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+ (i << 4) | IEEE80211_FTYPE_MGMT))
+ return -ENOBUFS;
+ stypes >>= 1;
+ i++;
+ }
+ nla_nest_end(msg, nl_ftypes);
+ }
+ nla_nest_end(msg, nl_ifs);
- rate = &dev->wiphy.bands[band]->bitrates[i];
- if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
- rate->bitrate))
- goto nla_put_failure;
- if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
- nla_put_flag(msg,
- NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
- goto nla_put_failure;
+ return 0;
+}
- nla_nest_end(msg, nl_rate);
- }
+static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
+ struct sk_buff *msg, u32 portid, u32 seq,
+ int flags, bool split, long *split_start,
+ long *band_start, long *chan_start)
+{
+ void *hdr;
+ struct nlattr *nl_bands, *nl_band;
+ struct nlattr *nl_freqs, *nl_freq;
+ struct nlattr *nl_cmds;
+ enum ieee80211_band band;
+ struct ieee80211_channel *chan;
+ int i;
+ const struct ieee80211_txrx_stypes *mgmt_stypes =
+ dev->wiphy.mgmt_stypes;
+ long start = 0, start_chan = 0, start_band = 0;
+ u32 features;
- nla_nest_end(msg, nl_rates);
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
+ if (!hdr)
+ return -ENOBUFS;
- nla_nest_end(msg, nl_band);
+ /* allow always using the variables */
+ if (!split) {
+ split_start = &start;
+ band_start = &start_band;
+ chan_start = &start_chan;
}
- nla_nest_end(msg, nl_bands);
- nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
- if (!nl_cmds)
- goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
+ nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
+ wiphy_name(&dev->wiphy)) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION,
+ cfg80211_rdev_list_generation))
+ goto nla_put_failure;
+
+ switch (*split_start) {
+ case 0:
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
+ dev->wiphy.retry_short) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
+ dev->wiphy.retry_long) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ dev->wiphy.frag_threshold) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+ dev->wiphy.rts_threshold) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+ dev->wiphy.coverage_class) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+ dev->wiphy.max_scan_ssids) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ dev->wiphy.max_sched_scan_ssids) ||
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+ dev->wiphy.max_scan_ie_len) ||
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+ dev->wiphy.max_sched_scan_ie_len) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
+ dev->wiphy.max_match_sets))
+ goto nla_put_failure;
- i = 0;
-#define CMD(op, n) \
- do { \
- if (dev->ops->op) { \
- i++; \
- if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
- goto nla_put_failure; \
- } \
- } while (0)
-
- CMD(add_virtual_intf, NEW_INTERFACE);
- CMD(change_virtual_intf, SET_INTERFACE);
- CMD(add_key, NEW_KEY);
- CMD(start_ap, START_AP);
- CMD(add_station, NEW_STATION);
- CMD(add_mpath, NEW_MPATH);
- CMD(update_mesh_config, SET_MESH_CONFIG);
- CMD(change_bss, SET_BSS);
- CMD(auth, AUTHENTICATE);
- CMD(assoc, ASSOCIATE);
- CMD(deauth, DEAUTHENTICATE);
- CMD(disassoc, DISASSOCIATE);
- CMD(join_ibss, JOIN_IBSS);
- CMD(join_mesh, JOIN_MESH);
- CMD(set_pmksa, SET_PMKSA);
- CMD(del_pmksa, DEL_PMKSA);
- CMD(flush_pmksa, FLUSH_PMKSA);
- if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
- CMD(remain_on_channel, REMAIN_ON_CHANNEL);
- CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
- CMD(mgmt_tx, FRAME);
- CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
- if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
- i++;
- if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
+ if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
goto nla_put_failure;
- }
- if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
- dev->ops->join_mesh) {
- i++;
- if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+ if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
goto nla_put_failure;
- }
- CMD(set_wds_peer, SET_WDS_PEER);
- if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
- CMD(tdls_mgmt, TDLS_MGMT);
- CMD(tdls_oper, TDLS_OPER);
- }
- if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
- CMD(sched_scan_start, START_SCHED_SCAN);
- CMD(probe_client, PROBE_CLIENT);
- CMD(set_noack_map, SET_NOACK_MAP);
- if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
- i++;
- if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
+ if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
+ goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+ nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
+ goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+ nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
+ goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
+ nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
- }
- CMD(start_p2p_device, START_P2P_DEVICE);
- CMD(set_mcast_rate, SET_MCAST_RATE);
-#ifdef CONFIG_NL80211_TESTMODE
- CMD(testmode_cmd, TESTMODE);
-#endif
+ (*split_start)++;
+ if (split)
+ break;
+ case 1:
+ if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
+ sizeof(u32) * dev->wiphy.n_cipher_suites,
+ dev->wiphy.cipher_suites))
+ goto nla_put_failure;
-#undef CMD
+ if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
+ dev->wiphy.max_num_pmkids))
+ goto nla_put_failure;
- if (dev->ops->connect || dev->ops->auth) {
- i++;
- if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
+ if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
goto nla_put_failure;
- }
- if (dev->ops->disconnect || dev->ops->deauth) {
- i++;
- if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ dev->wiphy.available_antennas_tx) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+ dev->wiphy.available_antennas_rx))
+ goto nla_put_failure;
+
+ if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+ nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
+ dev->wiphy.probe_resp_offload))
goto nla_put_failure;
- }
- nla_nest_end(msg, nl_cmds);
+ if ((dev->wiphy.available_antennas_tx ||
+ dev->wiphy.available_antennas_rx) &&
+ dev->ops->get_antenna) {
+ u32 tx_ant = 0, rx_ant = 0;
+ int res;
+ res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
+ if (!res) {
+ if (nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
+ tx_ant) ||
+ nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
+ rx_ant))
+ goto nla_put_failure;
+ }
+ }
- if (dev->ops->remain_on_channel &&
- (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
- nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
- dev->wiphy.max_remain_on_channel_duration))
- goto nla_put_failure;
+ (*split_start)++;
+ if (split)
+ break;
+ case 2:
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
+ dev->wiphy.interface_modes))
+ goto nla_put_failure;
+ (*split_start)++;
+ if (split)
+ break;
+ case 3:
+ nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
+ if (!nl_bands)
+ goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
- nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
- goto nla_put_failure;
+ for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
- if (mgmt_stypes) {
- u16 stypes;
- struct nlattr *nl_ftypes, *nl_ifs;
- enum nl80211_iftype ift;
+ sband = dev->wiphy.bands[band];
- nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
- if (!nl_ifs)
- goto nla_put_failure;
+ if (!sband)
+ continue;
- for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
- nl_ftypes = nla_nest_start(msg, ift);
- if (!nl_ftypes)
+ nl_band = nla_nest_start(msg, band);
+ if (!nl_band)
goto nla_put_failure;
- i = 0;
- stypes = mgmt_stypes[ift].tx;
- while (stypes) {
- if ((stypes & 1) &&
- nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
- (i << 4) | IEEE80211_FTYPE_MGMT))
+
+ switch (*chan_start) {
+ case 0:
+ if (nl80211_send_band_rateinfo(msg, sband))
goto nla_put_failure;
- stypes >>= 1;
- i++;
+ (*chan_start)++;
+ if (split)
+ break;
+ default:
+ /* add frequencies */
+ nl_freqs = nla_nest_start(
+ msg, NL80211_BAND_ATTR_FREQS);
+ if (!nl_freqs)
+ goto nla_put_failure;
+
+ for (i = *chan_start - 1;
+ i < sband->n_channels;
+ i++) {
+ nl_freq = nla_nest_start(msg, i);
+ if (!nl_freq)
+ goto nla_put_failure;
+
+ chan = &sband->channels[i];
+
+ if (nl80211_msg_put_channel(msg, chan,
+ split))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_freq);
+ if (split)
+ break;
+ }
+ if (i < sband->n_channels)
+ *chan_start = i + 2;
+ else
+ *chan_start = 0;
+ nla_nest_end(msg, nl_freqs);
+ }
+
+ nla_nest_end(msg, nl_band);
+
+ if (split) {
+ /* start again here */
+ if (*chan_start)
+ band--;
+ break;
}
- nla_nest_end(msg, nl_ftypes);
}
+ nla_nest_end(msg, nl_bands);
- nla_nest_end(msg, nl_ifs);
+ if (band < IEEE80211_NUM_BANDS)
+ *band_start = band + 1;
+ else
+ *band_start = 0;
- nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
- if (!nl_ifs)
+ /* if bands & channels are done, continue outside */
+ if (*band_start == 0 && *chan_start == 0)
+ (*split_start)++;
+ if (split)
+ break;
+ case 4:
+ nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+ if (!nl_cmds)
goto nla_put_failure;
- for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
- nl_ftypes = nla_nest_start(msg, ift);
- if (!nl_ftypes)
+ i = 0;
+#define CMD(op, n) \
+ do { \
+ if (dev->ops->op) { \
+ i++; \
+ if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
+ goto nla_put_failure; \
+ } \
+ } while (0)
+
+ CMD(add_virtual_intf, NEW_INTERFACE);
+ CMD(change_virtual_intf, SET_INTERFACE);
+ CMD(add_key, NEW_KEY);
+ CMD(start_ap, START_AP);
+ CMD(add_station, NEW_STATION);
+ CMD(add_mpath, NEW_MPATH);
+ CMD(update_mesh_config, SET_MESH_CONFIG);
+ CMD(change_bss, SET_BSS);
+ CMD(auth, AUTHENTICATE);
+ CMD(assoc, ASSOCIATE);
+ CMD(deauth, DEAUTHENTICATE);
+ CMD(disassoc, DISASSOCIATE);
+ CMD(join_ibss, JOIN_IBSS);
+ CMD(join_mesh, JOIN_MESH);
+ CMD(set_pmksa, SET_PMKSA);
+ CMD(del_pmksa, DEL_PMKSA);
+ CMD(flush_pmksa, FLUSH_PMKSA);
+ if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+ CMD(remain_on_channel, REMAIN_ON_CHANNEL);
+ CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
+ CMD(mgmt_tx, FRAME);
+ CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
+ if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
goto nla_put_failure;
- i = 0;
- stypes = mgmt_stypes[ift].rx;
- while (stypes) {
- if ((stypes & 1) &&
- nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
- (i << 4) | IEEE80211_FTYPE_MGMT))
- goto nla_put_failure;
- stypes >>= 1;
- i++;
- }
- nla_nest_end(msg, nl_ftypes);
}
- nla_nest_end(msg, nl_ifs);
- }
+ if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
+ dev->ops->join_mesh) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+ goto nla_put_failure;
+ }
+ CMD(set_wds_peer, SET_WDS_PEER);
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+ CMD(tdls_mgmt, TDLS_MGMT);
+ CMD(tdls_oper, TDLS_OPER);
+ }
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+ CMD(sched_scan_start, START_SCHED_SCAN);
+ CMD(probe_client, PROBE_CLIENT);
+ CMD(set_noack_map, SET_NOACK_MAP);
+ if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
+ goto nla_put_failure;
+ }
+ CMD(start_p2p_device, START_P2P_DEVICE);
+ CMD(set_mcast_rate, SET_MCAST_RATE);
-#ifdef CONFIG_PM
- if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
- struct nlattr *nl_wowlan;
+#ifdef CONFIG_NL80211_TESTMODE
+ CMD(testmode_cmd, TESTMODE);
+#endif
- nl_wowlan = nla_nest_start(msg,
- NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
- if (!nl_wowlan)
- goto nla_put_failure;
+#undef CMD
- if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
- goto nla_put_failure;
- if (dev->wiphy.wowlan.n_patterns) {
- struct nl80211_wowlan_pattern_support pat = {
- .max_patterns = dev->wiphy.wowlan.n_patterns,
- .min_pattern_len =
- dev->wiphy.wowlan.pattern_min_len,
- .max_pattern_len =
- dev->wiphy.wowlan.pattern_max_len,
- .max_pkt_offset =
- dev->wiphy.wowlan.max_pkt_offset,
- };
- if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
- sizeof(pat), &pat))
+ if (dev->ops->connect || dev->ops->auth) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
goto nla_put_failure;
}
- if (nl80211_send_wowlan_tcp_caps(dev, msg))
+ if (dev->ops->disconnect || dev->ops->deauth) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(msg, nl_cmds);
+ (*split_start)++;
+ if (split)
+ break;
+ case 5:
+ if (dev->ops->remain_on_channel &&
+ (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
+ nla_put_u32(msg,
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+ dev->wiphy.max_remain_on_channel_duration))
+ goto nla_put_failure;
+
+ if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+ nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
goto nla_put_failure;
- nla_nest_end(msg, nl_wowlan);
- }
+ if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
+ goto nla_put_failure;
+ (*split_start)++;
+ if (split)
+ break;
+ case 6:
+#ifdef CONFIG_PM
+ if (nl80211_send_wowlan(msg, dev, split))
+ goto nla_put_failure;
+ (*split_start)++;
+ if (split)
+ break;
+#else
+ (*split_start)++;
#endif
+ case 7:
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
+ dev->wiphy.software_iftypes))
+ goto nla_put_failure;
- if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
- dev->wiphy.software_iftypes))
- goto nla_put_failure;
+ if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
+ goto nla_put_failure;
- if (nl80211_put_iface_combinations(&dev->wiphy, msg))
- goto nla_put_failure;
+ (*split_start)++;
+ if (split)
+ break;
+ case 8:
+ if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+ nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
+ dev->wiphy.ap_sme_capa))
+ goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
- nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
- dev->wiphy.ap_sme_capa))
- goto nla_put_failure;
+ features = dev->wiphy.features;
+ /*
+ * We can only add the per-channel limit information if the
+ * dump is split, otherwise it makes it too big. Therefore
+ * only advertise it in that case.
+ */
+ if (split)
+ features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+ if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
+ goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
- dev->wiphy.features))
- goto nla_put_failure;
+ if (dev->wiphy.ht_capa_mod_mask &&
+ nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+ sizeof(*dev->wiphy.ht_capa_mod_mask),
+ dev->wiphy.ht_capa_mod_mask))
+ goto nla_put_failure;
- if (dev->wiphy.ht_capa_mod_mask &&
- nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
- sizeof(*dev->wiphy.ht_capa_mod_mask),
- dev->wiphy.ht_capa_mod_mask))
- goto nla_put_failure;
+ if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+ dev->wiphy.max_acl_mac_addrs &&
+ nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+ dev->wiphy.max_acl_mac_addrs))
+ goto nla_put_failure;
- if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
- dev->wiphy.max_acl_mac_addrs &&
- nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
- dev->wiphy.max_acl_mac_addrs))
- goto nla_put_failure;
+ /*
+ * Any information below this point is only available to
+ * applications that can deal with it being split. This
+ * helps ensure that newly added capabilities don't break
+ * older tools by overrunning their buffers.
+ *
+ * We still increment split_start so that in the split
+ * case we'll continue with more data in the next round,
+ * but break unconditionally so unsplit data stops here.
+ */
+ (*split_start)++;
+ break;
+ case 9:
+ if (dev->wiphy.extended_capabilities &&
+ (nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ dev->wiphy.extended_capabilities_len,
+ dev->wiphy.extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ dev->wiphy.extended_capabilities_len,
+ dev->wiphy.extended_capabilities_mask)))
+ goto nla_put_failure;
- if (dev->wiphy.extended_capabilities &&
- (nla_put(msg, NL80211_ATTR_EXT_CAPA,
- dev->wiphy.extended_capabilities_len,
- dev->wiphy.extended_capabilities) ||
- nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
- dev->wiphy.extended_capabilities_len,
- dev->wiphy.extended_capabilities_mask)))
- goto nla_put_failure;
+ if (dev->wiphy.vht_capa_mod_mask &&
+ nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
+ sizeof(*dev->wiphy.vht_capa_mod_mask),
+ dev->wiphy.vht_capa_mod_mask))
+ goto nla_put_failure;
+ /* done */
+ *split_start = 0;
+ break;
+ }
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -1383,22 +1548,83 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
{
- int idx = 0;
+ int idx = 0, ret;
int start = cb->args[0];
struct cfg80211_registered_device *dev;
+ s64 filter_wiphy = -1;
+ bool split = false;
+ struct nlattr **tb = nl80211_fam.attrbuf;
+ int res;
mutex_lock(&cfg80211_mutex);
+ res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+ tb, nl80211_fam.maxattr, nl80211_policy);
+ if (res == 0) {
+ split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
+ if (tb[NL80211_ATTR_WIPHY])
+ filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+ if (tb[NL80211_ATTR_WDEV])
+ filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ struct net_device *netdev;
+ int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+ netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
+ if (!netdev) {
+ mutex_unlock(&cfg80211_mutex);
+ return -ENODEV;
+ }
+ if (netdev->ieee80211_ptr) {
+ dev = wiphy_to_dev(
+ netdev->ieee80211_ptr->wiphy);
+ filter_wiphy = dev->wiphy_idx;
+ }
+ dev_put(netdev);
+ }
+ }
+
list_for_each_entry(dev, &cfg80211_rdev_list, list) {
if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
continue;
if (++idx <= start)
continue;
- if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI,
- dev) < 0) {
- idx--;
- break;
- }
+ if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
+ continue;
+ /* attempt to fit multiple wiphy data chunks into the skb */
+ do {
+ ret = nl80211_send_wiphy(dev, skb,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI,
+ split, &cb->args[1],
+ &cb->args[2],
+ &cb->args[3]);
+ if (ret < 0) {
+ /*
+ * If sending the wiphy data didn't fit (ENOBUFS
+ * or EMSGSIZE returned), this SKB is still
+ * empty (so it's not too big because another
+ * wiphy dataset is already in the skb) and
+ * we've not tried to adjust the dump allocation
+ * yet ... then adjust the alloc size to be
+ * bigger, and return 1 but with the empty skb.
+ * This results in an empty message being RX'ed
+ * in userspace, but that is ignored.
+ *
+ * We can then retry with the larger buffer.
+ */
+ if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+ !skb->len &&
+ cb->min_dump_alloc < 4096) {
+ cb->min_dump_alloc = 4096;
+ mutex_unlock(&cfg80211_mutex);
+ return 1;
+ }
+ idx--;
+ break;
+ }
+ } while (cb->args[1] > 0);
+ break;
}
mutex_unlock(&cfg80211_mutex);
@@ -1412,11 +1638,12 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
struct sk_buff *msg;
struct cfg80211_registered_device *dev = info->user_ptr[0];
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ msg = nlmsg_new(4096, GFP_KERNEL);
if (!msg)
return -ENOMEM;
- if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) {
+ if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
+ false, NULL, NULL, NULL) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -3023,6 +3250,7 @@ static int parse_station_flags(struct genl_info *info,
sta_flags = nla_data(nla);
params->sta_flags_mask = sta_flags->mask;
params->sta_flags_set = sta_flags->set;
+ params->sta_flags_set &= params->sta_flags_mask;
if ((params->sta_flags_mask |
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
return -EINVAL;
@@ -3376,6 +3604,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
return genlmsg_reply(msg, info);
}
+int cfg80211_check_station_change(struct wiphy *wiphy,
+ struct station_parameters *params,
+ enum cfg80211_station_type statype)
+{
+ if (params->listen_interval != -1)
+ return -EINVAL;
+ if (params->aid)
+ return -EINVAL;
+
+ /* When you run into this, adjust the code below for the new flag */
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
+ switch (statype) {
+ case CFG80211_STA_MESH_PEER_KERNEL:
+ case CFG80211_STA_MESH_PEER_USER:
+ /*
+ * No ignoring the TDLS flag here -- the userspace mesh
+ * code doesn't have the bug of including TDLS in the
+ * mask everywhere.
+ */
+ if (params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_MFP) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_SETUP:
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -EINVAL;
+ /* ignore since it can't change */
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ break;
+ default:
+ /* disallow mesh-specific things */
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
+ return -EINVAL;
+ if (params->local_pm)
+ return -EINVAL;
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+ return -EINVAL;
+ }
+
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
+ statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
+ /* TDLS can't be set, ... */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ return -EINVAL;
+ /*
+ * ... but don't bother the driver with it. This works around
+ * a hostapd/wpa_supplicant issue -- it always includes the
+ * TLDS_PEER flag in the mask even for AP mode.
+ */
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ }
+
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
+ /* reject other things that can't change */
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
+ return -EINVAL;
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
+ return -EINVAL;
+ if (params->supported_rates)
+ return -EINVAL;
+ if (params->ext_capab || params->ht_capa || params->vht_capa)
+ return -EINVAL;
+ }
+
+ if (statype != CFG80211_STA_AP_CLIENT) {
+ if (params->vlan)
+ return -EINVAL;
+ }
+
+ switch (statype) {
+ case CFG80211_STA_AP_MLME_CLIENT:
+ /* Use this only for authorizing/unauthorizing a station */
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return -EOPNOTSUPP;
+ break;
+ case CFG80211_STA_AP_CLIENT:
+ /* 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 (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+ params->sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED)))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_IBSS:
+ case CFG80211_STA_AP_STA:
+ /* reject any changes other than AUTHORIZED */
+ if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_SETUP:
+ /* reject any changes other than AUTHORIZED or WME */
+ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_WME)))
+ return -EINVAL;
+ /* force (at least) rates when authorizing */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
+ !params->supported_rates)
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
+ /* reject any changes */
+ return -EINVAL;
+ case CFG80211_STA_MESH_PEER_KERNEL:
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+ return -EINVAL;
+ break;
+ case CFG80211_STA_MESH_PEER_USER:
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_station_change);
+
/*
* Get vlan interface making sure it is running and on the right wiphy.
*/
@@ -3398,6 +3756,13 @@ static struct net_device *get_vlan(struct genl_info *info,
goto error;
}
+ if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ ret = -EINVAL;
+ goto error;
+ }
+
if (!netif_running(v)) {
ret = -ENETDOWN;
goto error;
@@ -3415,21 +3780,13 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
};
-static int nl80211_set_station_tdls(struct genl_info *info,
- struct station_parameters *params)
+static int nl80211_parse_sta_wme(struct genl_info *info,
+ struct station_parameters *params)
{
struct nlattr *tb[NL80211_STA_WME_MAX + 1];
struct nlattr *nla;
int err;
- /* Dummy STA entry gets updated once the peer capabilities are known */
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
- params->ht_capa =
- nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
- if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
- params->vht_capa =
- nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
-
/* parse WME attributes if present */
if (!info->attrs[NL80211_ATTR_STA_WME])
return 0;
@@ -3457,18 +3814,34 @@ static int nl80211_set_station_tdls(struct genl_info *info,
return 0;
}
+static int nl80211_set_station_tdls(struct genl_info *info,
+ struct station_parameters *params)
+{
+ /* Dummy STA entry gets updated once the peer capabilities are known */
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
+ params->ht_capa =
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+ params->vht_capa =
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+
+ return nl80211_parse_sta_wme(info, params);
+}
+
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- int err;
struct net_device *dev = info->user_ptr[1];
struct station_parameters params;
- u8 *mac_addr = NULL;
+ u8 *mac_addr;
+ int err;
memset(&params, 0, sizeof(params));
params.listen_interval = -1;
- params.plink_state = -1;
+
+ if (!rdev->ops->change_station)
+ return -EOPNOTSUPP;
if (info->attrs[NL80211_ATTR_STA_AID])
return -EINVAL;
@@ -3501,19 +3874,23 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
return -EINVAL;
- if (!rdev->ops->change_station)
- return -EOPNOTSUPP;
-
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
return -EINVAL;
- if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
params.plink_action =
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
+ return -EINVAL;
+ }
- if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
+ if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
params.plink_state =
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
+ if (params.plink_state >= NUM_NL80211_PLINK_STATES)
+ return -EINVAL;
+ params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
+ }
if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
enum nl80211_mesh_power_mode pm = nla_get_u32(
@@ -3526,127 +3903,33 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.local_pm = pm;
}
+ /* Include parameters for TDLS peer (will check later) */
+ err = nl80211_set_station_tdls(info, &params);
+ if (err)
+ return err;
+
+ params.vlan = get_vlan(info, rdev);
+ if (IS_ERR(params.vlan))
+ return PTR_ERR(params.vlan);
+
switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
- /* disallow mesh-specific things */
- if (params.plink_action)
- return -EINVAL;
- if (params.local_pm)
- return -EINVAL;
-
- /* TDLS can't be set, ... */
- if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
- return -EINVAL;
- /*
- * ... but don't bother the driver with it. This works around
- * a hostapd/wpa_supplicant issue -- it always includes the
- * TLDS_PEER flag in the mask even for AP mode.
- */
- params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
-
- /* 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;
- if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
- 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_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
- /*
- * Don't allow userspace to change the TDLS_PEER flag,
- * but silently ignore attempts to change it since we
- * don't have state here to verify that it doesn't try
- * to change the flag.
- */
- params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
- /* Include parameters for TDLS peer (driver will check) */
- err = nl80211_set_station_tdls(info, &params);
- if (err)
- return err;
- /* disallow things sta doesn't support */
- if (params.plink_action)
- return -EINVAL;
- if (params.local_pm)
- return -EINVAL;
- /* reject any changes other than AUTHORIZED or WME (for TDLS) */
- if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
- BIT(NL80211_STA_FLAG_WME)))
- return -EINVAL;
- break;
case NL80211_IFTYPE_ADHOC:
- /* disallow things sta doesn't support */
- if (params.plink_action)
- return -EINVAL;
- if (params.local_pm)
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
- return -EINVAL;
- /* reject any changes other than AUTHORIZED */
- if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
- return -EINVAL;
- break;
case NL80211_IFTYPE_MESH_POINT:
- /* disallow things mesh doesn't support */
- if (params.vlan)
- return -EINVAL;
- if (params.supported_rates)
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
- return -EINVAL;
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
- return -EINVAL;
- /*
- * No special handling for TDLS here -- the userspace
- * mesh code doesn't have this bug.
- */
- if (params.sta_flags_mask &
- ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_MFP) |
- BIT(NL80211_STA_FLAG_AUTHORIZED)))
- return -EINVAL;
break;
default:
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ goto out_put_vlan;
}
- /* be aware of params.vlan when changing code here */
-
+ /* driver will call cfg80211_check_station_change() */
err = rdev_change_station(rdev, dev, mac_addr, &params);
+ out_put_vlan:
if (params.vlan)
dev_put(params.vlan);
@@ -3663,6 +3946,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
+ if (!rdev->ops->add_station)
+ return -EOPNOTSUPP;
+
if (!info->attrs[NL80211_ATTR_MAC])
return -EINVAL;
@@ -3708,50 +3994,32 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.vht_capa =
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
- if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
params.plink_action =
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
+ return -EINVAL;
+ }
- if (!rdev->ops->add_station)
- return -EOPNOTSUPP;
+ err = nl80211_parse_sta_wme(info, &params);
+ if (err)
+ return err;
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
return -EINVAL;
+ /* When you run into this, adjust the code below for the new flag */
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
- /* parse WME attributes if sta is WME capable */
- if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
- (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
- info->attrs[NL80211_ATTR_STA_WME]) {
- struct nlattr *tb[NL80211_STA_WME_MAX + 1];
- struct nlattr *nla;
-
- nla = info->attrs[NL80211_ATTR_STA_WME];
- err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
- nl80211_sta_wme_policy);
- if (err)
- return err;
-
- if (tb[NL80211_STA_WME_UAPSD_QUEUES])
- params.uapsd_queues =
- nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
- if (params.uapsd_queues &
- ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
- return -EINVAL;
+ /* ignore WME attributes if iface/sta is not capable */
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
+ !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
- if (tb[NL80211_STA_WME_MAX_SP])
- params.max_sp =
- nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
-
- if (params.max_sp &
- ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
- return -EINVAL;
-
- params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
- }
/* TDLS peers cannot be added */
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
return -EINVAL;
@@ -3772,6 +4040,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.vlan);
break;
case NL80211_IFTYPE_MESH_POINT:
+ /* ignore uAPSD data */
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
/* associated is disallowed */
if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
return -EINVAL;
@@ -3780,8 +4051,14 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
break;
case NL80211_IFTYPE_STATION:
- /* associated is disallowed */
- if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /* ignore uAPSD data */
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
+ /* these are disallowed */
+ if (params.sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED)))
return -EINVAL;
/* Only TDLS peers can be added */
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
@@ -3792,6 +4069,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* ... with external setup is supported */
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
return -EOPNOTSUPP;
+ /*
+ * Older wpa_supplicant versions always mark the TDLS peer
+ * as authorized, but it shouldn't yet be.
+ */
+ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
break;
default:
return -EOPNOTSUPP;
@@ -4336,6 +4618,7 @@ static const struct nla_policy
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+ [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
@@ -4474,6 +4757,7 @@ do { \
static int nl80211_parse_mesh_setup(struct genl_info *info,
struct mesh_setup *setup)
{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
if (!info->attrs[NL80211_ATTR_MESH_SETUP])
@@ -4510,8 +4794,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
setup->ie = nla_data(ieattr);
setup->ie_len = nla_len(ieattr);
}
+ if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
+ !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
+ return -EINVAL;
+ setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
+ if (setup->is_secure)
+ setup->user_mpm = true;
return 0;
}
@@ -5706,14 +5996,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
- struct cfg80211_crypto_settings crypto;
struct ieee80211_channel *chan;
- const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
- int err, ssid_len, ie_len = 0;
- bool use_mfp = false;
- u32 flags = 0;
- struct ieee80211_ht_cap *ht_capa = NULL;
- struct ieee80211_ht_cap *ht_capa_mask = NULL;
+ struct cfg80211_assoc_request req = {};
+ const u8 *bssid, *ssid;
+ int err, ssid_len = 0;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
@@ -5741,41 +6027,58 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
if (info->attrs[NL80211_ATTR_IE]) {
- ie = nla_data(info->attrs[NL80211_ATTR_IE]);
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
if (info->attrs[NL80211_ATTR_USE_MFP]) {
enum nl80211_mfp mfp =
nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
if (mfp == NL80211_MFP_REQUIRED)
- use_mfp = true;
+ req.use_mfp = true;
else if (mfp != NL80211_MFP_NO)
return -EINVAL;
}
if (info->attrs[NL80211_ATTR_PREV_BSSID])
- prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+ req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
- flags |= ASSOC_REQ_DISABLE_HT;
+ req.flags |= ASSOC_REQ_DISABLE_HT;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
- ht_capa_mask =
- nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+ memcpy(&req.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(req.ht_capa_mask));
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
- if (!ht_capa_mask)
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&req.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(req.ht_capa));
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+ req.flags |= ASSOC_REQ_DISABLE_VHT;
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+ memcpy(&req.vht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+ sizeof(req.vht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
return -EINVAL;
- ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ memcpy(&req.vht_capa,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+ sizeof(req.vht_capa));
}
- err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+ err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err)
- err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
- ssid, ssid_len, ie, ie_len, use_mfp,
- &crypto, flags, ht_capa,
- ht_capa_mask);
+ err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+ ssid, ssid_len, &req);
return err;
}
@@ -6355,6 +6658,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
sizeof(connect.ht_capa));
}
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+ connect.flags |= ASSOC_REQ_DISABLE_VHT;
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+ memcpy(&connect.vht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+ sizeof(connect.vht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
+ kfree(connkeys);
+ return -EINVAL;
+ }
+ memcpy(&connect.vht_capa,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+ sizeof(connect.vht_capa));
+ }
+
err = cfg80211_connect(rdev, dev, &connect, connkeys);
if (err)
kfree(connkeys);
@@ -7128,6 +7449,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
return err;
}
+ if (setup.user_mpm)
+ cfg.auto_open_plinks = false;
+
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
if (err)
@@ -7327,7 +7651,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
return -EINVAL;
if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
- rdev->wiphy.wowlan.tcp->data_interval_max)
+ rdev->wiphy.wowlan.tcp->data_interval_max ||
+ nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
return -EINVAL;
wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
@@ -7823,6 +8148,54 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
return 0;
}
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ void *hdr;
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+ NL80211_CMD_GET_PROTOCOL_FEATURES);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_update_ft_ies_params ft_params;
+ struct net_device *dev = info->user_ptr[1];
+
+ if (!rdev->ops->update_ft_ies)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MDID] ||
+ !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ memset(&ft_params, 0, sizeof(ft_params));
+ ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+ ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+ return rdev_update_ft_ies(rdev, dev, &ft_params);
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -8499,6 +8872,19 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+ .doit = nl80211_get_protocol_features,
+ .policy = nl80211_policy,
+ },
+ {
+ .cmd = NL80211_CMD_UPDATE_FT_IES,
+ .doit = nl80211_update_ft_ies,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8526,7 +8912,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
if (!msg)
return;
- if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+ if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
+ false, NULL, NULL, NULL) < 0) {
nlmsg_free(msg);
return;
}
@@ -8850,21 +9237,31 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
NL80211_CMD_DISASSOCIATE, gfp);
}
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len, gfp_t gfp)
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+ size_t len)
{
- nl80211_send_mlme_event(rdev, netdev, buf, len,
- NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_send_unprot_deauth(dev);
+ nl80211_send_mlme_event(rdev, dev, buf, len,
+ NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
}
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len, gfp_t gfp)
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+ size_t len)
{
- nl80211_send_mlme_event(rdev, netdev, buf, len,
- NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_send_unprot_disassoc(dev);
+ nl80211_send_mlme_event(rdev, dev, buf, len,
+ NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
}
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
struct net_device *netdev, int cmd,
@@ -9067,14 +9464,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *macaddr, const u8* ie, u8 ie_len,
- gfp_t gfp)
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+ const u8* ie, u8 ie_len, gfp_t gfp)
{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct sk_buff *msg;
void *hdr;
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+ return;
+
+ trace_cfg80211_notify_new_peer_candidate(dev, addr);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9086,8 +9488,8 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
(ie_len && ie &&
nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
goto nla_put_failure;
@@ -9102,6 +9504,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *addr,
@@ -9170,7 +9573,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
if (!nl_freq)
goto nla_put_failure;
- if (nl80211_msg_put_channel(msg, channel_before))
+ if (nl80211_msg_put_channel(msg, channel_before, false))
goto nla_put_failure;
nla_nest_end(msg, nl_freq);
@@ -9178,7 +9581,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
if (!nl_freq)
goto nla_put_failure;
- if (nl80211_msg_put_channel(msg, channel_after))
+ if (nl80211_msg_put_channel(msg, channel_after, false))
goto nla_put_failure;
nla_nest_end(msg, nl_freq);
@@ -9240,31 +9643,42 @@ static void nl80211_send_remain_on_chan_event(
nlmsg_free(msg);
}
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, u64 cookie,
- struct ieee80211_channel *chan,
- unsigned int duration, gfp_t gfp)
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration, gfp_t gfp)
{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
rdev, wdev, cookie, chan,
duration, gfp);
}
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
-void nl80211_send_remain_on_channel_cancel(
- struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ gfp_t gfp)
{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
rdev, wdev, cookie, chan, 0, gfp);
}
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- struct station_info *sinfo, gfp_t gfp)
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp)
{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
+ trace_cfg80211_new_sta(dev, mac_addr, sinfo);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9278,14 +9692,17 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
}
+EXPORT_SYMBOL(cfg80211_new_sta);
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- gfp_t gfp)
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
void *hdr;
+ trace_cfg80211_del_sta(dev, mac_addr);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9310,12 +9727,14 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_del_sta);
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- enum nl80211_connect_failed_reason reason,
- gfp_t gfp)
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp)
{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
void *hdr;
@@ -9344,6 +9763,7 @@ void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_conn_failed);
static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
const u8 *addr, gfp_t gfp)
@@ -9388,19 +9808,47 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
return true;
}
-bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
{
- return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
- addr, 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, NL80211_CMD_UNEXPECTED_FRAME,
+ addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
}
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
- const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
{
- return __nl80211_unexpected_frame(dev,
- NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
- addr, 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_frame(dev,
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+ addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
}
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlportid,
@@ -9440,15 +9888,17 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
return -ENOBUFS;
}
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, u64 cookie,
- const u8 *buf, size_t len, bool ack,
- gfp_t gfp)
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+ const u8 *buf, size_t len, bool ack, gfp_t gfp)
{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct net_device *netdev = wdev->netdev;
struct sk_buff *msg;
void *hdr;
+ trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9476,17 +9926,21 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
struct nlattr *pinfoattr;
void *hdr;
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9498,7 +9952,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
goto nla_put_failure;
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
@@ -9521,10 +9975,11 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *bssid,
- const u8 *replay_ctr, gfp_t gfp)
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
{
struct sk_buff *msg;
struct nlattr *rekey_attr;
@@ -9566,9 +10021,22 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, int index,
- const u8 *bssid, bool preauth, gfp_t gfp)
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_gtk_rekey_notify(dev, bssid);
+ nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
+
+static void
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int index,
+ const u8 *bssid, bool preauth, gfp_t gfp)
{
struct sk_buff *msg;
struct nlattr *attr;
@@ -9611,9 +10079,22 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- struct cfg80211_chan_def *chandef, gfp_t gfp)
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+ const u8 *bssid, bool preauth, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
+ nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
+}
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
@@ -9645,11 +10126,36 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *peer,
- u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
+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);
+
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets,
+ u32 rate, u32 intvl, gfp_t gfp)
{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
struct nlattr *pinfoattr;
void *hdr;
@@ -9665,7 +10171,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
goto nla_put_failure;
@@ -9694,6 +10200,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -9746,15 +10253,18 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *peer,
- u32 num_packets, gfp_t gfp)
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp)
{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
struct nlattr *pinfoattr;
void *hdr;
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
@@ -9766,7 +10276,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
goto nla_put_failure;
@@ -9789,6 +10299,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
u64 cookie, bool acked, gfp_t gfp)
@@ -10075,6 +10586,50 @@ static struct notifier_block nl80211_netlink_notifier = {
.notifier_call = nl80211_netlink_notify,
};
+void cfg80211_ft_event(struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event)
+{
+ struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ trace_cfg80211_ft_event(wiphy, netdev, ft_event);
+
+ if (!ft_event->target_ap)
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
+ if (ft_event->ies)
+ nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
+ if (ft_event->ric_ies)
+ nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+ ft_event->ric_ies);
+
+ err = genlmsg_end(msg, hdr);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, GFP_KERNEL);
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
+
/* initialisation/exit functions */
int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index b061da4..a4073e8 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -29,12 +29,6 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *buf, size_t len, gfp_t gfp);
void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *addr, gfp_t gfp);
@@ -54,10 +48,6 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u16 reason,
const u8 *ie, size_t ie_len, bool from_ap);
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *macaddr, const u8* ie, u8 ie_len,
- gfp_t gfp);
void
nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *addr,
@@ -73,41 +63,10 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
gfp_t gfp);
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, u64 cookie,
- struct ieee80211_channel *chan,
- unsigned int duration, gfp_t gfp);
-void nl80211_send_remain_on_channel_cancel(
- struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan, gfp_t gfp);
-
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- struct station_info *sinfo, gfp_t gfp);
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- gfp_t gfp);
-
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *mac_addr,
- enum nl80211_connect_failed_reason reason,
- gfp_t gfp);
-
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlpid,
int freq, int sig_dbm,
const u8 *buf, size_t len, gfp_t gfp);
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, u64 cookie,
- const u8 *buf, size_t len, bool ack,
- gfp_t gfp);
-
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -115,31 +74,4 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *peer,
- u32 num_packets, gfp_t gfp);
-
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *peer,
- u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
-
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *bssid,
- const u8 *replay_ctr, gfp_t gfp);
-
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, int index,
- const u8 *bssid, bool preauth, gfp_t gfp);
-
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef, gfp_t gfp);
-
-bool nl80211_unexpected_frame(struct net_device *dev,
- const u8 *addr, gfp_t gfp);
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
- const u8 *addr, gfp_t gfp);
-
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 422d382..d77e1c1 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -6,11 +6,12 @@
#include "core.h"
#include "trace.h"
-static inline int rdev_suspend(struct cfg80211_registered_device *rdev)
+static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
+ struct cfg80211_wowlan *wowlan)
{
int ret;
- trace_rdev_suspend(&rdev->wiphy, rdev->wowlan);
- ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
+ trace_rdev_suspend(&rdev->wiphy, wowlan);
+ ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
@@ -887,4 +888,17 @@ static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
+
+static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ int ret;
+
+ trace_rdev_update_ft_ies(&rdev->wiphy, dev, ftie);
+ ret = rdev->ops->update_ft_ies(&rdev->wiphy, dev, ftie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 98532c00..e6df52d 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -184,14 +184,14 @@ static const struct ieee80211_regdomain world_regdom = {
NL80211_RRF_NO_IBSS |
NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */
- REG_RULE(5180-10, 5240+10, 40, 6, 20,
+ REG_RULE(5180-10, 5240+10, 80, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
- /* NB: 5260 MHz - 5700 MHz requies DFS */
+ /* NB: 5260 MHz - 5700 MHz requires DFS */
/* IEEE 802.11a, channel 149..165 */
- REG_RULE(5745-10, 5825+10, 40, 6, 20,
+ REG_RULE(5745-10, 5825+10, 80, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f432bd3..bad4c4b 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -159,7 +159,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct cfg80211_connect_params *params;
- const u8 *prev_bssid = NULL;
+ struct cfg80211_assoc_request req = {};
int err;
ASSERT_WDEV_LOCK(wdev);
@@ -186,16 +186,20 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
BUG_ON(!rdev->ops->assoc);
wdev->conn->state = CFG80211_CONN_ASSOCIATING;
if (wdev->conn->prev_bssid_valid)
- prev_bssid = wdev->conn->prev_bssid;
- err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
- params->channel, params->bssid,
- prev_bssid,
- params->ssid, params->ssid_len,
- params->ie, params->ie_len,
- params->mfp != NL80211_MFP_NO,
- &params->crypto,
- params->flags, &params->ht_capa,
- &params->ht_capa_mask);
+ req.prev_bssid = wdev->conn->prev_bssid;
+ req.ie = params->ie;
+ req.ie_len = params->ie_len;
+ req.use_mfp = params->mfp != NL80211_MFP_NO;
+ req.crypto = params->crypto;
+ req.flags = params->flags;
+ req.ht_capa = params->ht_capa;
+ req.ht_capa_mask = params->ht_capa_mask;
+ req.vht_capa = params->vht_capa;
+ req.vht_capa_mask = params->vht_capa_mask;
+
+ err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
+ params->bssid, params->ssid,
+ params->ssid_len, &req);
if (err)
__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 238ee49..8f28b9f 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -83,6 +83,14 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
+{
+ struct wireless_dev *wdev;
+
+ list_for_each_entry(wdev, &rdev->wdev_list, list)
+ cfg80211_leave(rdev, wdev);
+}
+
static int wiphy_suspend(struct device *dev, pm_message_t state)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
@@ -90,12 +98,19 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
rdev->suspend_at = get_seconds();
- if (rdev->ops->suspend) {
- rtnl_lock();
- if (rdev->wiphy.registered)
- ret = rdev_suspend(rdev);
- rtnl_unlock();
+ rtnl_lock();
+ if (rdev->wiphy.registered) {
+ if (!rdev->wowlan)
+ cfg80211_leave_all(rdev);
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev, rdev->wowlan);
+ if (ret == 1) {
+ /* Driver refuse to configure wowlan */
+ cfg80211_leave_all(rdev);
+ ret = rdev_suspend(rdev, NULL);
+ }
}
+ rtnl_unlock();
return ret;
}
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index b7a5313..ccadef2 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1785,6 +1785,26 @@ TRACE_EVENT(rdev_set_mac_acl,
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
);
+TRACE_EVENT(rdev_update_ft_ies,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_update_ft_ies_params *ftie),
+ TP_ARGS(wiphy, netdev, ftie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, md)
+ __dynamic_array(u8, ie, ftie->ie_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->md = ftie->md;
+ memcpy(__get_dynamic_array(ie), ftie->ie, ftie->ie_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", md: 0x%x",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->md)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
@@ -2413,6 +2433,32 @@ TRACE_EVENT(cfg80211_report_wowlan_wakeup,
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
);
+TRACE_EVENT(cfg80211_ft_event,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event),
+ TP_ARGS(wiphy, netdev, ft_event),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __dynamic_array(u8, ies, ft_event->ies_len)
+ MAC_ENTRY(target_ap)
+ __dynamic_array(u8, ric_ies, ft_event->ric_ies_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (ft_event->ies)
+ memcpy(__get_dynamic_array(ies), ft_event->ies,
+ ft_event->ies_len);
+ MAC_ASSIGN(target_ap, ft_event->target_ap);
+ if (ft_event->ric_ies)
+ memcpy(__get_dynamic_array(ric_ies), ft_event->ric_ies,
+ ft_event->ric_ies_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", target_ap: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
+);
+
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
OpenPOWER on IntegriCloud