diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/agg-rx.c | 8 | ||||
-rw-r--r-- | net/mac80211/agg-tx.c | 12 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 33 | ||||
-rw-r--r-- | net/mac80211/chan.c | 74 | ||||
-rw-r--r-- | net/mac80211/debugfs.c | 2 | ||||
-rw-r--r-- | net/mac80211/debugfs_key.c | 2 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 17 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 4 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 24 | ||||
-rw-r--r-- | net/mac80211/iface.c | 54 | ||||
-rw-r--r-- | net/mac80211/key.c | 49 | ||||
-rw-r--r-- | net/mac80211/main.c | 22 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 30 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 123 | ||||
-rw-r--r-- | net/mac80211/pm.c | 15 | ||||
-rw-r--r-- | net/mac80211/rx.c | 41 | ||||
-rw-r--r-- | net/mac80211/trace.h | 23 | ||||
-rw-r--r-- | net/mac80211/tx.c | 60 | ||||
-rw-r--r-- | net/mac80211/util.c | 73 | ||||
-rw-r--r-- | net/mac80211/vht.c | 14 | ||||
-rw-r--r-- | net/mac80211/wme.c | 18 |
21 files changed, 434 insertions, 264 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index cd4cf84a7f99..cce28e3b2232 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -250,10 +250,10 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; mgmt->u.action.u.addba_resp.dialog_token = dialog_token; - capab = (u16)(amsdu << 0); /* bit 0 A-MSDU support */ - capab |= (u16)(policy << 1); /* bit 1 aggregation policy */ - capab |= (u16)(tid << 2); /* bit 5:2 TID number */ - capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */ + capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK); + capab |= u16_encode_bits(policy, IEEE80211_ADDBA_PARAM_POLICY_MASK); + capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK); + capab |= u16_encode_bits(buf_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab); mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index b37c8a983d88..430a58587538 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -95,10 +95,10 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; mgmt->u.action.u.addba_req.dialog_token = dialog_token; - capab = (u16)(1 << 0); /* bit 0 A-MSDU support */ - capab |= (u16)(1 << 1); /* bit 1 aggregation policy */ - capab |= (u16)(tid << 2); /* bit 5:2 TID number */ - capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */ + capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK; + capab |= IEEE80211_ADDBA_PARAM_POLICY_MASK; + capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK); + capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); @@ -950,8 +950,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK; - tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; - buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; + tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK); + buf_size = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes); txq = sta->sta.txq[tid]; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7276e66ae435..c4c70e30ad7f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -405,6 +405,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case WLAN_CIPHER_SUITE_WEP104: if (WARN_ON_ONCE(fips_enabled)) return -EINVAL; + break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_AES_CMAC: @@ -1121,10 +1122,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p; sdata->vif.bss_conf.twt_responder = params->twt_responder; - memcpy(&sdata->vif.bss_conf.he_obss_pd, ¶ms->he_obss_pd, - sizeof(struct ieee80211_he_obss_pd)); - memcpy(&sdata->vif.bss_conf.he_bss_color, ¶ms->he_bss_color, - sizeof(struct ieee80211_he_bss_color)); + sdata->vif.bss_conf.he_obss_pd = params->he_obss_pd; + sdata->vif.bss_conf.he_bss_color = params->he_bss_color; sdata->vif.bss_conf.s1g = params->chandef.chan->band == NL80211_BAND_S1GHZ; @@ -2708,16 +2707,6 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, return 0; } -static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN); - - return 0; -} - static void ieee80211_rfkill_poll(struct wiphy *wiphy) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -3307,6 +3296,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, if (cfg80211_get_chandef_type(¶ms->chandef) != cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) return -EINVAL; + break; case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: @@ -3458,7 +3448,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, IEEE80211_QUEUE_STOP_REASON_CSA); cfg80211_ch_switch_started_notify(sdata->dev, &sdata->csa_chandef, - params->count); + params->count, params->block_tx); if (changed) { ieee80211_bss_info_change_notify(sdata, changed); @@ -4083,6 +4073,17 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy, return ret; } +static int ieee80211_set_sar_specs(struct wiphy *wiphy, + struct cfg80211_sar_specs *sar) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->set_sar_specs) + return -EOPNOTSUPP; + + return local->ops->set_sar_specs(&local->hw, sar); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -4138,7 +4139,6 @@ const struct cfg80211_ops mac80211_config_ops = { .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, - .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) @@ -4186,4 +4186,5 @@ const struct cfg80211_ops mac80211_config_ops = { .probe_mesh_link = ieee80211_probe_mesh_link, .set_tid_config = ieee80211_set_tid_config, .reset_tid_config = ieee80211_reset_tid_config, + .set_sar_specs = ieee80211_set_sar_specs, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 8f48aff74c7b..907bb1f748a1 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -9,6 +9,7 @@ #include <net/cfg80211.h> #include "ieee80211_i.h" #include "driver-ops.h" +#include "rate.h" static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) @@ -191,11 +192,13 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, return NULL; } -enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) +static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta) { - switch (sta->bandwidth) { + enum ieee80211_sta_rx_bandwidth width = ieee80211_sta_cap_rx_bw(sta); + + switch (width) { case IEEE80211_STA_RX_BW_20: - if (sta->ht_cap.ht_supported) + if (sta->sta.ht_cap.ht_supported) return NL80211_CHAN_WIDTH_20; else return NL80211_CHAN_WIDTH_20_NOHT; @@ -232,7 +235,7 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) continue; - max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); + max_bw = max(max_bw, ieee80211_get_sta_bw(sta)); } rcu_read_unlock(); @@ -275,11 +278,11 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, case NL80211_IFTYPE_NAN: continue; case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: width = vif->bss_conf.chandef.width; break; + case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_MONITOR: @@ -343,10 +346,42 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); } +static void ieee80211_chan_bw_change(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + struct sta_info *sta; + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[ctx->conf.def.chan->band]; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &local->sta_list, + list) { + enum ieee80211_sta_rx_bandwidth new_sta_bw; + + if (!ieee80211_sdata_running(sta->sdata)) + continue; + + if (rcu_access_pointer(sta->sdata->vif.chanctx_conf) != + &ctx->conf) + continue; + + new_sta_bw = ieee80211_sta_cur_vht_bw(sta); + if (new_sta_bw == sta->sta.bandwidth) + continue; + + sta->sta.bandwidth = new_sta_bw; + rate_control_rate_update(local, sband, sta, + IEEE80211_RC_BW_CHANGED); + } + rcu_read_unlock(); +} + static void ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct cfg80211_chan_def *chandef) { + enum nl80211_chan_width width; + if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { ieee80211_recalc_chanctx_min_def(local, ctx); return; @@ -354,7 +389,25 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); + width = ctx->conf.def.width; ctx->conf.def = *chandef; + + /* expected to handle only 20/40/80/160 channel widths */ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + break; + default: + WARN_ON(1); + } + + if (chandef->width < width) + ieee80211_chan_bw_change(local, ctx); + drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); ieee80211_recalc_chanctx_min_def(local, ctx); @@ -362,6 +415,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, local->_oper_chandef = *chandef; ieee80211_hw_config(local, 0); } + + if (chandef->width > width) + ieee80211_chan_bw_change(local, ctx); } static struct ieee80211_chanctx * @@ -743,7 +799,6 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, continue; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: break; @@ -1052,8 +1107,14 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) if (WARN_ON(!chandef)) return -EINVAL; + if (old_ctx->conf.def.width > new_ctx->conf.def.width) + ieee80211_chan_bw_change(local, new_ctx); + ieee80211_change_chanctx(local, new_ctx, chandef); + if (old_ctx->conf.def.width < new_ctx->conf.def.width) + ieee80211_chan_bw_change(local, new_ctx); + vif_chsw[0].vif = &sdata->vif; vif_chsw[0].old_ctx = &old_ctx->conf; vif_chsw[0].new_ctx = &new_ctx->conf; @@ -1444,6 +1505,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx); ieee80211_recalc_chanctx_min_def(local, ctx); + ieee80211_chan_bw_change(local, ctx); list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, reserved_chanctx_list) { diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 90470392fdaa..48f144f107d5 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -53,7 +53,7 @@ static const struct file_operations name## _ops = { \ DEBUGFS_READONLY_FILE_OPS(name) #define DEBUGFS_ADD(name) \ - debugfs_create_file(#name, 0400, phyd, local, &name## _ops); + debugfs_create_file(#name, 0400, phyd, local, &name## _ops) #define DEBUGFS_ADD_MODE(name, mode) \ debugfs_create_file(#name, mode, phyd, local, &name## _ops); diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 98a713475e0f..f53dec8a3d5c 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -319,7 +319,7 @@ KEY_OPS(key); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, key->debugfs.dir, \ - key, &key_##name##_ops); + key, &key_##name##_ops) #define DEBUGFS_ADD_W(name) \ debugfs_create_file(#name, 0600, key->debugfs.dir, \ key, &key_##name##_ops); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index fe8a7a87e513..0ad3860852ff 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -574,9 +574,6 @@ static ssize_t ieee80211_if_parse_tsf( IEEE80211_IF_FILE_RW(tsf); -/* WDS attributes */ -IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); - #ifdef CONFIG_MAC80211_MESH IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC); @@ -645,7 +642,7 @@ IEEE80211_IF_FILE(dot11MeshConnectedToAuthServer, #define DEBUGFS_ADD_MODE(name, mode) \ debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \ - sdata, &name##_ops); + sdata, &name##_ops) #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400) @@ -701,11 +698,6 @@ static void add_ibss_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(tsf, 0600); } -static void add_wds_files(struct ieee80211_sub_if_data *sdata) -{ - DEBUGFS_ADD(peer); -} - #ifdef CONFIG_MAC80211_MESH static void add_mesh_files(struct ieee80211_sub_if_data *sdata) @@ -719,7 +711,7 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) struct dentry *dir = debugfs_create_dir("mesh_stats", sdata->vif.debugfs_dir); #define MESHSTATS_ADD(name)\ - debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); + debugfs_create_file(#name, 0400, dir, sdata, &name##_ops) MESHSTATS_ADD(fwded_mcast); MESHSTATS_ADD(fwded_unicast); @@ -736,7 +728,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) sdata->vif.debugfs_dir); #define MESHPARAMS_ADD(name) \ - debugfs_create_file(#name, 0600, dir, sdata, &name##_ops); + debugfs_create_file(#name, 0600, dir, sdata, &name##_ops) MESHPARAMS_ADD(dot11MeshMaxRetries); MESHPARAMS_ADD(dot11MeshRetryTimeout); @@ -805,9 +797,6 @@ static void add_files(struct ieee80211_sub_if_data *sdata) case NL80211_IFTYPE_AP_VLAN: add_vlan_files(sdata); break; - case NL80211_IFTYPE_WDS: - add_wds_files(sdata); - break; default: break; } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 829dcad69c2c..eb4bb79d936a 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -274,7 +274,7 @@ static ssize_t sta_aql_read(struct file *file, char __user *userbuf, "Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n", q_depth[0], q_depth[1], q_depth[2], q_depth[3], q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1], - q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]), + q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]); rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); kfree(buf); @@ -985,7 +985,7 @@ STA_OPS(he_capa); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ - sta->debugfs_dir, sta, &sta_ ##name## _ops); + sta->debugfs_dir, sta, &sta_ ##name## _ops) #define DEBUGFS_ADD_COUNTER(name, field) \ debugfs_create_ulong(#name, 0400, sta->debugfs_dir, &sta->field); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2a21226fb518..8bf9c0e974d6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -311,11 +311,6 @@ struct ieee80211_if_ap { bool multicast_to_unicast; }; -struct ieee80211_if_wds { - struct sta_info *sta; - u8 remote_addr[ETH_ALEN]; -}; - struct ieee80211_if_vlan { struct list_head list; /* write-protected with RTNL and local->mtx */ @@ -457,7 +452,9 @@ struct ieee80211_if_managed { unsigned long probe_timeout; int probe_send_count; bool nullfunc_failed; - bool connection_loss; + u8 connection_loss:1, + driver_disconnect:1, + reconnect:1; struct cfg80211_bss *associated; struct ieee80211_mgd_auth_data *auth_data; @@ -985,7 +982,6 @@ struct ieee80211_sub_if_data { union { struct ieee80211_if_ap ap; - struct ieee80211_if_wds wds; struct ieee80211_if_vlan vlan; struct ieee80211_if_managed mgd; struct ieee80211_if_ibss ibss; @@ -1593,13 +1589,8 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) { WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START && status->flag & RX_FLAG_MACTIME_END); - if (status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END)) - return true; - /* can't handle non-legacy preamble yet */ - if (status->flag & RX_FLAG_MACTIME_PLCP_START && - status->encoding == RX_ENC_LEGACY) - return true; - return false; + return !!(status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END | + RX_FLAG_MACTIME_PLCP_START)); } void ieee80211_vif_inc_num_mcast(struct ieee80211_sub_if_data *sdata); @@ -1795,7 +1786,7 @@ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); -void ieee80211_tx_pending(unsigned long data); +void ieee80211_tx_pending(struct tasklet_struct *t); netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, @@ -2146,7 +2137,7 @@ void ieee80211_txq_remove_vlan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats, struct txq_info *txqi); -void ieee80211_wake_txqs(unsigned long data); +void ieee80211_wake_txqs(struct tasklet_struct *t); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *bssid, @@ -2286,7 +2277,6 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, enum ieee80211_chanctx_mode chanmode, u8 radar_detect); int ieee80211_max_num_channels(struct ieee80211_local *local); -enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta); void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, struct ieee80211_chanctx *ctx); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 44154cc596cd..3b9ec4ef81c3 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -230,10 +230,6 @@ static inline int identical_mac_addr_allowed(int type1, int type2) type2 == NL80211_IFTYPE_MONITOR || type1 == NL80211_IFTYPE_P2P_DEVICE || type2 == NL80211_IFTYPE_P2P_DEVICE || - (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) || - (type1 == NL80211_IFTYPE_WDS && - (type2 == NL80211_IFTYPE_WDS || - type2 == NL80211_IFTYPE_AP)) || (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_AP_VLAN) || (type1 == NL80211_IFTYPE_AP_VLAN && (type2 == NL80211_IFTYPE_AP || @@ -417,15 +413,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * (because if we remove a STA after ops->remove_interface() * the driver will have removed the vif info already!) * - * In WDS mode a station must exist here and be flushed, for - * AP_VLANs stations may exist since there's nothing else that + * For AP_VLANs stations may exist since there's nothing else that * would have removed them, but in other modes there shouldn't * be any stations. */ flushed = sta_info_flush(sdata); - WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && - ((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || - (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1))); + WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0); /* don't count this interface for allmulti while it is down */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) @@ -552,8 +545,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * When we get here, the interface is marked down. * Free the remaining keys, if there are any * (which can happen in AP mode if userspace sets - * keys before the interface is operating, and maybe - * also in WDS mode) + * keys before the interface is operating) * * Force the key freeing to always synchronize_net() * to wait for the RX path in case it is using this @@ -1022,16 +1014,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct net_device *dev = wdev->netdev; struct ieee80211_local *local = sdata->local; - struct sta_info *sta; u32 changed = 0; int res; u32 hw_reconf_flags = 0; switch (sdata->vif.type) { - case NL80211_IFTYPE_WDS: - if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) - return -ENOLINK; - break; case NL80211_IFTYPE_AP_VLAN: { struct ieee80211_sub_if_data *master; @@ -1080,6 +1067,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_WDS: /* cannot happen */ WARN_ON(1); break; @@ -1198,7 +1186,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_OCB: netif_carrier_off(dev); break; - case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: break; @@ -1220,28 +1207,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) set_bit(SDATA_STATE_RUNNING, &sdata->state); switch (sdata->vif.type) { - case NL80211_IFTYPE_WDS: - /* Create STA entry for the WDS peer */ - sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, - GFP_KERNEL); - if (!sta) { - res = -ENOMEM; - goto err_del_interface; - } - - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); - sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); - - res = sta_info_insert(sta); - if (res) { - /* STA has been freed */ - goto err_del_interface; - } - - rate_control_rate_init(sta); - netif_carrier_on(dev); - break; case NL80211_IFTYPE_P2P_DEVICE: rcu_assign_pointer(local->p2p_sdata, sdata); break; @@ -1358,6 +1323,7 @@ static void ieee80211_iface_work(struct work_struct *work) while ((skb = skb_dequeue(&sdata->skb_queue))) { struct ieee80211_mgmt *mgmt = (void *)skb->data; + kcov_remote_start_common(skb_get_kcov_handle(skb)); if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { int len = skb->len; @@ -1467,6 +1433,7 @@ static void ieee80211_iface_work(struct work_struct *work) } kfree_skb(skb); + kcov_remote_stop(); } /* then other type-dependent work */ @@ -1576,9 +1543,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->u.mntr.flags = MONITOR_FLAG_CONTROL | MONITOR_FLAG_OTHER_BSS; break; - case NL80211_IFTYPE_WDS: - sdata->vif.bss_conf.bssid = NULL; - break; case NL80211_IFTYPE_NAN: idr_init(&sdata->u.nan.function_inst_ids); spin_lock_init(&sdata->u.nan.func_lock); @@ -1589,6 +1553,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: WARN_ON(1); break; @@ -1633,9 +1598,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_OCB: /* * Could probably support everything - * but WDS here (WDS do_open can fail - * under memory pressure, which this - * code isn't prepared to handle). + * but here. */ break; case NL80211_IFTYPE_P2P_CLIENT: @@ -1728,7 +1691,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, case NL80211_IFTYPE_MONITOR: /* doesn't matter */ break; - case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: /* match up with an AP interface */ list_for_each_entry(sdata, &local->interfaces, list) { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 8c5f829ff6d7..a4817aa4b171 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -1300,3 +1300,52 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, return &key->conf; } EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add); + +void ieee80211_key_mic_failure(struct ieee80211_key_conf *keyconf) +{ + struct ieee80211_key *key; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + key->u.aes_cmac.icverrors++; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + key->u.aes_gmac.icverrors++; + break; + default: + /* ignore the others for now, we don't keep counters now */ + break; + } +} +EXPORT_SYMBOL_GPL(ieee80211_key_mic_failure); + +void ieee80211_key_replay(struct ieee80211_key_conf *keyconf) +{ + struct ieee80211_key *key; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + key->u.ccmp.replays++; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + key->u.aes_cmac.replays++; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + key->u.aes_gmac.replays++; + break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + key->u.gcmp.replays++; + break; + } +} +EXPORT_SYMBOL_GPL(ieee80211_key_replay); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 523380aed92e..dee88ec566ad 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -220,9 +220,9 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_ERP_SLOT; } -static void ieee80211_tasklet_handler(unsigned long data) +static void ieee80211_tasklet_handler(struct tasklet_struct *t) { - struct ieee80211_local *local = (struct ieee80211_local *) data; + struct ieee80211_local *local = from_tasklet(local, t, tasklet); struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -733,16 +733,12 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, skb_queue_head_init(&local->pending[i]); atomic_set(&local->agg_queue_stop[i], 0); } - tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, - (unsigned long)local); + tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending); if (ops->wake_tx_queue) - tasklet_init(&local->wake_txqs_tasklet, ieee80211_wake_txqs, - (unsigned long)local); + tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); - tasklet_init(&local->tasklet, - ieee80211_tasklet_handler, - (unsigned long) local); + tasklet_setup(&local->tasklet, ieee80211_tasklet_handler); skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); @@ -935,14 +931,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -EINVAL; } } else { - /* - * WDS is currently prohibited when channel contexts are used - * because there's no clear definition of which channel WDS - * type interfaces use - */ - if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) - return -EINVAL; - /* DFS is not supported with multi-channel combinations yet */ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *comb; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index ce5825d6f1d1..97095b7c9c64 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -667,6 +667,35 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh) } } +static void +ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata, + u8 *ie, u8 ie_len) +{ + struct ieee80211_supported_band *sband; + const u8 *cap; + const struct ieee80211_he_operation *he_oper = NULL; + + sband = ieee80211_get_sband(sdata); + if (!sband) + return; + + if (!ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT) || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + return; + + sdata->vif.bss_conf.he_support = true; + + cap = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION, ie, ie_len); + if (cap && cap[1] >= ieee80211_he_oper_size(&cap[3])) + he_oper = (void *)(cap + 3); + + if (he_oper) + sdata->vif.bss_conf.he_oper.params = + __le32_to_cpu(he_oper->he_oper_params); +} + /** * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame * @hdr: 802.11 frame header @@ -943,6 +972,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) bcn->tail_len = skb->len; memcpy(bcn->tail, skb->data, bcn->tail_len); + ieee80211_mesh_update_bss_params(sdata, bcn->tail, bcn->tail_len); bcn->meshconf = (struct ieee80211_meshconf_ie *) (bcn->tail + ifmsh->meshconf_offset); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6adfcb9c06dc..0e4d950cf907 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1417,6 +1417,17 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } + if (sdata->vif.bss_conf.chandef.chan->band != + csa_ie.chandef.chan->band) { + sdata_info(sdata, + "AP %pM switches to different band (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", + ifmgd->associated->bssid, + csa_ie.chandef.chan->center_freq, + csa_ie.chandef.width, csa_ie.chandef.center_freq1, + csa_ie.chandef.center_freq2); + goto lock_and_drop_connection; + } + if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, IEEE80211_CHAN_DISABLED)) { sdata_info(sdata, @@ -1429,9 +1440,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, csa_ie.chandef.width, csa_ie.chandef.center_freq1, csa_ie.chandef.freq1_offset, csa_ie.chandef.center_freq2); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - return; + goto lock_and_drop_connection; } if (cfg80211_chandef_identical(&csa_ie.chandef, @@ -1493,6 +1502,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->csa_chandef = csa_ie.chandef; sdata->csa_block_tx = csa_ie.mode; ifmgd->csa_ignored_same_chan = false; + ifmgd->beacon_crc_valid = false; if (sdata->csa_block_tx) ieee80211_stop_vif_queues(local, sdata, @@ -1500,7 +1510,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->mtx); cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, - csa_ie.count); + csa_ie.count, csa_ie.mode); if (local->ops->channel_switch) { /* use driver's channel switch callback */ @@ -1516,6 +1526,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, TU_TO_EXP_TIME((csa_ie.count - 1) * cbss->beacon_interval)); return; + lock_and_drop_connection: + mutex_lock(&local->mtx); + mutex_lock(&local->chanctx_mtx); drop_connection: /* * This is just so that the disconnect flow will know that @@ -1560,9 +1573,17 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata, chan_increment = 1; break; case NL80211_BAND_5GHZ: - case NL80211_BAND_6GHZ: chan_increment = 4; break; + case NL80211_BAND_6GHZ: + /* + * In the 6 GHz band, the "maximum transmit power level" + * field in the triplets is reserved, and thus will be + * zero and we shouldn't use it to control TX power. + * The actual TX power will be given in the transmit + * power envelope element instead. + */ + return false; } /* find channel */ @@ -2382,6 +2403,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, /* clear bssid only after building the needed mgmt frames */ eth_zero_addr(ifmgd->bssid); + sdata->vif.bss_conf.ssid_len = 0; + /* remove AP and TDLS peers */ sta_info_flush(sdata); @@ -2720,7 +2743,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get); static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, const u8 *buf, size_t len, bool tx, - u16 reason) + u16 reason, bool reconnect) { struct ieee80211_event event = { .type = MLME_EVENT, @@ -2729,7 +2752,7 @@ static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, }; if (tx) - cfg80211_tx_mlme_mgmt(sdata->dev, buf, len); + cfg80211_tx_mlme_mgmt(sdata->dev, buf, len, reconnect); else cfg80211_rx_mlme_mgmt(sdata->dev, buf, len); @@ -2751,13 +2774,18 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) tx = !sdata->csa_block_tx; - /* AP is probably out of range (or not reachable for another reason) so - * remove the bss struct for that AP. - */ - cfg80211_unlink_bss(local->hw.wiphy, ifmgd->associated); + if (!ifmgd->driver_disconnect) { + /* + * AP is probably out of range (or not reachable for another + * reason) so remove the bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, ifmgd->associated); + } ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + ifmgd->driver_disconnect ? + WLAN_REASON_DEAUTH_LEAVING : + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, tx, frame_buf); mutex_lock(&local->mtx); sdata->vif.csa_active = false; @@ -2770,7 +2798,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->mtx); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + ifmgd->reconnect); + ifmgd->reconnect = false; sdata_unlock(sdata); } @@ -2789,6 +2819,13 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work) sdata_info(sdata, "Connection to AP %pM lost\n", ifmgd->bssid); __ieee80211_disconnect(sdata); + ifmgd->connection_loss = false; + } else if (ifmgd->driver_disconnect) { + sdata_info(sdata, + "Driver requested disconnection from AP %pM\n", + ifmgd->bssid); + __ieee80211_disconnect(sdata); + ifmgd->driver_disconnect = false; } else { ieee80211_mgd_probe_ap(sdata, true); } @@ -2827,6 +2864,21 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_connection_loss); +void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; + + trace_api_disconnect(sdata, reconnect); + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) + return; + + sdata->u.mgd.driver_disconnect = true; + sdata->u.mgd.reconnect = reconnect; + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); +} +EXPORT_SYMBOL(ieee80211_disconnect); static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, bool assoc) @@ -3130,7 +3182,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, - reason_code); + reason_code, false); return; } @@ -3179,7 +3231,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); + ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code, + false); } static void ieee80211_get_rates(struct ieee80211_supported_band *sband, @@ -3199,8 +3252,8 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, *have_higher_than_11mbit = true; /* - * Skip HT, VHT and HE BSS membership selectors since they're - * not rates. + * Skip HT, VHT, HE and SAE H2E only BSS membership selectors + * since they're not rates. * * Note: Even though the membership selector and the basic * rate flag share the same bit, they are not exactly @@ -3208,7 +3261,8 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, */ if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) || supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY) || - supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY)) + supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY) || + supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E)) continue; for (j = 0; j < sband->n_bitrates; j++) { @@ -3494,14 +3548,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, le32_get_bits(elems->he_operation->he_oper_params, IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); - bss_conf->multi_sta_back_32bit = - sta->sta.he_cap.he_cap_elem.mac_cap_info[2] & - IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP; - - bss_conf->ack_enabled = - sta->sta.he_cap.he_cap_elem.mac_cap_info[2] & - IEEE80211_HE_MAC_CAP2_ACK_EN; - bss_conf->uora_exists = !!elems->uora_element; if (elems->uora_element) bss_conf->uora_ocw_range = elems->uora_element[0]; @@ -4199,7 +4245,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, true, deauth_buf); ieee80211_report_disconnect(sdata, deauth_buf, sizeof(deauth_buf), true, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, + false); return; } @@ -4344,7 +4391,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, tx, frame_buf); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - reason); + reason, false); } static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) @@ -4716,7 +4763,8 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + IEEE80211_DEAUTH_FRAME_LEN, + false); } /* This is a bit of a hack - we should find a better and more generic @@ -5430,7 +5478,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - WLAN_REASON_UNSPECIFIED); + WLAN_REASON_UNSPECIFIED, + false); } sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); @@ -5471,6 +5520,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *assoc_data; const struct cfg80211_bss_ies *beacon_ies; struct ieee80211_supported_band *sband; + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; const u8 *ssidie, *ht_ie, *vht_ie; int i, err; bool override = false; @@ -5488,6 +5538,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); assoc_data->ssid_len = ssidie[1]; + memcpy(bss_conf->ssid, assoc_data->ssid, assoc_data->ssid_len); + bss_conf->ssid_len = assoc_data->ssid_len; rcu_read_unlock(); if (ifmgd->associated) { @@ -5502,7 +5554,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - WLAN_REASON_UNSPECIFIED); + WLAN_REASON_UNSPECIFIED, + false); } if (ifmgd->auth_data && !ifmgd->auth_data->done) { @@ -5801,7 +5854,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_destroy_auth_data(sdata, false); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - req->reason_code); + req->reason_code, false); return 0; } @@ -5821,7 +5874,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_destroy_assoc_data(sdata, false, true); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - req->reason_code); + req->reason_code, false); return 0; } @@ -5836,7 +5889,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, tx, frame_buf); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - req->reason_code); + req->reason_code, false); return 0; } @@ -5869,7 +5922,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, frame_buf); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, - req->reason_code); + req->reason_code, false); return 0; } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 38c45e1dafd8..ae378a41c927 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -150,21 +150,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) case NL80211_IFTYPE_STATION: ieee80211_mgd_quiesce(sdata); break; - case NL80211_IFTYPE_WDS: - /* tear down aggregation sessions and remove STAs */ - mutex_lock(&local->sta_mtx); - sta = sdata->u.wds.sta; - if (sta && sta->uploaded) { - enum ieee80211_sta_state state; - - state = sta->sta_state; - for (; state > IEEE80211_STA_NOTEXIST; state--) - WARN_ON(drv_sta_state(local, sta->sdata, - sta, state, - state - 1)); - } - mutex_unlock(&local->sta_mtx); - break; default: break; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1e2e5a406d58..13b9bcc4865d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -32,16 +32,6 @@ #include "wme.h" #include "rate.h" -static inline void ieee80211_rx_stats(struct net_device *dev, u32 len) -{ - struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); - - u64_stats_update_begin(&tstats->syncp); - tstats->rx_packets++; - tstats->rx_bytes += len; - u64_stats_update_end(&tstats->syncp); -} - /* * monitor mode reception * @@ -842,7 +832,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (skb) { skb->dev = sdata->dev; - ieee80211_rx_stats(skb->dev, skb->len); + dev_sw_netstats_rx_add(skb->dev, skb->len); netif_receive_skb(skb); } } @@ -1477,7 +1467,6 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) if (unlikely((ieee80211_is_data(hdr->frame_control) || ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && - rx->sdata->vif.type != NL80211_IFTYPE_WDS && rx->sdata->vif.type != NL80211_IFTYPE_OCB && (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { /* @@ -1758,7 +1747,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) { sta->rx_stats.last_rx = jiffies; } else if (!ieee80211_is_s1g_beacon(hdr->frame_control) && - is_multicast_ether_addr(hdr->addr1)) { + !is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. @@ -2560,7 +2549,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) skb = rx->skb; xmit_skb = NULL; - ieee80211_rx_stats(dev, skb->len); + dev_sw_netstats_rx_add(dev, skb->len); if (rx->sta) { /* The seqno index has the same property as needed @@ -3699,7 +3688,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, } prev_dev = sdata->dev; - ieee80211_rx_stats(sdata->dev, skb->len); + dev_sw_netstats_rx_add(sdata->dev, skb->len); } if (prev_dev) { @@ -4080,10 +4069,6 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) return false; return true; - case NL80211_IFTYPE_WDS: - if (bssid || !ieee80211_is_data(hdr->frame_control)) - return false; - return ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2); case NL80211_IFTYPE_P2P_DEVICE: return ieee80211_is_public_action(hdr, skb->len) || ieee80211_is_probe_req(hdr->frame_control) || @@ -4416,7 +4401,7 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, skb->dev = fast_rx->dev; - ieee80211_rx_stats(fast_rx->dev, skb->len); + dev_sw_netstats_rx_add(fast_rx->dev, skb->len); /* The seqno index has the same property as needed * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS @@ -4742,6 +4727,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, status->rx_flags = 0; + kcov_remote_start_common(skb_get_kcov_handle(skb)); + /* * Frames with failed FCS/PLCP checksum are not returned, * all other frames are returned without radiotap header @@ -4749,15 +4736,15 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, * Also, frames with less than 16 bytes are dropped. */ skb = ieee80211_rx_monitor(local, skb, rate); - if (!skb) - return; - - ieee80211_tpt_led_trig_rx(local, - ((struct ieee80211_hdr *)skb->data)->frame_control, - skb->len); + if (skb) { + ieee80211_tpt_led_trig_rx(local, + ((struct ieee80211_hdr *)skb->data)->frame_control, + skb->len); - __ieee80211_rx_handle_packet(hw, pubsta, skb, list); + __ieee80211_rx_handle_packet(hw, pubsta, skb, list); + } + kcov_remote_stop(); return; drop: kfree_skb(skb); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 89723907a094..601322e16957 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH -* Copyright (C) 2018 - 2019 Intel Corporation +* Copyright (C) 2018 - 2020 Intel Corporation */ #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) @@ -2086,6 +2086,27 @@ TRACE_EVENT(api_connection_loss, ) ); +TRACE_EVENT(api_disconnect, + TP_PROTO(struct ieee80211_sub_if_data *sdata, bool reconnect), + + TP_ARGS(sdata, reconnect), + + TP_STRUCT__entry( + VIF_ENTRY + __field(int, reconnect) + ), + + TP_fast_assign( + VIF_ASSIGN; + __entry->reconnect = reconnect; + ), + + TP_printk( + VIF_PR_FMT " reconnect:%d", + VIF_PR_ARG, __entry->reconnect + ) +); + TRACE_EVENT(api_cqm_rssi_notify, TP_PROTO(struct ieee80211_sub_if_data *sdata, enum nl80211_cqm_rssi_threshold_event rssi_event, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 56a4d0d20a26..6422da6690f7 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -38,16 +38,6 @@ /* misc utils */ -static inline void ieee80211_tx_stats(struct net_device *dev, u32 len) -{ - struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); - - u64_stats_update_begin(&tstats->syncp); - tstats->tx_packets++; - tstats->tx_bytes += len; - u64_stats_update_end(&tstats->syncp); -} - static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, struct sk_buff *skb, int group_addr, int next_frag_len) @@ -319,9 +309,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (tx->sdata->vif.type == NL80211_IFTYPE_OCB) return TX_CONTINUE; - if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) - return TX_CONTINUE; - if (tx->flags & IEEE80211_TX_PS_BUFFERED) return TX_CONTINUE; @@ -2113,6 +2100,9 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, info->flags |= IEEE80211_TX_CTL_NO_ACK; if (txflags & IEEE80211_RADIOTAP_F_TX_NOSEQNO) info->control.flags |= IEEE80211_TX_CTRL_NO_SEQNO; + if (txflags & IEEE80211_RADIOTAP_F_TX_ORDER) + info->control.flags |= + IEEE80211_TX_CTRL_DONT_REORDER; break; case IEEE80211_RADIOTAP_RATE: @@ -2279,11 +2269,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, payload[7]); } - /* - * Initialize skb->priority for QoS frames. This is put in the TID field - * of the frame before passing it to the driver. + /* Initialize skb->priority for QoS frames. If the DONT_REORDER flag + * is set, stick to the default value for skb->priority to assure + * frames injected with this flag are not reordered relative to each + * other. */ - if (ieee80211_is_data_qos(hdr->frame_control)) { + if (ieee80211_is_data_qos(hdr->frame_control) && + !(info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER)) { u8 *p = ieee80211_get_qos_ctl(hdr); skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; } @@ -2295,8 +2287,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, * we handle as though they are non-injected frames. * This code here isn't entirely correct, the local MAC address * isn't always enough to find the interface to use; for proper - * VLAN/WDS support we will need a different mechanism (which - * likely isn't going to be monitor interfaces). + * VLAN support we have an nl80211-based mechanism. * * This is necessary, for example, for old hostapd versions that * don't use nl80211-based management TX/RX. @@ -2307,8 +2298,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, if (!ieee80211_sdata_running(tmp_sdata)) continue; if (tmp_sdata->vif.type == NL80211_IFTYPE_MONITOR || - tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - tmp_sdata->vif.type == NL80211_IFTYPE_WDS) + tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; if (ether_addr_equal(tmp_sdata->vif.addr, hdr->addr2)) { sdata = tmp_sdata; @@ -2402,9 +2392,6 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, } sta = sta_info_get_bss(sdata, skb->data); break; - case NL80211_IFTYPE_WDS: - sta = sta_info_get(sdata, sdata->u.wds.remote_addr); - break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: /* determined much later */ @@ -2580,20 +2567,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, hdrlen = 24; band = chanctx_conf->def.chan->band; break; - case NL80211_IFTYPE_WDS: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); - /* RA TA DA SA */ - memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN); - memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); - hdrlen = 30; - /* - * This is the exception! WDS style interfaces are prohibited - * when channel contexts are in used so this must be valid - */ - band = local->hw.conf.chandef.chan->band; - break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: if (!is_multicast_ether_addr(skb->data)) { @@ -3403,7 +3376,7 @@ static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata, if (key) info->control.hw_key = &key->conf; - ieee80211_tx_stats(skb->dev, skb->len); + dev_sw_netstats_tx_add(skb->dev, 1, skb->len); if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; @@ -4021,7 +3994,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, goto out; } - ieee80211_tx_stats(dev, skb->len); + dev_sw_netstats_tx_add(dev, 1, skb->len); ieee80211_xmit(sdata, sta, skb); } @@ -4248,7 +4221,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; - ieee80211_tx_stats(dev, skb->len); + dev_sw_netstats_tx_add(dev, 1, skb->len); sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; sta->tx_stats.packets[skb_get_queue_mapping(skb)]++; @@ -4418,9 +4391,10 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, /* * Transmit all pending packets. Called from tasklet. */ -void ieee80211_tx_pending(unsigned long data) +void ieee80211_tx_pending(struct tasklet_struct *t) { - struct ieee80211_local *local = (struct ieee80211_local *)data; + struct ieee80211_local *local = from_tasklet(local, t, + tx_pending_tasklet); unsigned long flags; int i; bool txok; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 94e624e9439b..8d3ae6b2f95f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -386,9 +386,10 @@ _ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags) rcu_read_unlock(); } -void ieee80211_wake_txqs(unsigned long data) +void ieee80211_wake_txqs(struct tasklet_struct *t) { - struct ieee80211_local *local = (struct ieee80211_local *)data; + struct ieee80211_local *local = from_tasklet(local, t, + wake_txqs_tasklet); unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); @@ -2513,7 +2514,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) return res; } break; - case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: @@ -2523,6 +2523,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_WDS: WARN_ON(1); break; } @@ -3665,6 +3666,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, u64 ts = status->mactime; struct rate_info ri; u16 rate; + u8 n_ltf; if (WARN_ON(!ieee80211_have_rx_timestamp(status))) return 0; @@ -3675,11 +3677,58 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, /* Fill cfg80211 rate info */ switch (status->encoding) { + case RX_ENC_HE: + ri.flags |= RATE_INFO_FLAGS_HE_MCS; + ri.mcs = status->rate_idx; + ri.nss = status->nss; + ri.he_ru_alloc = status->he_ru; + if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) + ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + + /* + * See P802.11ax_D6.0, section 27.3.4 for + * VHT PPDU format. + */ + if (status->flag & RX_FLAG_MACTIME_PLCP_START) { + mpdu_offset += 2; + ts += 36; + + /* + * TODO: + * For HE MU PPDU, add the HE-SIG-B. + * For HE ER PPDU, add 8us for the HE-SIG-A. + * For HE TB PPDU, add 4us for the HE-STF. + * Add the HE-LTF durations - variable. + */ + } + + break; case RX_ENC_HT: ri.mcs = status->rate_idx; ri.flags |= RATE_INFO_FLAGS_MCS; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + + /* + * See P802.11REVmd_D3.0, section 19.3.2 for + * HT PPDU format. + */ + if (status->flag & RX_FLAG_MACTIME_PLCP_START) { + mpdu_offset += 2; + if (status->enc_flags & RX_ENC_FLAG_HT_GF) + ts += 24; + else + ts += 32; + + /* + * Add Data HT-LTFs per streams + * TODO: add Extension HT-LTFs, 4us per LTF + */ + n_ltf = ((ri.mcs >> 3) & 3) + 1; + n_ltf = n_ltf == 3 ? 4 : n_ltf; + ts += n_ltf * 4; + } + break; case RX_ENC_VHT: ri.flags |= RATE_INFO_FLAGS_VHT_MCS; @@ -3687,6 +3736,23 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ri.nss = status->nss; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + + /* + * See P802.11REVmd_D3.0, section 21.3.2 for + * VHT PPDU format. + */ + if (status->flag & RX_FLAG_MACTIME_PLCP_START) { + mpdu_offset += 2; + ts += 36; + + /* + * Add VHT-LTFs per streams + */ + n_ltf = (ri.nss != 1) && (ri.nss % 2) ? + ri.nss + 1 : ri.nss; + ts += 4 * n_ltf; + } + break; default: WARN_ON(1); @@ -3710,7 +3776,6 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); if (status->flag & RX_FLAG_MACTIME_PLCP_START) { - /* TODO: handle HT/VHT preambles */ if (status->band == NL80211_BAND_5GHZ) { ts += 20 << shift; mpdu_offset += 2; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index fb0e3a657d2d..c3ca97373774 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -465,12 +465,18 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) * IEEE80211-2016 specification makes higher bandwidth operation * possible on the TDLS link if the peers have wider bandwidth * capability. + * + * However, in this case, and only if the TDLS peer is authorized, + * limit to the tdls_chandef so that the configuration here isn't + * wider than what's actually requested on the channel context. */ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && - test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) - return bw; - - bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); + test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && + test_sta_flag(sta, WLAN_STA_AUTHORIZED) && + sta->tdls_chandef.chan) + bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); + else + bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); return bw; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 2fb99325135a..9ea6004abe1b 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -118,9 +118,11 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr) { struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *p; - if (local->hw.queues < IEEE80211_NUM_ACS) + if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) || + local->hw.queues < IEEE80211_NUM_ACS) return 0; if (!ieee80211_is_data(hdr->frame_control)) { @@ -141,6 +143,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct sk_buff *skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mac80211_qos_map *qos_map; bool qos; @@ -153,7 +156,7 @@ u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, else qos = false; - if (!qos) { + if (!qos || (info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER)) { skb->priority = 0; /* required for correct WPA/11i MIC */ return IEEE80211_AC_BE; } @@ -202,9 +205,6 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: ra = skb->data; break; - case NL80211_IFTYPE_WDS: - ra = sdata->u.wds.remote_addr; - break; case NL80211_IFTYPE_STATION: /* might be a TDLS station */ sta = sta_info_get(sdata, skb->data); @@ -249,6 +249,14 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, p = ieee80211_get_qos_ctl(hdr); + /* don't overwrite the QoS field of injected frames */ + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + /* do take into account Ack policy of injected frames */ + if (*p & IEEE80211_QOS_CTL_ACK_POLICY_NOACK) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + return; + } + /* set up the first byte */ /* |